From 7c37eda6b5b1917df8a947fc5e76c9f93dc4654f Mon Sep 17 00:00:00 2001 From: Varsha Prasad Narsing Date: Mon, 27 Feb 2023 16:28:24 -0500 Subject: [PATCH 1/2] Conditions: Map out-of-date bundleDeployment with an operator condition This PR brings in the change that sets the operator condition to Unknown when the BundleDeployment's conditions are out od date. Signed-off-by: Varsha Prasad Narsing --- controllers/operator_controller.go | 21 +- controllers/operator_controller_test.go | 318 +++++++++++++----------- 2 files changed, 190 insertions(+), 149 deletions(-) diff --git a/controllers/operator_controller.go b/controllers/operator_controller.go index 8492bb8df..e89968497 100644 --- a/controllers/operator_controller.go +++ b/controllers/operator_controller.go @@ -294,10 +294,6 @@ func verifyBDStatus(dep *rukpakv1alpha1.BundleDeployment) (metav1.ConditionStatu isValidBundleCond := apimeta.FindStatusCondition(dep.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) isInstalledCond := apimeta.FindStatusCondition(dep.Status.Conditions, rukpakv1alpha1.TypeInstalled) - if isValidBundleCond == nil && isInstalledCond == nil { - return metav1.ConditionUnknown, fmt.Sprintf("waiting for bundleDeployment %q status to be updated", dep.Name) - } - if isValidBundleCond != nil && isValidBundleCond.Status == metav1.ConditionFalse { return metav1.ConditionFalse, isValidBundleCond.Message } @@ -309,11 +305,21 @@ func verifyBDStatus(dep *rukpakv1alpha1.BundleDeployment) (metav1.ConditionStatu if isInstalledCond != nil && isInstalledCond.Status == metav1.ConditionTrue { return metav1.ConditionTrue, "install was successful" } - return metav1.ConditionUnknown, fmt.Sprintf("could not determine the state of bundleDeployment %s", dep.Name) + return metav1.ConditionUnknown, fmt.Sprintf("could not determine the state of BundleDeployment %s", dep.Name) } // mapBDStatusToReadyCondition returns the operator object's "TypeReady" condition based on the bundle deployment statuses. func mapBDStatusToReadyCondition(existingBD *rukpakv1alpha1.BundleDeployment, observedGeneration int64) metav1.Condition { + // if BundleDeployment status is stale, return an unknown condition. + if isBundleDepStale(existingBD) { + return metav1.Condition{ + Type: operatorsv1alpha1.TypeReady, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: fmt.Sprintf("waiting for BundleDeployment %q status to be updated. BundleDeployment conditions out of date.", existingBD.Name), + ObservedGeneration: observedGeneration, + } + } // update operator status: // 1. If the Operator "Ready" status is "Unknown": The status of successful bundleDeployment is unknown, wait till Rukpak updates the BD status. // 2. If the Operator "Ready" status is "True": Update the "successful resolution" status and return the result. @@ -338,3 +344,8 @@ func mapBDStatusToReadyCondition(existingBD *rukpakv1alpha1.BundleDeployment, ob ObservedGeneration: observedGeneration, } } + +// isBundleDepStale returns true if conditions are out of date. +func isBundleDepStale(bd *rukpakv1alpha1.BundleDeployment) bool { + return bd != nil && bd.Status.ObservedGeneration != bd.GetGeneration() +} diff --git a/controllers/operator_controller_test.go b/controllers/operator_controller_test.go index f1764ca19..e2c0c7b6d 100644 --- a/controllers/operator_controller_test.go +++ b/controllers/operator_controller_test.go @@ -117,7 +117,7 @@ var _ = Describe("Reconcile Test", func() { Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring("waiting for bundleDeployment")) + Expect(cond.Message).To(ContainSubstring("waiting for BundleDeployment")) }) }) When("the expected BundleDeployment already exists", func() { @@ -180,7 +180,7 @@ var _ = Describe("Reconcile Test", func() { Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring("waiting for bundleDeployment")) + Expect(cond.Message).To(ContainSubstring("waiting for BundleDeployment")) }) }) When("an out-of-date BundleDeployment exists", func() { @@ -223,12 +223,12 @@ var _ = Describe("Reconcile Test", func() { Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) }) - It("sets resolution success status", func() { + It("sets resolution to unknown status", func() { cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeReady) Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring("waiting for bundleDeployment")) + Expect(cond.Message).To(ContainSubstring("waiting for BundleDeployment")) }) }) }) @@ -348,180 +348,210 @@ var _ = Describe("Reconcile Test", func() { Expect(err).NotTo(HaveOccurred()) }) - It("verify operator status when bundle deployment is waiting to be created", func() { - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + When("The BundleDeployment status is stale", func() { + It("sets operator ready condition to unknown when bundle deployment status is stale", func() { + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring(`waiting for bundleDeployment`)) + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("waiting for BundleDeployment %q status to be updated. BundleDeployment conditions out of date.", bd.Name))) + }) }) - It("verify operator status when `HasValidBundle` condition of rukpak is false", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeHasValidBundle, - Status: metav1.ConditionFalse, - Message: "failed to unpack", - Reason: rukpakv1alpha1.ReasonUnpackFailed, + When("The BundleDeployment status is up-to-date", func() { + BeforeEach(func() { + bd.Status.ObservedGeneration = bd.GetGeneration() }) - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + It("verify operator status when bundle deployment is waiting to be created", func() { + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) - Expect(cond.Message).To(ContainSubstring(`failed to unpack`)) - }) + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) - It("verify operator status when `InstallReady` condition of rukpak is false", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionFalse, - Message: "failed to install", - Reason: rukpakv1alpha1.ReasonInstallFailed, + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) }) - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) + It("verify operator status when `HasValidBundle` condition of rukpak is false", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeHasValidBundle, + Status: metav1.ConditionFalse, + Message: "failed to unpack", + Reason: rukpakv1alpha1.ReasonUnpackFailed, + }) - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) - Expect(cond.Message).To(ContainSubstring(`failed to install`)) - }) + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) - It("verify operator status when `InstallReady` condition of rukpak is true", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionTrue, - Message: "operator installed successfully", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(ContainSubstring(`failed to unpack`)) }) - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) + It("verify operator status when `InstallReady` condition of rukpak is false", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionFalse, + Message: "failed to install", + Reason: rukpakv1alpha1.ReasonInstallFailed, + }) - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationSucceeded)) - Expect(cond.Message).To(ContainSubstring(`install was successful`)) - }) + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) - It("verify any other unknown status of bundledeployment", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeHasValidBundle, - Status: metav1.ConditionUnknown, - Message: "unpacking", - Reason: rukpakv1alpha1.ReasonUnpackSuccessful, + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(ContainSubstring(`failed to install`)) }) - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Message: "installing", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + It("verify operator status when `InstallReady` condition of rukpak is true", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionTrue, + Message: "operator installed successfully", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationSucceeded)) + Expect(cond.Message).To(ContainSubstring(`install was successful`)) }) - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) + It("verify any other unknown status of bundledeployment", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeHasValidBundle, + Status: metav1.ConditionUnknown, + Message: "unpacking", + Reason: rukpakv1alpha1.ReasonUnpackSuccessful, + }) - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Message: "installing", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring(`could not determine the state of bundleDeployment`)) - }) + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) - It("verify operator status when bundleDeployment installation status is unknown", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Message: "installing", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) }) - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) + It("verify operator status when bundleDeployment installation status is unknown", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Message: "installing", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring(`could not determine the state of bundleDeployment`)) + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) + }) }) }) AfterEach(func() { From c8b82fdcae750f81a16587f60ac3b978bc14bde5 Mon Sep 17 00:00:00 2001 From: Varsha Prasad Narsing Date: Fri, 3 Mar 2023 16:25:49 -0500 Subject: [PATCH 2/2] rearrange BundleDeployment tests Signed-off-by: Varsha Prasad Narsing --- controllers/operator_controller_test.go | 469 ++++++++++++------------ 1 file changed, 238 insertions(+), 231 deletions(-) diff --git a/controllers/operator_controller_test.go b/controllers/operator_controller_test.go index e2c0c7b6d..3a3940f37 100644 --- a/controllers/operator_controller_test.go +++ b/controllers/operator_controller_test.go @@ -121,9 +121,10 @@ var _ = Describe("Reconcile Test", func() { }) }) When("the expected BundleDeployment already exists", func() { + var bd *rukpakv1alpha1.BundleDeployment BeforeEach(func() { By("patching the existing BD") - err := cl.Create(ctx, &rukpakv1alpha1.BundleDeployment{ + bd = &rukpakv1alpha1.BundleDeployment{ ObjectMeta: metav1.ObjectMeta{ Name: opKey.Name, OwnerReferences: []metav1.OwnerReference{ @@ -151,37 +152,248 @@ var _ = Describe("Reconcile Test", func() { }, }, }, - }) - Expect(err).NotTo(HaveOccurred()) + } - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) + }) - By("fetching updated operator after reconcile") - Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred()) + When("the BundleDeployment spec is out of date", func() { + It("results in the expected BundleDeployment", func() { + By("modifying the BD spec and creating the object") + bd.Spec.ProvisionerClassName = "core-rukpak-io-helm" + err := cl.Create(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching updated operator after reconcile") + Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred()) + + By("checking the expected BD spec") + bd := &rukpakv1alpha1.BundleDeployment{} + err = cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd) + Expect(err).NotTo(HaveOccurred()) + Expect(bd.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain")) + Expect(bd.Spec.Template.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-registry")) + Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage)) + Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) + Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + + By("checking the expected status conditions") + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("waiting for BundleDeployment %q status to be updated. BundleDeployment conditions out of date.", bd.Name))) + }) }) - PIt("does not patch the BundleDeployment", func() { - // TODO: verify that no patch call is made. + + When("The BundleDeployment spec is up-to-date", func() { + BeforeEach(func() { + err := cl.Create(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + bd.Status.ObservedGeneration = bd.GetGeneration() + }) + + When("the BundleDeployment is not patched", func() { + PIt("does not patch the BundleDeployment", func() { + // TODO: verify that no patch call is made. + }) + }) + + When("The BundleDeployment status is mapped to the expected Operator status", func() { + It("verify operator status when bundle deployment is waiting to be created", func() { + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) + }) + + It("verify operator status when `HasValidBundle` condition of rukpak is false", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeHasValidBundle, + Status: metav1.ConditionFalse, + Message: "failed to unpack", + Reason: rukpakv1alpha1.ReasonUnpackFailed, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(ContainSubstring(`failed to unpack`)) + }) + + It("verify operator status when `InstallReady` condition of rukpak is false", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionFalse, + Message: "failed to install", + Reason: rukpakv1alpha1.ReasonInstallFailed, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(ContainSubstring(`failed to install`)) + }) + + It("verify operator status when `InstallReady` condition of rukpak is true", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionTrue, + Message: "operator installed successfully", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationSucceeded)) + Expect(cond.Message).To(ContainSubstring(`install was successful`)) + }) + + It("verify any other unknown status of bundledeployment", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeHasValidBundle, + Status: metav1.ConditionUnknown, + Message: "unpacking", + Reason: rukpakv1alpha1.ReasonUnpackSuccessful, + }) + + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Message: "installing", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) + }) + + It("verify operator status when bundleDeployment installation status is unknown", func() { + apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ + Type: rukpakv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Message: "installing", + Reason: rukpakv1alpha1.ReasonInstallationSucceeded, + }) + + By("updating the status of bundleDeployment") + err := cl.Status().Update(ctx, bd) + Expect(err).NotTo(HaveOccurred()) + + By("running reconcile") + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) + Expect(res).To(Equal(ctrl.Result{})) + Expect(err).NotTo(HaveOccurred()) + + By("fetching the updated operator after reconcile") + op := &operatorsv1alpha1.Operator{} + err = cl.Get(ctx, opKey, op) + Expect(err).NotTo(HaveOccurred()) + + By("checking the expected conditions") + cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) + }) + + }) + }) - It("results in the expected BundleDeployment", func() { - bd := &rukpakv1alpha1.BundleDeployment{} - err := cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd) + + AfterEach(func() { + err := cl.Delete(ctx, bd) Expect(err).NotTo(HaveOccurred()) - Expect(bd.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain")) - Expect(bd.Spec.Template.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-registry")) - Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage)) - Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) - Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) - }) - It("sets the status on operator", func() { - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(ContainSubstring("waiting for BundleDeployment")) }) + }) When("an out-of-date BundleDeployment exists", func() { BeforeEach(func() { @@ -348,211 +560,6 @@ var _ = Describe("Reconcile Test", func() { Expect(err).NotTo(HaveOccurred()) }) - When("The BundleDeployment status is stale", func() { - It("sets operator ready condition to unknown when bundle deployment status is stale", func() { - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(Equal(fmt.Sprintf("waiting for BundleDeployment %q status to be updated. BundleDeployment conditions out of date.", bd.Name))) - }) - }) - - When("The BundleDeployment status is up-to-date", func() { - BeforeEach(func() { - bd.Status.ObservedGeneration = bd.GetGeneration() - }) - - It("verify operator status when bundle deployment is waiting to be created", func() { - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) - }) - - It("verify operator status when `HasValidBundle` condition of rukpak is false", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeHasValidBundle, - Status: metav1.ConditionFalse, - Message: "failed to unpack", - Reason: rukpakv1alpha1.ReasonUnpackFailed, - }) - - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) - Expect(cond.Message).To(ContainSubstring(`failed to unpack`)) - }) - - It("verify operator status when `InstallReady` condition of rukpak is false", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionFalse, - Message: "failed to install", - Reason: rukpakv1alpha1.ReasonInstallFailed, - }) - - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) - Expect(cond.Message).To(ContainSubstring(`failed to install`)) - }) - - It("verify operator status when `InstallReady` condition of rukpak is true", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionTrue, - Message: "operator installed successfully", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, - }) - - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationSucceeded)) - Expect(cond.Message).To(ContainSubstring(`install was successful`)) - }) - - It("verify any other unknown status of bundledeployment", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeHasValidBundle, - Status: metav1.ConditionUnknown, - Message: "unpacking", - Reason: rukpakv1alpha1.ReasonUnpackSuccessful, - }) - - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Message: "installing", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, - }) - - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) - }) - - It("verify operator status when bundleDeployment installation status is unknown", func() { - apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{ - Type: rukpakv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Message: "installing", - Reason: rukpakv1alpha1.ReasonInstallationSucceeded, - }) - - By("updating the status of bundleDeployment") - err := cl.Status().Update(ctx, bd) - Expect(err).NotTo(HaveOccurred()) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - - By("fetching the updated operator after reconcile") - op := &operatorsv1alpha1.Operator{} - err = cl.Get(ctx, opKey, op) - Expect(err).NotTo(HaveOccurred()) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(Equal(fmt.Sprintf("could not determine the state of BundleDeployment %s", bd.Name))) - }) - }) }) AfterEach(func() { verifyInvariants(ctx, operator)