diff --git a/api/v1alpha1/operator_types.go b/api/v1alpha1/operator_types.go index 79cfea8751..0c0e2a8533 100644 --- a/api/v1alpha1/operator_types.go +++ b/api/v1alpha1/operator_types.go @@ -46,7 +46,8 @@ type OperatorSpec struct { const ( // TODO(user): add more Types, here and into init() - TypeResolved = "Resolved" + TypeInstalled = "Installed" + TypeResolved = "Resolved" ReasonBundleLookupFailed = "BundleLookupFailed" ReasonInstallationFailed = "InstallationFailed" @@ -61,6 +62,7 @@ const ( func init() { // TODO(user): add Types from above operatorutil.ConditionTypes = append(operatorutil.ConditionTypes, + TypeInstalled, TypeResolved, ) // TODO(user): add Reasons from above @@ -78,6 +80,8 @@ func init() { // OperatorStatus defines the observed state of Operator type OperatorStatus struct { + // +optional + InstalledBundleResource string `json:"installedBundleResource,omitempty"` // +optional ResolvedBundleResource string `json:"resolvedBundleResource,omitempty"` diff --git a/config/crd/bases/operators.operatorframework.io_operators.yaml b/config/crd/bases/operators.operatorframework.io_operators.yaml index 1a43f50144..7f1bc3a050 100644 --- a/config/crd/bases/operators.operatorframework.io_operators.yaml +++ b/config/crd/bases/operators.operatorframework.io_operators.yaml @@ -131,6 +131,8 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map + installedBundleResource: + type: string resolvedBundleResource: type: string type: object diff --git a/controllers/operator_controller.go b/controllers/operator_controller.go index 4b21c40ac0..006b6144e8 100644 --- a/controllers/operator_controller.go +++ b/controllers/operator_controller.go @@ -106,6 +106,16 @@ func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool { func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha1.Operator) (ctrl.Result, error) { // validate spec if err := validators.ValidateOperatorSpec(op); err != nil { + // Set the TypeInstalled condition to Unknown to indicate that the resolution + // hasn't been attempted yet, due to the spec being invalid. + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: "installation has not been attempted as spec is invalid", + ObservedGeneration: op.GetGeneration(), + }) // Set the TypeResolved condition to Unknown to indicate that the resolution // hasn't been attempted yet, due to the spec being invalid. op.Status.ResolvedBundleResource = "" @@ -121,6 +131,14 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha // run resolution solution, err := r.Resolver.Resolve(ctx) if err != nil { + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: "installation has not been attempted as resolution failed", + ObservedGeneration: op.GetGeneration(), + }) op.Status.ResolvedBundleResource = "" apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ Type: operatorsv1alpha1.TypeResolved, @@ -136,6 +154,14 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha // Operator's desired package name. bundleEntity, err := r.getBundleEntityFromSolution(solution, op.Spec.PackageName) if err != nil { + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: "installation has not been attempted as resolution failed", + ObservedGeneration: op.GetGeneration(), + }) op.Status.ResolvedBundleResource = "" apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ Type: operatorsv1alpha1.TypeResolved, @@ -150,6 +176,14 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha // Get the bundle image reference for the bundle bundleImage, err := bundleEntity.BundlePath() if err != nil { + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: "installation has not been attempted as resolution failed", + ObservedGeneration: op.GetGeneration(), + }) op.Status.ResolvedBundleResource = "" apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ Type: operatorsv1alpha1.TypeResolved, @@ -176,6 +210,14 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha dep := r.generateExpectedBundleDeployment(*op, bundleImage) if err := r.ensureBundleDeployment(ctx, dep); err != nil { // originally Reason: operatorsv1alpha1.ReasonInstallationFailed + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionFalse, + Reason: operatorsv1alpha1.ReasonInstallationFailed, + Message: err.Error(), + ObservedGeneration: op.GetGeneration(), + }) return ctrl.Result{}, err } @@ -183,13 +225,78 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha existingTypedBundleDeployment := &rukpakv1alpha1.BundleDeployment{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(dep.UnstructuredContent(), existingTypedBundleDeployment); err != nil { // originally Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown + op.Status.InstalledBundleResource = "" + apimeta.SetStatusCondition(&op.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: err.Error(), + ObservedGeneration: op.GetGeneration(), + }) return ctrl.Result{}, err } + // Let's set the proper Installed condition and InstalledBundleResource field based on the + // existing BundleDeployment object status. + installedCond, InstalledBundleResource := mapBDStatusToInstalledCondition(existingTypedBundleDeployment, op) + apimeta.SetStatusCondition(&op.Status.Conditions, installedCond) + op.Status.InstalledBundleResource = InstalledBundleResource + // set the status of the operator based on the respective bundle deployment status conditions. return ctrl.Result{}, nil } +func mapBDStatusToInstalledCondition(existingTypedBundleDeployment *rukpakv1alpha1.BundleDeployment, op *operatorsv1alpha1.Operator) (metav1.Condition, string) { + bundleDeploymentReady := apimeta.FindStatusCondition(existingTypedBundleDeployment.Status.Conditions, rukpakv1alpha1.TypeInstalled) + if bundleDeploymentReady == nil { + return metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: "bundledeployment status is unknown", + ObservedGeneration: op.GetGeneration(), + }, "" + } + + if bundleDeploymentReady.Status != metav1.ConditionTrue { + return metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionFalse, + Reason: operatorsv1alpha1.ReasonInstallationFailed, + Message: fmt.Sprintf("bundledeployment not ready: %s", bundleDeploymentReady.Message), + ObservedGeneration: op.GetGeneration(), + }, "" + } + + bundleDeploymentSource := existingTypedBundleDeployment.Spec.Template.Spec.Source + switch bundleDeploymentSource.Type { + case rukpakv1alpha1.SourceTypeImage: + return metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionTrue, + Reason: operatorsv1alpha1.ReasonSuccess, + Message: fmt.Sprintf("installed from %q", bundleDeploymentSource.Image.Ref), + ObservedGeneration: op.GetGeneration(), + }, bundleDeploymentSource.Image.Ref + case rukpakv1alpha1.SourceTypeGit: + return metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionTrue, + Reason: operatorsv1alpha1.ReasonSuccess, + Message: fmt.Sprintf("installed from %q", bundleDeploymentSource.Git.Repository+"@"+bundleDeploymentSource.Git.Ref.Commit), + ObservedGeneration: op.GetGeneration(), + }, bundleDeploymentSource.Git.Repository + "@" + bundleDeploymentSource.Git.Ref.Commit + default: + return metav1.Condition{ + Type: operatorsv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: operatorsv1alpha1.ReasonInstallationStatusUnknown, + Message: fmt.Sprintf("unknown bundledeployment source type %q", bundleDeploymentSource.Type), + ObservedGeneration: op.GetGeneration(), + }, "" + } +} + func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Solution, packageName string) (*entity.BundleEntity, error) { for _, variable := range solution.SelectedVariables() { switch v := variable.(type) { diff --git a/controllers/operator_controller_test.go b/controllers/operator_controller_test.go index 9d0f3cdd40..9b21357d3f 100644 --- a/controllers/operator_controller_test.go +++ b/controllers/operator_controller_test.go @@ -75,6 +75,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -110,6 +111,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -117,6 +119,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(Equal(fmt.Sprintf("package '%s' at version '0.50.0' not found", pkgName))) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) When("the operator specifies a valid available package", func() { @@ -154,12 +161,20 @@ var _ = Describe("Operator Controller Test", func() { It("sets the resolvedBundleResource status field", func() { Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) }) + It("sets the InstalledBundleResource status field", func() { + Expect(operator.Status.InstalledBundleResource).To(Equal("")) + }) It("sets the status on operator", func() { cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) }) }) When("the expected BundleDeployment already exists", func() { @@ -225,6 +240,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected status conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -232,6 +248,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) }) }) @@ -267,6 +288,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -274,6 +296,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) }) It("verify operator status when `HasValidBundle` condition of rukpak is false", func() { @@ -300,6 +327,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -307,6 +335,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) }) It("verify operator status when `InstallReady` condition of rukpak is false", func() { @@ -333,6 +366,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -340,6 +374,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + 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() { @@ -366,6 +405,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -373,6 +413,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) + Expect(cond.Message).To(Equal("installed from \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) }) It("verify any other unknown status of bundledeployment", func() { @@ -406,6 +451,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -413,6 +459,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(Equal("bundledeployment not ready: installing")) }) It("verify operator status when bundleDeployment installation status is unknown", func() { @@ -439,6 +490,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(op.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(op.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -446,6 +498,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionFalse)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed)) + Expect(cond.Message).To(Equal("bundledeployment not ready: installing")) }) }) @@ -501,12 +558,20 @@ var _ = Describe("Operator Controller Test", func() { It("sets the resolvedBundleResource status field", func() { Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) }) + It("sets the InstalledBundleResource status field", func() { + Expect(operator.Status.InstalledBundleResource).To(Equal("")) + }) It("sets resolution to unknown status", func() { cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) Expect(cond).NotTo(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) }) }) }) @@ -532,6 +597,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -539,6 +605,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(ContainSubstring(`error determining bundle path for entity`)) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) When("the operator specifies a duplicate package", func() { @@ -579,6 +650,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -586,6 +658,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(Equal(`duplicate identifier "required package prometheus" in input`)) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) When("the existing operator status is based on bundleDeployment", func() { @@ -664,6 +741,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -671,6 +749,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) By("fetching the bundled deployment") bd := &rukpakv1alpha1.BundleDeployment{} @@ -712,6 +795,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -719,6 +803,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess)) Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed\"")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("bundledeployment status is unknown")) By("fetching the bundled deployment") bd := &rukpakv1alpha1.BundleDeployment{} @@ -761,6 +850,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -768,6 +858,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(Equal(fmt.Sprintf("package '%s' at version '%s' in channel '%s' not found", pkgName, pkgVer, pkgChan))) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) When("the operator specifies a package in a channel that does not exist", func() { @@ -798,6 +893,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -805,6 +901,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(Equal(fmt.Sprintf("package '%s' in channel '%s' not found", pkgName, pkgChan))) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) When("the operator specifies a package version that does not exist in the channel", func() { @@ -838,6 +939,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -845,6 +947,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionFalse)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) Expect(cond.Message).To(Equal(fmt.Sprintf("package '%s' at version '%s' in channel '%s' not found", pkgName, pkgVer, pkgChan))) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) }) }) AfterEach(func() { @@ -896,6 +1003,7 @@ var _ = Describe("Operator Controller Test", func() { By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("")) + Expect(operator.Status.InstalledBundleResource).To(Equal("")) By("checking the expected conditions") cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) @@ -903,6 +1011,11 @@ var _ = Describe("Operator Controller Test", func() { Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionUnknown)) Expect(cond.Message).To(Equal("validation has not been attempted as spec is invalid")) + cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) + Expect(cond).NotTo(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) + Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) + Expect(cond.Message).To(Equal("installation has not been attempted as spec is invalid")) }) }) }) diff --git a/test/e2e/install_test.go b/test/e2e/install_test.go index efb6d30374..c714bf02a3 100644 --- a/test/e2e/install_test.go +++ b/test/e2e/install_test.go @@ -70,7 +70,7 @@ var _ = Describe("Operator Install", func() { Eventually(func(g Gomega) { err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator) g.Expect(err).ToNot(HaveOccurred()) - g.Expect(len(operator.Status.Conditions)).To(Equal(1)) + g.Expect(len(operator.Status.Conditions)).To(Equal(2)) cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) g.Expect(cond).ToNot(BeNil()) g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) @@ -81,6 +81,14 @@ var _ = Describe("Operator Install", func() { By("eventually installing the package successfully") Eventually(func(g Gomega) { + err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator) + g.Expect(err).ToNot(HaveOccurred()) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) + g.Expect(cond).ToNot(BeNil()) + g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) + g.Expect(cond.Message).To(ContainSubstring("installed from")) + g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty()) bd := rukpakv1alpha1.BundleDeployment{} err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd) g.Expect(err).ToNot(HaveOccurred())