diff --git a/pkg/controller/machine/machine_controller_suite_test.go b/pkg/controller/machine/machine_controller_suite_test.go index 407bbd9a597c7a32259e8cdd6a5f039f726a86dc..6207afed56579462d50e0c2e5dee0b847ebdd6ee 100644 --- a/pkg/controller/machine/machine_controller_suite_test.go +++ b/pkg/controller/machine/machine_controller_suite_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/klog/v2/textlogger" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/komega" logf "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -61,13 +62,19 @@ func TestMachineController(t *testing.T) { RunSpecs(t, "Machine Controller Suite") } -var _ = BeforeSuite(func(ctx SpecContext) { +var _ = BeforeSuite(func() { By("bootstrapping test environment") var err error cfg, testEnv, err = StartEnvTest() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + + komega.SetContext(ctx) + komega.SetClient(k8sClient) + }) var _ = AfterSuite(func() { diff --git a/pkg/controller/machine/machine_controller_test.go b/pkg/controller/machine/machine_controller_test.go index 1edb1825de811d62b0ba85384b6d30f46afb545c..cae79995812106643486f525fa920ed5a2e0e3c3 100644 --- a/pkg/controller/machine/machine_controller_test.go +++ b/pkg/controller/machine/machine_controller_test.go @@ -28,9 +28,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + testutils "github.com/openshift/cluster-api-actuator-pkg/testutils" machinev1resourcebuilder "github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/machine/v1beta1" "github.com/openshift/cluster-control-plane-machine-set-operator/test/e2e/framework" - testutils "github.com/openshift/machine-api-operator/pkg/util/testing" + "github.com/openshift/machine-api-operator/pkg/util/testing" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -55,11 +56,10 @@ var _ = Describe("Machine Reconciler", func() { }) Expect(err).NotTo(HaveOccurred()) - k8sClient = mgr.GetClient() k = komega.New(k8sClient) By("Setting up feature gates") - gate, err := testutils.NewDefaultMutableFeatureGate() + gate, err := testing.NewDefaultMutableFeatureGate() Expect(err).NotTo(HaveOccurred()) By("Setting up a new reconciler") @@ -96,15 +96,15 @@ var _ = Describe("Machine Reconciler", func() { }) AfterEach(func() { - By("Deleting the machines") - Expect(cleanResources(namespace.Name)).To(Succeed()) - - By("Deleting the namespace") - Expect(k8sClient.Delete(ctx, namespace)).To(Succeed()) - By("Closing the manager") mgrCtxCancel() Eventually(mgrStopped, timeout).WithTimeout(20 * time.Second).Should(BeClosed()) + + By("Cleaning up test resources") + testutils.CleanupResources(Default, ctx, cfg, k8sClient, namespace.GetName(), + &machinev1.Machine{}, + ) + }) It("Should reconcile a Machine", func() { @@ -127,99 +127,98 @@ var _ = Describe("Machine Reconciler", func() { }) - It("Should set the Paused condition appropriately", func() { - instance := machineBuilder.Build() - - By("Creating the Machine") - Expect(k8sClient.Create(ctx, instance)).To(Succeed()) - - By("Setting the AuthoritativeAPI to ClusterAPI") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityClusterAPI - })).Should(Succeed()) - - By("Verifying that the AuthoritativeAPI is set to Cluster API") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityClusterAPI))) - - By("Verifying the paused condition is approproately set to true") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(PausedCondition)), - HaveField("Status", Equal(corev1.ConditionTrue)), - )))) - - // The condition should remain true whilst transitioning through 'Migrating' - // Run this in a goroutine so we don't block - - // Copy the instance before starting the goroutine, to avoid data races - instanceCopy := instance.DeepCopy() - go func() { - defer GinkgoRecover() - framework.RunCheckUntil( - ctx, - // Check that we consistently have the Paused condition true - func(_ context.Context, g framework.GomegaAssertions) bool { - By("Checking that the paused condition is consistently true whilst migrating to MachineAPI") - - localInstance := instanceCopy.DeepCopy() - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { - return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) - } - - return g.Expect(localInstance.Status.Conditions).Should(ContainElement(SatisfyAll( - HaveField("Type", Equal(PausedCondition)), - HaveField("Status", Equal(corev1.ConditionTrue)), - ))) - - }, - // Condition / until function: until we observe the MachineAuthority being MAPI - func(_ context.Context, g framework.GomegaAssertions) bool { - By("Checking that the AuthoritativeAPI is not MachineAPI") - - localInstance := instanceCopy.DeepCopy() - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { - return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) - } - - return g.Expect(localInstance.Status.AuthoritativeAPI).To(Equal(machinev1.MachineAuthorityMachineAPI)) - }) - }() - - By("Transitioning the AuthoritativeAPI though 'Migrating' to MachineAPI") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating - })).Should(Succeed()) - - By("Updating the AuthoritativeAPI from Migrating to MachineAPI") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMachineAPI - })).Should(Succeed()) - - Eventually(k.Object(instance), timeout).Should(HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityMachineAPI))) - - By("Verifying the paused condition is approproately set to false") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(PausedCondition)), - HaveField("Status", Equal(corev1.ConditionFalse)), - )))) - - By("Unsetting the AuthoritativeAPI field in the status") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = "" - })).Should(Succeed()) + Describe("Paused condition", func() { + var instance *machinev1.Machine + BeforeEach(func() { + instance = machineBuilder.Build() + }) - By("Verifying the paused condition is still approproately set to false") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(PausedCondition)), - HaveField("Status", Equal(corev1.ConditionFalse)), - )))) + It("Should set the Paused condition appropriately", func() { + + By("Creating the Machine") + Expect(k8sClient.Create(ctx, instance)).To(Succeed()) + + By("Setting the AuthoritativeAPI to ClusterAPI") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityClusterAPI + })).Should(Succeed()) + + By("Verifying the paused condition is approproately set to true") + Eventually(k.Object(instance), timeout).Should(SatisfyAll( + HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(PausedCondition)), + HaveField("Status", Equal(corev1.ConditionTrue)), + ))), + HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityClusterAPI)), + )) + + // The condition should remain true whilst transitioning through 'Migrating' + // Run this in a goroutine so we don't block + + // Copy the instance before starting the goroutine, to avoid data races + instanceCopy := instance.DeepCopy() + go func() { + defer GinkgoRecover() + framework.RunCheckUntil( + ctx, + // Check that we consistently have the Paused condition true + func(_ context.Context, g framework.GomegaAssertions) bool { + By("Checking that the paused condition is consistently true whilst migrating to MachineAPI") + + localInstance := instanceCopy.DeepCopy() + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { + return g.Expect(err).Should(WithTransform(testing.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) + } + + return g.Expect(localInstance.Status.Conditions).Should(ContainElement(SatisfyAll( + HaveField("Type", Equal(PausedCondition)), + HaveField("Status", Equal(corev1.ConditionTrue)), + ))) + + }, + // Condition / until function: until we observe the MachineAuthority being MAPI + func(_ context.Context, g framework.GomegaAssertions) bool { + By("Checking that the AuthoritativeAPI is not MachineAPI") + + localInstance := instanceCopy.DeepCopy() + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { + return g.Expect(err).Should(WithTransform(testing.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) + } + + return g.Expect(localInstance.Status.AuthoritativeAPI).To(Equal(machinev1.MachineAuthorityMachineAPI)) + }) + }() + + By("Transitioning the AuthoritativeAPI though 'Migrating' to MachineAPI") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating + })).Should(Succeed()) + + By("Updating the AuthoritativeAPI from Migrating to MachineAPI") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMachineAPI + })).Should(Succeed()) + + By("Verifying the paused condition is approproately set to false") + Eventually(k.Object(instance), timeout).Should(SatisfyAll( + HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(PausedCondition)), + HaveField("Status", Equal(corev1.ConditionFalse)), + ))), + HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityMachineAPI)), + )) + + By("Unsetting the AuthoritativeAPI field in the status") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = "" + })).Should(Succeed()) + + By("Verifying the paused condition is still approproately set to false") + Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(PausedCondition)), + HaveField("Status", Equal(corev1.ConditionFalse)), + HaveField("Message", Equal("The AuthoritativeAPI is not set")), + )))) + }) }) }) - -func cleanResources(namespace string) error { - machine := &machinev1.Machine{} - if err := k8sClient.DeleteAllOf(ctx, machine, client.InNamespace(namespace)); err != nil { - return err - } - - return nil -} diff --git a/pkg/controller/machineset/controller.go b/pkg/controller/machineset/controller.go index 163ea6b85efe13240bf9d1e590b1ee94b65d6ca5..5ebd998f3b7f8bbc98d8521ccf70cbae0211ad49 100644 --- a/pkg/controller/machineset/controller.go +++ b/pkg/controller/machineset/controller.go @@ -188,12 +188,12 @@ func (r *ReconcileMachineSet) Reconcile(ctx context.Context, request reconcile.R "The AuthoritativeAPI is set to %s", string(machineSet.Status.AuthoritativeAPI), )) - _, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status) + machineSet, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status) if err != nil { klog.Errorf("%v: error updating status: %v", machineSet.Name, err) } - klog.Infof("%v: machine is paused, taking no further action", machineSet.Name) + klog.Infof("%v: machine set is paused, taking no further action", machineSet.Name) return reconcile.Result{}, nil } @@ -211,7 +211,7 @@ func (r *ReconcileMachineSet) Reconcile(ctx context.Context, request reconcile.R machinev1.ConditionSeverityInfo, pausedFalseReason, )) - _, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status) + machineSet, err := updateMachineSetStatus(r.Client, machineSet, machineSetCopy.Status) if err != nil { klog.Errorf("%v: error updating status: %v", machineSet.Name, err) } diff --git a/pkg/controller/machineset/machineset_controller_suite_test.go b/pkg/controller/machineset/machineset_controller_suite_test.go index afc9ff473d8082a6c72d518ccc8f0401d3c3f35c..5662aa634aa481ebf6cea924a8ddb17af1a40bf9 100644 --- a/pkg/controller/machineset/machineset_controller_suite_test.go +++ b/pkg/controller/machineset/machineset_controller_suite_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/klog/v2/textlogger" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/komega" logf "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -57,11 +58,10 @@ var ( func TestMachinesetController(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "MachineSet Controller Suite") } -var _ = BeforeSuite(func(ctx SpecContext) { +var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ @@ -80,6 +80,12 @@ var _ = BeforeSuite(func(ctx SpecContext) { cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + + komega.SetContext(ctx) + komega.SetClient(k8sClient) }) var _ = AfterSuite(func() { diff --git a/pkg/controller/machineset/machineset_controller_test.go b/pkg/controller/machineset/machineset_controller_test.go index 6bc5b308f38e952c9a3a951932e7db9a43316ed7..27a4dda399749cd8c310b7edce87bd9b686b5c1a 100644 --- a/pkg/controller/machineset/machineset_controller_test.go +++ b/pkg/controller/machineset/machineset_controller_test.go @@ -27,8 +27,9 @@ import ( machinev1resourcebuilder "github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/machine/v1beta1" "github.com/openshift/machine-api-operator/pkg/controller/machine" + testutils "github.com/openshift/cluster-api-actuator-pkg/testutils" "github.com/openshift/cluster-control-plane-machine-set-operator/test/e2e/framework" - testutils "github.com/openshift/machine-api-operator/pkg/util/testing" + "github.com/openshift/machine-api-operator/pkg/util/testing" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" @@ -56,12 +57,10 @@ var _ = Describe("MachineSet Reconciler", func() { }, }) Expect(err).NotTo(HaveOccurred()) - - k8sClient = mgr.GetClient() k = komega.New(k8sClient) By("Setting up feature gates") - gate, err := testutils.NewDefaultMutableFeatureGate() + gate, err := testing.NewDefaultMutableFeatureGate() Expect(err).NotTo(HaveOccurred()) By("Setting up a new reconciler") @@ -91,22 +90,21 @@ var _ = Describe("MachineSet Reconciler", func() { By("Setting up the machine set builder") machineSetBuilder = machinev1resourcebuilder.MachineSet(). WithNamespace(namespace.ObjectMeta.Name). - WithName("foo"). + WithGenerateName("foo"). WithReplicas(replicas). WithLabels(map[string]string{"foo": "bar"}) - }) AfterEach(func() { - By("Deleting the machinesets") - Expect(cleanResources(namespace.Name)).To(Succeed()) - - By("Deleting the namespace") - Expect(k8sClient.Delete(ctx, namespace)).To(Succeed()) - By("Closing the manager") mgrCtxCancel() Eventually(mgrStopped, timeout).WithTimeout(20 * time.Second).Should(BeClosed()) + + By("Cleaning up test resources") + testutils.CleanupResources(Default, ctx, cfg, k8sClient, namespace.GetName(), + &machinev1.Machine{}, + &machinev1.MachineSet{}, + ) }) It("Should reconcile a MachineSet", func() { @@ -149,107 +147,100 @@ var _ = Describe("MachineSet Reconciler", func() { }, timeout*3).Should(BeEquivalentTo(replicas)) }) - It("Should set the Paused condition appropriately", func() { - instance := machineSetBuilder. - WithName("baz"). - WithLabels(map[string]string{"baz": "bar"}). - Build() - - By("Creating the MachineSet") - Expect(k8sClient.Create(ctx, instance)).To(Succeed()) - - By("Setting the AuthoritativeAPI to ClusterAPI") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityClusterAPI - })).Should(Succeed()) - - By("Verifying that the AuthoritativeAPI is set to Cluster API") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityClusterAPI))) - - By("Verifying the paused condition is approproately set to true") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(machine.PausedCondition)), - HaveField("Status", Equal(corev1.ConditionTrue)), - )))) - - // The condition should remain true whilst transitioning through 'Migrating' - // Run this in a goroutine so we don't block - - // Copy the instance before starting the goroutine, to avoid data races - instanceCopy := instance.DeepCopy() - go func() { - defer GinkgoRecover() - framework.RunCheckUntil( - ctx, - // Check that we consistently have the Paused condition true - func(_ context.Context, g framework.GomegaAssertions) bool { - By("Checking that the paused condition is consistently true whilst migrating to MachineAPI") - - localInstance := instanceCopy.DeepCopy() - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { - return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) - } - - return g.Expect(localInstance.Status.Conditions).Should(ContainElement(SatisfyAll( - HaveField("Type", Equal(machine.PausedCondition)), - HaveField("Status", Equal(corev1.ConditionTrue)), - ))) - - }, - // Condition / until function: until we observe the MachineAuthority being MAPI - func(_ context.Context, g framework.GomegaAssertions) bool { - By("Checking that the AuthoritativeAPI is not MachineAPI") - - localInstance := instanceCopy.DeepCopy() - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { - return g.Expect(err).Should(WithTransform(testutils.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) - } - - return g.Expect(localInstance.Status.AuthoritativeAPI).To(Equal(machinev1.MachineAuthorityMachineAPI)) - }) - }() - - By("Transitioning the AuthoritativeAPI though Migrating") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating - })).Should(Succeed()) - - By("Updating the AuthoritativeAPI from Migrating to MachineAPI") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMachineAPI - })).Should(Succeed()) - - Eventually(k.Object(instance), timeout).Should(HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityMachineAPI))) - - By("Verifying the paused condition is approproately set to false") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(machine.PausedCondition)), - HaveField("Status", Equal(corev1.ConditionFalse)), - )))) - - By("Unsetting the AuthoritativeAPI field in the status") - Eventually(k.UpdateStatus(instance, func() { - instance.Status.AuthoritativeAPI = "" - })).Should(Succeed()) - - By("Verifying the paused condition is still approproately set to false") - Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( - HaveField("Type", Equal(machine.PausedCondition)), - HaveField("Status", Equal(corev1.ConditionFalse)), - )))) + Describe("Paused Condition", func() { + var instance *machinev1.MachineSet + BeforeEach(func() { + instance = machineSetBuilder. + WithGenerateName("baz-"). + WithLabels(map[string]string{"baz": "bar"}). + Build() + }) + It("Should set the Paused condition appropriately", func() { + + By("Creating the MachineSet") + Expect(k8sClient.Create(ctx, instance)).To(Succeed()) + + By("Setting the AuthoritativeAPI to ClusterAPI") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityClusterAPI + })).Should(Succeed()) + + By("Verifying the paused condition is approproately set to true") + Eventually(k.Object(instance), timeout).Should(SatisfyAll( + HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(machine.PausedCondition)), + HaveField("Status", Equal(corev1.ConditionTrue)), + ))), + HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityClusterAPI)), + )) + + // The condition should remain true whilst transitioning through 'Migrating' + // Run this in a goroutine so we don't block + + // Copy the instance before starting the goroutine, to avoid data races + instanceCopy := instance.DeepCopy() + go func() { + defer GinkgoRecover() + framework.RunCheckUntil( + ctx, + // Check that we consistently have the Paused condition true + func(_ context.Context, g framework.GomegaAssertions) bool { + By("Checking that the paused condition is consistently true whilst migrating to MachineAPI") + + localInstance := instanceCopy.DeepCopy() + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { + return g.Expect(err).Should(WithTransform(testing.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) + } + + return g.Expect(localInstance.Status.Conditions).Should(ContainElement(SatisfyAll( + HaveField("Type", Equal(machine.PausedCondition)), + HaveField("Status", Equal(corev1.ConditionTrue)), + ))) + + }, + // Condition / until function: until we observe the MachineAuthority being MAPI + func(_ context.Context, g framework.GomegaAssertions) bool { + By("Checking that the AuthoritativeAPI is not MachineAPI") + + localInstance := instanceCopy.DeepCopy() + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(localInstance), localInstance); err != nil { + return g.Expect(err).Should(WithTransform(testing.IsRetryableAPIError, BeTrue()), "expected temporary error while getting machine: %v", err) + } + + return g.Expect(localInstance.Status.AuthoritativeAPI).To(Equal(machinev1.MachineAuthorityMachineAPI)) + }) + }() + + By("Transitioning the AuthoritativeAPI though Migrating") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMigrating + })).Should(Succeed()) + + By("Updating the AuthoritativeAPI from Migrating to MachineAPI") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = machinev1.MachineAuthorityMachineAPI + })).Should(Succeed()) + + By("Verifying the paused condition is approproately set to false") + Eventually(k.Object(instance), timeout).Should(SatisfyAll( + HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(machine.PausedCondition)), + HaveField("Status", Equal(corev1.ConditionFalse)), + ))), + HaveField("Status.AuthoritativeAPI", Equal(machinev1.MachineAuthorityMachineAPI)), + )) + + By("Unsetting the AuthoritativeAPI field in the status") + Eventually(k.UpdateStatus(instance, func() { + instance.Status.AuthoritativeAPI = "" + })).Should(Succeed()) + + By("Verifying the paused condition is still approproately set to false") + Eventually(k.Object(instance), timeout).Should(HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", Equal(machine.PausedCondition)), + HaveField("Status", Equal(corev1.ConditionFalse)), + HaveField("Message", Equal("The AuthoritativeAPI is not set")), + )))) + }) }) }) - -func cleanResources(namespace string) error { - machineSet := &machinev1.MachineSet{} - if err := k8sClient.DeleteAllOf(ctx, machineSet, client.InNamespace(namespace)); err != nil { - return err - } - - machine := &machinev1.Machine{} - if err := k8sClient.DeleteAllOf(ctx, machine, client.InNamespace(namespace)); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/.gitignore b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e660fd93d3196215552065b1e63bf6a2f393ed86 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/Makefile b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d9c503ed916d5e1367cf05c6c0a1a4a561078f6b --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/Makefile @@ -0,0 +1,34 @@ +GO111MODULE = on +export GO111MODULE +GOFLAGS ?= -mod=vendor +export GOFLAGS +GOPROXY ?= +export GOPROXY + +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.28 + +PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) +ENVTEST = go run ${PROJECT_DIR}/vendor/sigs.k8s.io/controller-runtime/tools/setup-envtest +GOLANGCI_LINT = go run ${PROJECT_DIR}/vendor/github.com/golangci/golangci-lint/cmd/golangci-lint + +.PHONY: all +all: lint unit + +.PHONY: vendor +vendor: ## Update vendor directory + go mod tidy + go mod vendor + go mod verify + +.PHONY: lint +lint: ## Go lint your code + ( GOLANGCI_LINT_CACHE=$(PROJECT_DIR)/.cache $(GOLANGCI_LINT) run --config ../.golangci.yaml --timeout 10m ) + +.PHONY: unit +unit: ## Run unit tests + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path --bin-dir $(PROJECT_DIR)/bin)" ./hack/test.sh + +.PHONY: help +help: + @grep -E '^[a-zA-Z/0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/cleanup.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/cleanup.go new file mode 100644 index 0000000000000000000000000000000000000000..cb1b317177c1c80aa13b11b5cad87a3a30bcbf32 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/cleanup.go @@ -0,0 +1,150 @@ +/* +Copyright 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testutils + +import ( + "context" + "fmt" + + "github.com/onsi/gomega" + + corev1resourcebuilder "github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1" + corev1 "k8s.io/api/core/v1" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreclient "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest/komega" +) + +// CleanupResources will clean up resources of the types of objects given in a particular namespace if given. +// The namespace will then be removed once it has been emptied. +// This utility is intended to be used in AfterEach blocks to clean up from a specific test. +// It calls various gomega assertions so will fail a test if there are any errors. +func CleanupResources(g gomega.Gomega, ctx context.Context, cfg *rest.Config, k8sClient client.Client, namespace string, objs ...client.Object) { + for _, obj := range objs { + cleanupResource(g, ctx, k8sClient, namespace, obj) + } + + if namespace != "" { + removeNamespace(g, ctx, cfg, k8sClient, namespace) + } +} + +// cleanupResource removes all of a particular resource within a namespace. +func cleanupResource(g gomega.Gomega, ctx context.Context, k8sClient client.Client, namespace string, obj client.Object) { + removeFinalizersFromAll(g, ctx, k8sClient, namespace, obj) + + g.Eventually(func() (client.ObjectList, error) { + if err := k8sClient.DeleteAllOf(ctx, obj, client.InNamespace(namespace)); err != nil { + return nil, fmt.Errorf("error deleting resource list: %w", err) + } + + listObj := newListFromObject(g, k8sClient, obj) + + return komega.ObjectList(listObj, client.InNamespace(namespace))() + }).Should(gomega.HaveField("Items", gomega.HaveLen(0))) +} + +// removeFinalizersFromAll removes any finalizers from all of the objects of the given object kind, +// in the namespace provided. +func removeFinalizersFromAll(g gomega.Gomega, ctx context.Context, k8sClient client.Client, namespace string, obj client.Object) { + listObj := newListFromObject(g, k8sClient, obj) + + g.Expect(k8sClient.List(ctx, listObj, client.InNamespace(namespace))).Should(gomega.Succeed()) + + listItems, err := apimeta.ExtractList(listObj) + g.Expect(err).ToNot(gomega.HaveOccurred()) + + for _, item := range listItems { + o, ok := item.(client.Object) + g.Expect(ok).To(gomega.BeTrue()) + + removeFinalizers(g, o) + } +} + +// removeFinalizers removes all finalizers from the object given. +// Finalizers must be removed one by one else the API server will reject the update. +func removeFinalizers(g gomega.Gomega, obj client.Object) { + filter := func(finalizers []string, toRemove string) []string { + out := []string{} + + for _, f := range finalizers { + if f != toRemove { + out = append(out, f) + } + } + + return out + } + + for _, finalizer := range obj.GetFinalizers() { + g.Eventually(komega.Update(obj, func() { + obj.SetFinalizers(filter(obj.GetFinalizers(), finalizer)) + })).Should(gomega.Succeed()) + } +} + +// newListFromObject converts an individual object type into a list object type to allow the +// the list to be checked for emptiness. +func newListFromObject(g gomega.Gomega, k8sClient client.Client, obj client.Object) client.ObjectList { + objGVKs, _, err := k8sClient.Scheme().ObjectKinds(obj) + g.Expect(err).ToNot(gomega.HaveOccurred()) + g.Expect(objGVKs).To(gomega.HaveLen(1)) + + listGVK := objGVKs[0] + listGVK.Kind = fmt.Sprintf("%sList", listGVK.Kind) + + newObj, err := k8sClient.Scheme().New(listGVK) + g.Expect(err).ToNot(gomega.HaveOccurred()) + + listObj, ok := newObj.(client.ObjectList) + g.Expect(ok).To(gomega.BeTrue()) + + return listObj +} + +// removeNamespace handles the namespace finalization act that is normally performed by the garbage collector +// once it is happy that the namespace has no objects left within it. +func removeNamespace(g gomega.Gomega, ctx context.Context, cfg *rest.Config, k8sClient client.Client, namespace string) { + coreClient, err := coreclient.NewForConfig(cfg) + g.Expect(err).ToNot(gomega.HaveOccurred()) + + nsBuilder := corev1resourcebuilder.Namespace().WithName(namespace) + + // Delete the namespace + ns := nsBuilder.Build() + g.Expect(k8sClient.Delete(ctx, ns)).To(gomega.Succeed()) + + // Remove the finalizer + g.Eventually(func() error { + if err := komega.Get(ns)(); err != nil { + return fmt.Errorf("could not get namespace: %w", err) + } + ns.Spec.Finalizers = []corev1.FinalizerName{} + + _, err := coreClient.Namespaces().Finalize(ctx, ns, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("could not finalize namespace: %w", err) + } + + return nil + }).Should(gomega.Succeed()) +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/conditions.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/conditions.go new file mode 100644 index 0000000000000000000000000000000000000000..728b02cd8fb1d33d50cb179fe83aea8d0c57d5d0 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/conditions.go @@ -0,0 +1,232 @@ +/* +Copyright 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testutils + +import ( + "errors" + "fmt" + "strings" + + "github.com/onsi/gomega" + "github.com/onsi/gomega/types" + + configv1 "github.com/openshift/api/config/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// errActualTypeMismatchCondition is used when the type of the actual object does not match the expected type of Condition. +var errActualTypeMismatchCondition = errors.New("actual should be of type Condition") + +// MatchConditions returns a custom matcher to check equality of a slice of metav1.Condtion. +func MatchConditions(expected []metav1.Condition) types.GomegaMatcher { + return &matchConditions{ + expected: expected, + } +} + +type matchConditions struct { + expected []metav1.Condition +} + +// Match checks for equality between the actual and expected objects. +func (m matchConditions) Match(actual interface{}) (success bool, err error) { + elems := []interface{}{} + for _, condition := range m.expected { + elems = append(elems, MatchCondition(condition)) + } + + ok, err := gomega.ConsistOf(elems).Match(actual) + if !ok { + return false, wrap(err, "conditions did not match") + } + + return true, nil +} + +// FailureMessage is the message returned to the test when the actual and expected +// objects do not match. +func (m matchConditions) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) +} + +// NegatedFailureMessage is the negated version of the FailureMessage. +func (m matchConditions) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) +} + +// MatchCondition returns a custom matcher to check equality of metav1.Condition. +func MatchCondition(expected metav1.Condition) types.GomegaMatcher { + return &matchCondition{ + expected: expected, + } +} + +type matchCondition struct { + expected metav1.Condition +} + +// Match checks for equality between the actual and expected objects. +// +//nolint:dupl +func (m matchCondition) Match(actual interface{}) (success bool, err error) { + actualCondition, ok := actual.(metav1.Condition) + if !ok { + return false, errActualTypeMismatchCondition + } + + ok, err = gomega.Equal(m.expected.Type).Match(actualCondition.Type) + if !ok { + return false, wrap(err, "condition type does not match") + } + + ok, err = gomega.Equal(m.expected.Status).Match(actualCondition.Status) + if !ok { + return false, wrap(err, "condition status does not match") + } + + ok, err = gomega.Equal(m.expected.Reason).Match(actualCondition.Reason) + if !ok { + return false, wrap(err, "condition reason does not match") + } + + ok, err = gomega.Equal(m.expected.Message).Match(actualCondition.Message) + if !ok { + return false, wrap(err, "condition message does not match") + } + + return true, nil +} + +// FailureMessage is the message returned to the test when the actual and expected +// objects do not match. +func (m matchCondition) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) +} + +// NegatedFailureMessage is the negated version of the FailureMessage. +func (m matchCondition) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) +} + +// errActualTypeMismatchClusterOperatorStatusCondition is used when the type of the actual object does not match +// the expected type of ClusterOperatorStatusCondition. +var errActualTypeMismatchClusterOperatorStatusCondition = errors.New("actual should be of type ClusterOperatorStatusCondition") + +// MatchClusterOperatorStatusConditions returns a custom matcher to check equality of configv1.ClusterOperatorStatusConditions. +func MatchClusterOperatorStatusConditions(expected []configv1.ClusterOperatorStatusCondition) types.GomegaMatcher { + return &matchClusterOperatorConditions{ + expected: expected, + } +} + +type matchClusterOperatorConditions struct { + expected []configv1.ClusterOperatorStatusCondition +} + +// Match checks for equality between the actual and expected objects. +func (m matchClusterOperatorConditions) Match(actual interface{}) (success bool, err error) { + elems := []interface{}{} + for _, condition := range m.expected { + elems = append(elems, MatchClusterOperatorStatusCondition(condition)) + } + + ok, err := gomega.ConsistOf(elems).Match(actual) + if !ok { + return false, wrap(err, "conditions did not match") + } + + return true, nil +} + +// FailureMessage is the message returned to the test when the actual and expected +// objects do not match. +func (m matchClusterOperatorConditions) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) +} + +// NegatedFailureMessage is the negated version of the FailureMessage. +func (m matchClusterOperatorConditions) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) +} + +// MatchClusterOperatorStatusCondition returns a custom matcher to check equality of configv1.ClusterOperatorStatusCondition. +func MatchClusterOperatorStatusCondition(expected configv1.ClusterOperatorStatusCondition) types.GomegaMatcher { + return &matchClusterOperatorCondition{ + expected: expected, + } +} + +type matchClusterOperatorCondition struct { + expected configv1.ClusterOperatorStatusCondition +} + +// Match checks for equality between the actual and expected objects. +// +//nolint:dupl +func (m matchClusterOperatorCondition) Match(actual interface{}) (success bool, err error) { + actualCondition, ok := actual.(configv1.ClusterOperatorStatusCondition) + if !ok { + return false, errActualTypeMismatchClusterOperatorStatusCondition + } + + ok, err = gomega.Equal(m.expected.Type).Match(actualCondition.Type) + if !ok { + return false, wrap(err, "condition type does not match") + } + + ok, err = gomega.Equal(m.expected.Status).Match(actualCondition.Status) + if !ok { + return false, wrap(err, "condition status does not match") + } + + ok, err = gomega.Equal(m.expected.Reason).Match(actualCondition.Reason) + if !ok { + return false, wrap(err, "condition reason does not match") + } + + ok, err = gomega.Equal(m.expected.Message).Match(actualCondition.Message) + if !ok { + return false, wrap(err, "condition message does not match") + } + + return true, nil +} + +// FailureMessage is the message returned to the test when the actual and expected +// objects do not match. +func (m matchClusterOperatorCondition) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) +} + +// NegatedFailureMessage is the negated version of the FailureMessage. +func (m matchClusterOperatorCondition) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) +} + +// wrap wraps an error with the given message if the error isn't nil. +func wrap(err error, msg string) error { + if err == nil { + return nil + } + + if !strings.HasSuffix(msg, "%w") { + msg = fmt.Sprintf("%s: %%w", msg) + } + + // We are expecting the passed messages to wrap the error, so skip linting. + return fmt.Errorf(msg, err) //nolint:goerr113 +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/logger.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/logger.go new file mode 100644 index 0000000000000000000000000000000000000000..ea36f78319ebf97a925dfdb20384084648beaf81 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/logger.go @@ -0,0 +1,116 @@ +/* +Copyright 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testutils + +import ( + "github.com/go-logr/logr" +) + +// LogEntry captures the information about a particular log line. +type LogEntry struct { + // Error is the error passed to an error log line. + Error error + + // KeysAndValues are the keys and values that were logged with + // the log line. + KeysAndValues []interface{} + + // Level is the level of the info log line. + Level int + + // Messages is the message from the log line. + Message string +} + +// TestLogger provides a logr.Logger and access to a list of log +// entries logged via the logger. +type TestLogger interface { + Entries() []LogEntry + Logger() logr.Logger +} + +// NewTestLogger constructs a new TestLogger. +func NewTestLogger() TestLogger { + l := &testLogger{ + entries: &[]LogEntry{}, + } + l.logger = logr.New(l) + + return l +} + +// testLogger is an implementation of the TestLogger interface. +type testLogger struct { + entries *[]LogEntry + keysAndValues []interface{} + logger logr.Logger + runtimeInfo logr.RuntimeInfo +} + +// Logger provides the TestLoggers logr.Logger. +func (t *testLogger) Logger() logr.Logger { + return t.logger +} + +// Entries returns the previously logged log entries. +func (t *testLogger) Entries() []LogEntry { + return *t.entries +} + +// Init configures the logr.LogSink implementation. +func (t *testLogger) Init(info logr.RuntimeInfo) { + t.runtimeInfo = info +} + +// Enabled is used to determine whether an info log should be logged. +func (t *testLogger) Enabled(_ int) bool { + // Always return true so that we capture every log line in the test. + return true +} + +// Info accepts an info log line. +func (t *testLogger) Info(level int, msg string, keysAndValues ...interface{}) { + *t.entries = append(*t.entries, LogEntry{ + KeysAndValues: append(t.keysAndValues, keysAndValues...), + Level: level, + Message: msg, + }) +} + +// Error accepts an error log line. +func (t *testLogger) Error(err error, msg string, keysAndValues ...interface{}) { + *t.entries = append(*t.entries, LogEntry{ + Error: err, + KeysAndValues: append(t.keysAndValues, keysAndValues...), + Message: msg, + }) +} + +// WithValues creates a child logger with additional keys and values attached. +func (t *testLogger) WithValues(keysAndValues ...interface{}) logr.LogSink { + return &testLogger{ + runtimeInfo: t.runtimeInfo, + logger: t.logger, + entries: t.entries, + keysAndValues: append(t.keysAndValues, keysAndValues...), + } +} + +// WithName sets the name of the logger. This is not currently supported. +func (t *testLogger) WithName(name string) logr.LogSink { + return t +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/configmap.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/configmap.go new file mode 100644 index 0000000000000000000000000000000000000000..c99ba7277f2a45e3b95439812e11d2263ea4b803 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/configmap.go @@ -0,0 +1,92 @@ +/* +Copyright 2023 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ConfigMap creates a new ConfigMap builder. +func ConfigMap() ConfigMapBuilder { + return ConfigMapBuilder{} +} + +// ConfigMapBuilder is used to build out a ConfigMap object. +type ConfigMapBuilder struct { + generateName string + name string + namespace string + labels map[string]string + data map[string]string +} + +// Build builds a new ConfigMap based on the configuration provided. +func (m ConfigMapBuilder) Build() *corev1.ConfigMap { + ConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: m.generateName, + Name: m.name, + Namespace: m.namespace, + Labels: m.labels, + }, + Data: m.data, + } + + return ConfigMap +} + +// WithData sets the data for the ConfigMap builder. +func (m ConfigMapBuilder) WithData(data map[string]string) ConfigMapBuilder { + m.data = data + return m +} + +// WithGenerateName sets the generateName for the ConfigMap builder. +func (m ConfigMapBuilder) WithGenerateName(generateName string) ConfigMapBuilder { + m.generateName = generateName + return m +} + +// WithLabel sets the labels for the ConfigMap builder. +func (m ConfigMapBuilder) WithLabel(key, value string) ConfigMapBuilder { + if m.labels == nil { + m.labels = make(map[string]string) + } + + m.labels[key] = value + + return m +} + +// WithLabels sets the labels for the ConfigMap builder. +func (m ConfigMapBuilder) WithLabels(labels map[string]string) ConfigMapBuilder { + m.labels = labels + return m +} + +// WithName sets the name for the ConfigMap builder. +func (m ConfigMapBuilder) WithName(name string) ConfigMapBuilder { + m.name = name + return m +} + +// WithNamespace sets the namespace for the ConfigMap builder. +func (m ConfigMapBuilder) WithNamespace(namespace string) ConfigMapBuilder { + m.namespace = namespace + return m +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/namespace.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/namespace.go new file mode 100644 index 0000000000000000000000000000000000000000..e2cd1f859c510496dd48fcd83dde23c0dedd1781 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/namespace.go @@ -0,0 +1,55 @@ +/* +Copyright 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Namespace creates a new namespace builder. +func Namespace() NamespaceBuilder { + return NamespaceBuilder{} +} + +// NamespaceBuilder is used to build out a namespace object. +type NamespaceBuilder struct { + generateName string + name string +} + +// Build builds a new namespace based on the configuration provided. +func (n NamespaceBuilder) Build() *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: n.generateName, + Name: n.name, + }, + } +} + +// WithGenerateName sets the generateName for the namespace builder. +func (n NamespaceBuilder) WithGenerateName(generateName string) NamespaceBuilder { + n.generateName = generateName + return n +} + +// WithName sets the name for the namespace builder. +func (n NamespaceBuilder) WithName(name string) NamespaceBuilder { + n.name = name + return n +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/node.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/node.go new file mode 100644 index 0000000000000000000000000000000000000000..ff103b767880c7d0092a0e36131b80b6de893562 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/node.go @@ -0,0 +1,121 @@ +/* +Copyright 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + masterNodeRoleLabel = "node-role.kubernetes.io/master" + workerNodeRoleLabel = "node-role.kubernetes.io/worker" +) + +// Node creates a new node builder. +func Node() NodeBuilder { + return NodeBuilder{} +} + +// NodeBuilder is used to build out a node object. +type NodeBuilder struct { + generateName string + name string + labels map[string]string + conditions []corev1.NodeCondition +} + +// Build builds a new node based on the configuration provided. +func (m NodeBuilder) Build() *corev1.Node { + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: m.generateName, + Name: m.name, + Labels: m.labels, + }, + Status: corev1.NodeStatus{ + Conditions: m.conditions, + }, + } + + return node +} + +// AsWorker sets the worker role on the node labels for the node builder. +func (m NodeBuilder) AsWorker() NodeBuilder { + return m.WithLabel(workerNodeRoleLabel, "") +} + +// AsMaster sets the master role on the node labels for the node builder. +func (m NodeBuilder) AsMaster() NodeBuilder { + return m.WithLabel(masterNodeRoleLabel, "") +} + +// AsNotReady sets the node as ready for the node builder. +func (m NodeBuilder) AsNotReady() NodeBuilder { + return m.WithConditions([]corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionFalse, + }, + }) +} + +// AsReady sets the node as ready for the node builder. +func (m NodeBuilder) AsReady() NodeBuilder { + return m.WithConditions([]corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }) +} + +// WithConditions sets the conditions for the node builder. +func (m NodeBuilder) WithConditions(conditions []corev1.NodeCondition) NodeBuilder { + m.conditions = conditions + return m +} + +// WithGenerateName sets the generateName for the node builder. +func (m NodeBuilder) WithGenerateName(generateName string) NodeBuilder { + m.generateName = generateName + return m +} + +// WithLabel sets the labels for the node builder. +func (m NodeBuilder) WithLabel(key, value string) NodeBuilder { + if m.labels == nil { + m.labels = make(map[string]string) + } + + m.labels[key] = value + + return m +} + +// WithLabels sets the labels for the node builder. +func (m NodeBuilder) WithLabels(labels map[string]string) NodeBuilder { + m.labels = labels + return m +} + +// WithName sets the name for the node builder. +func (m NodeBuilder) WithName(name string) NodeBuilder { + m.name = name + return m +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/secret.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/secret.go new file mode 100644 index 0000000000000000000000000000000000000000..98d8ce04fb291552a340eb719ed9232b04e6681b --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/secret.go @@ -0,0 +1,92 @@ +/* +Copyright 2023 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Secret creates a new Secret builder. +func Secret() SecretBuilder { + return SecretBuilder{} +} + +// SecretBuilder is used to build out a Secret object. +type SecretBuilder struct { + generateName string + name string + namespace string + labels map[string]string + data map[string][]byte +} + +// Build builds a new Secret based on the configuration provided. +func (m SecretBuilder) Build() *corev1.Secret { + Secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: m.generateName, + Name: m.name, + Namespace: m.namespace, + Labels: m.labels, + }, + Data: m.data, + } + + return Secret +} + +// WithData sets the data for the Secret builder. +func (m SecretBuilder) WithData(data map[string][]byte) SecretBuilder { + m.data = data + return m +} + +// WithGenerateName sets the generateName for the Secret builder. +func (m SecretBuilder) WithGenerateName(generateName string) SecretBuilder { + m.generateName = generateName + return m +} + +// WithLabel sets the labels for the Secret builder. +func (m SecretBuilder) WithLabel(key, value string) SecretBuilder { + if m.labels == nil { + m.labels = make(map[string]string) + } + + m.labels[key] = value + + return m +} + +// WithLabels sets the labels for the Secret builder. +func (m SecretBuilder) WithLabels(labels map[string]string) SecretBuilder { + m.labels = labels + return m +} + +// WithName sets the name for the Secret builder. +func (m SecretBuilder) WithName(name string) SecretBuilder { + m.name = name + return m +} + +// WithNamespace sets the namespace for the Secret builder. +func (m SecretBuilder) WithNamespace(namespace string) SecretBuilder { + m.namespace = namespace + return m +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/service.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/service.go new file mode 100644 index 0000000000000000000000000000000000000000..6ff97d8d27b38d110fc31aafc8271ca8fd6379ea --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1/service.go @@ -0,0 +1,102 @@ +/* +Copyright 2023 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Service creates a new Service builder. +func Service() ServiceBuilder { + return ServiceBuilder{} +} + +// ServiceBuilder is used to build out a Service object. +type ServiceBuilder struct { + generateName string + name string + namespace string + labels map[string]string + selector map[string]string + ports []corev1.ServicePort +} + +// Build builds a new Service based on the configuration provided. +func (m ServiceBuilder) Build() *corev1.Service { + Service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: m.generateName, + Name: m.name, + Namespace: m.namespace, + Labels: m.labels, + }, + Spec: corev1.ServiceSpec{ + Ports: m.ports, + Selector: m.selector, + }, + } + + return Service +} + +// WithGenerateName sets the generateName for the Service builder. +func (m ServiceBuilder) WithGenerateName(generateName string) ServiceBuilder { + m.generateName = generateName + return m +} + +// WithLabel sets the labels for the Service builder. +func (m ServiceBuilder) WithLabel(key, value string) ServiceBuilder { + if m.labels == nil { + m.labels = make(map[string]string) + } + + m.labels[key] = value + + return m +} + +// WithLabels sets the labels for the Service builder. +func (m ServiceBuilder) WithLabels(labels map[string]string) ServiceBuilder { + m.labels = labels + return m +} + +// WithName sets the name for the Service builder. +func (m ServiceBuilder) WithName(name string) ServiceBuilder { + m.name = name + return m +} + +// WithNamespace sets the namespace for the Service builder. +func (m ServiceBuilder) WithNamespace(namespace string) ServiceBuilder { + m.namespace = namespace + return m +} + +// WithPorts sets the ports for the Service builder. +func (m ServiceBuilder) WithPorts(ports []corev1.ServicePort) ServiceBuilder { + m.ports = ports + return m +} + +// WithSelector sets the selector for the Service builder. +func (m ServiceBuilder) WithSelector(selector map[string]string) ServiceBuilder { + m.selector = selector + return m +} diff --git a/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/tools.go b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/tools.go new file mode 100644 index 0000000000000000000000000000000000000000..3e758e892daaae549886fb33c5f232256d8e7b29 --- /dev/null +++ b/vendor/github.com/openshift/cluster-api-actuator-pkg/testutils/tools.go @@ -0,0 +1,13 @@ +//go:build tools +// +build tools + +// Official workaround to track tool dependencies with go modules: +// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module + +package tools + +import ( + _ "github.com/golangci/golangci-lint/cmd/golangci-lint" + _ "github.com/onsi/ginkgo/v2/ginkgo" + _ "sigs.k8s.io/controller-runtime/tools/setup-envtest" +) diff --git a/vendor/modules.txt b/vendor/modules.txt index 19bdbec2c75d181a92bca5edf60440cc8af623e4..b89681e536a7c4922ebca2b35c2842371e4a5030 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -833,8 +833,10 @@ github.com/openshift/client-go/operator/applyconfigurations/internal github.com/openshift/client-go/operator/applyconfigurations/operator/v1 # github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20240626103413-ddea9c7c0aca ## explicit; go 1.21 +github.com/openshift/cluster-api-actuator-pkg/testutils github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/config/v1 +github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/core/v1 github.com/openshift/cluster-api-actuator-pkg/testutils/resourcebuilder/machine/v1beta1 # github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20240909043600-373ac49835bf ## explicit; go 1.22.0