diff --git a/api/v1/clusterextensionrevision_types.go b/api/v1/clusterextensionrevision_types.go index b94abd107..13ac4ce2a 100644 --- a/api/v1/clusterextensionrevision_types.go +++ b/api/v1/clusterextensionrevision_types.go @@ -39,6 +39,7 @@ const ( ClusterExtensionRevisionReasonProbeFailure = "ProbeFailure" ClusterExtensionRevisionReasonIncomplete = "Incomplete" ClusterExtensionRevisionReasonProgressing = "Progressing" + ClusterExtensionRevisionReasonArchived = "Archived" ) // ClusterExtensionRevisionSpec defines the desired state of ClusterExtensionRevision. @@ -148,6 +149,8 @@ type ClusterExtensionRevisionStatus struct { // +kubebuilder:subresource:status // ClusterExtensionRevision is the Schema for the clusterextensionrevisions API +// +kubebuilder:printcolumn:name="Available",type=string,JSONPath=`.status.conditions[?(@.type=='Available')].status` +// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` type ClusterExtensionRevision struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml index 89a6f646b..5004c8c6f 100644 --- a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml +++ b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml @@ -15,7 +15,14 @@ spec: singular: clusterextensionrevision scope: Cluster versions: - - name: v1 + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: description: ClusterExtensionRevision is the Schema for the clusterextensionrevisions diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller.go b/internal/operator-controller/controllers/clusterextensionrevision_controller.go index fc6316e09..888249161 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller.go @@ -118,52 +118,8 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev revision, opts, previous := toBoxcutterRevision(rev) - if !rev.DeletionTimestamp.IsZero() || - rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { - // - // Teardown - // - tres, err := c.RevisionEngine.Teardown(ctx, *revision) - if err != nil { - meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, - Status: metav1.ConditionFalse, - Reason: ocv1.ClusterExtensionRevisionReasonReconcileFailure, - Message: err.Error(), - ObservedGeneration: rev.Generation, - }) - return ctrl.Result{}, fmt.Errorf("revision teardown: %v", err) - } - - l.Info("teardown report", "report", tres.String()) - if !tres.IsComplete() { - // TODO: If it is not complete, it seems like it would be good to update - // the status in some way to tell the user that the teardown is still - // in progress. - return ctrl.Result{}, nil - } - - if err := c.TrackingCache.Free(ctx, rev); err != nil { - meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, - Status: metav1.ConditionFalse, - Reason: ocv1.ClusterExtensionRevisionReasonReconcileFailure, - Message: err.Error(), - ObservedGeneration: rev.Generation, - }) - return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err) - } - if err := c.removeFinalizer(ctx, rev, clusterExtensionRevisionTeardownFinalizer); err != nil { - meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ - Type: "Available", - Status: metav1.ConditionFalse, - Reason: "ReconcileFailure", - Message: err.Error(), - ObservedGeneration: rev.Generation, - }) - return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err) - } - return ctrl.Result{}, nil + if !rev.DeletionTimestamp.IsZero() || rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { + return c.teardown(ctx, rev, revision) } // @@ -339,6 +295,59 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev return ctrl.Result{}, nil } +func (c *ClusterExtensionRevisionReconciler) teardown(ctx context.Context, rev *ocv1.ClusterExtensionRevision, revision *boxcutter.Revision) (ctrl.Result, error) { + l := log.FromContext(ctx) + + tres, err := c.RevisionEngine.Teardown(ctx, *revision) + if err != nil { + meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Status: metav1.ConditionFalse, + Reason: ocv1.ClusterExtensionRevisionReasonReconcileFailure, + Message: err.Error(), + ObservedGeneration: rev.Generation, + }) + return ctrl.Result{}, fmt.Errorf("revision teardown: %v", err) + } + + l.Info("teardown report", "report", tres.String()) + if !tres.IsComplete() { + // TODO: If it is not complete, it seems like it would be good to update + // the status in some way to tell the user that the teardown is still + // in progress. + return ctrl.Result{}, nil + } + + if err := c.TrackingCache.Free(ctx, rev); err != nil { + meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Status: metav1.ConditionFalse, + Reason: ocv1.ClusterExtensionRevisionReasonReconcileFailure, + Message: err.Error(), + ObservedGeneration: rev.Generation, + }) + return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err) + } + + // Ensure Available condition is set to Unknown before removing the finalizer when archiving + if rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived && + !meta.IsStatusConditionPresentAndEqual(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable, metav1.ConditionUnknown) { + meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Status: metav1.ConditionUnknown, + Reason: ocv1.ClusterExtensionRevisionReasonArchived, + Message: "revision is archived", + ObservedGeneration: rev.Generation, + }) + return ctrl.Result{}, nil + } + + if err := c.removeFinalizer(ctx, rev, clusterExtensionRevisionTeardownFinalizer); err != nil { + return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err) + } + return ctrl.Result{}, nil +} + type Sourcerer interface { Source(handler handler.EventHandler, predicates ...predicate.Predicate) source.Source } diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go index 694bd4d4a..873a6cc74 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go @@ -544,6 +544,40 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_Deletion(t *testing.T) { require.NotContains(t, "olm.operatorframework.io/teardown", rev.Finalizers) }, }, + { + name: "set Available condition to Unknown with reason Archived when archiving revision", + revisionResult: mockRevisionResult{}, + existingObjs: func() []client.Object { + ext := newTestClusterExtension() + rev1 := newTestClusterExtensionRevision(clusterExtensionRevisionName) + rev1.Finalizers = []string{ + "olm.operatorframework.io/teardown", + } + rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) + return []client.Object{rev1, ext} + }, + revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + return func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + return &mockRevisionTeardownResult{ + isComplete: true, + }, nil + } + }, + validate: func(t *testing.T, c client.Client) { + rev := &ocv1.ClusterExtensionRevision{} + err := c.Get(t.Context(), client.ObjectKey{ + Name: clusterExtensionRevisionName, + }, rev) + require.NoError(t, err) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + require.NotNil(t, cond) + require.Equal(t, metav1.ConditionUnknown, cond.Status) + require.Equal(t, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason) + require.Equal(t, "revision is archived", cond.Message) + require.Equal(t, int64(1), cond.ObservedGeneration) + }, + }, { name: "revision is torn down when in archived state and finalizer is removed", revisionResult: mockRevisionResult{}, @@ -554,6 +588,13 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_Deletion(t *testing.T) { "olm.operatorframework.io/teardown", } rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Status: metav1.ConditionUnknown, + Reason: ocv1.ClusterExtensionRevisionReasonArchived, + Message: "revision is archived", + ObservedGeneration: rev1.Generation, + }) require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) return []client.Object{rev1, ext} }, @@ -570,7 +611,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_Deletion(t *testing.T) { Name: clusterExtensionRevisionName, }, rev) require.NoError(t, err) - require.NotContains(t, "olm.operatorframework.io/teardown", rev.Finalizers) + require.NotContains(t, rev.Finalizers, "olm.operatorframework.io/teardown") }, }, { diff --git a/manifests/experimental-e2e.yaml b/manifests/experimental-e2e.yaml index 1bc93321e..d08ffc8a7 100644 --- a/manifests/experimental-e2e.yaml +++ b/manifests/experimental-e2e.yaml @@ -606,7 +606,14 @@ spec: singular: clusterextensionrevision scope: Cluster versions: - - name: v1 + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: description: ClusterExtensionRevision is the Schema for the clusterextensionrevisions diff --git a/manifests/experimental.yaml b/manifests/experimental.yaml index 69128a8b7..fc1dd3dcf 100644 --- a/manifests/experimental.yaml +++ b/manifests/experimental.yaml @@ -571,7 +571,14 @@ spec: singular: clusterextensionrevision scope: Cluster versions: - - name: v1 + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: description: ClusterExtensionRevision is the Schema for the clusterextensionrevisions