Skip to content

Commit fdcc6d9

Browse files
author
Per Goncalves da Silva
committed
Call RevisionEngine.Teardown when CER is archived
When a ClusterExtensionRevision transitions to the Archived lifecycle state, invoke the revision engine's Teardown method to clean up managed resources that are no longer part of the active revision. This ensures resources removed between bundle versions (e.g. a ConfigMap present in v1.0.0 but absent in v1.2.0) are deleted from the cluster. Changes: - Split teardown() into delete() (CER deletion) and archive() (CER archival); only archive() calls revisionEngine.Teardown() - Move RevisionEngine creation and watch establishment before the archive check so they are available for both archive and reconcile paths - Handle incomplete teardown (requeue after 5s) and teardown errors (return error for controller retry) - Rename rev variable to cer for consistency with the type name - Update unit tests: rename to ArchivalAndDeletion, add test cases for incomplete teardown requeue, teardown error propagation, and factory creation error during archived teardown - Add dummy-configmap to v1.0.0 test bundle (absent in v1.2.0) and assert it is removed in the "Each update creates a new revision" e2e test Signed-off-by: Per Goncalves da Silva <pegoncal@redhat.com> Signed-off-by: Per G. da Silva <pegoncal@redhat.com>
1 parent 130c987 commit fdcc6d9

File tree

4 files changed

+136
-72
lines changed

4 files changed

+136
-72
lines changed

internal/operator-controller/controllers/clusterextensionrevision_controller.go

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -131,61 +131,65 @@ func checkForUnexpectedClusterExtensionRevisionFieldChange(a, b ocv1.ClusterExte
131131
return !equality.Semantic.DeepEqual(a.Spec, b.Spec)
132132
}
133133

134-
func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
134+
func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
135135
l := log.FromContext(ctx)
136136

137-
revision, opts, err := c.toBoxcutterRevision(ctx, rev)
138-
if err != nil {
139-
setRetryingConditions(rev, err.Error())
140-
return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err)
137+
if !cer.DeletionTimestamp.IsZero() {
138+
return c.delete(ctx, cer)
141139
}
142140

143-
if !rev.DeletionTimestamp.IsZero() || rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived {
144-
return c.teardown(ctx, rev)
141+
if err := c.ensureFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil {
142+
return ctrl.Result{}, fmt.Errorf("error ensuring teardown finalizer: %v", err)
145143
}
146144

147-
revVersion := rev.GetAnnotations()[labels.BundleVersionKey]
148-
//
149-
// Reconcile
150-
//
151-
if err := c.ensureFinalizer(ctx, rev, clusterExtensionRevisionTeardownFinalizer); err != nil {
152-
return ctrl.Result{}, fmt.Errorf("error ensuring teardown finalizer: %v", err)
145+
revision, opts, err := c.toBoxcutterRevision(ctx, cer)
146+
if err != nil {
147+
setRetryingConditions(cer, err.Error())
148+
return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err)
153149
}
154150

155-
if err := c.establishWatch(ctx, rev, revision); err != nil {
151+
if err := c.establishWatch(ctx, cer, revision); err != nil {
156152
werr := fmt.Errorf("establish watch: %v", err)
157-
setRetryingConditions(rev, werr.Error())
153+
setRetryingConditions(cer, werr.Error())
158154
return ctrl.Result{}, werr
159155
}
160156

161-
revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, rev)
157+
revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, cer)
162158
if err != nil {
163-
setRetryingConditions(rev, err.Error())
159+
setRetryingConditions(cer, err.Error())
164160
return ctrl.Result{}, fmt.Errorf("failed to create revision engine: %v", err)
165161
}
166162

163+
if cer.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived {
164+
if err := c.TrackingCache.Free(ctx, cer); err != nil {
165+
markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error())
166+
return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err)
167+
}
168+
return c.archive(ctx, revisionEngine, cer, revision)
169+
}
170+
167171
rres, err := revisionEngine.Reconcile(ctx, *revision, opts...)
168172
if err != nil {
169173
if rres != nil {
170174
// Log detailed reconcile reports only in debug mode (V(1)) to reduce verbosity.
171175
l.V(1).Info("reconcile report", "report", rres.String())
172176
}
173-
setRetryingConditions(rev, err.Error())
177+
setRetryingConditions(cer, err.Error())
174178
return ctrl.Result{}, fmt.Errorf("revision reconcile: %v", err)
175179
}
176180

177181
// Retry failing preflight checks with a flat 10s retry.
178182
// TODO: report status, backoff?
179183
if verr := rres.GetValidationError(); verr != nil {
180184
l.Error(fmt.Errorf("%w", verr), "preflight validation failed, retrying after 10s")
181-
setRetryingConditions(rev, fmt.Sprintf("revision validation error: %s", verr))
185+
setRetryingConditions(cer, fmt.Sprintf("revision validation error: %s", verr))
182186
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
183187
}
184188

185189
for i, pres := range rres.GetPhases() {
186190
if verr := pres.GetValidationError(); verr != nil {
187191
l.Error(fmt.Errorf("%w", verr), "phase preflight validation failed, retrying after 10s", "phase", i)
188-
setRetryingConditions(rev, fmt.Sprintf("phase %d validation error: %s", i, verr))
192+
setRetryingConditions(cer, fmt.Sprintf("phase %d validation error: %s", i, verr))
189193
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
190194
}
191195

@@ -198,21 +202,22 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
198202

199203
if len(collidingObjs) > 0 {
200204
l.Error(fmt.Errorf("object collision detected"), "object collision, retrying after 10s", "phase", i, "collisions", collidingObjs)
201-
setRetryingConditions(rev, fmt.Sprintf("revision object collisions in phase %d\n%s", i, strings.Join(collidingObjs, "\n\n")))
205+
setRetryingConditions(cer, fmt.Sprintf("revision object collisions in phase %d\n%s", i, strings.Join(collidingObjs, "\n\n")))
202206
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
203207
}
204208
}
205209

210+
revVersion := cer.GetAnnotations()[labels.BundleVersionKey]
206211
if !rres.InTransition() {
207-
markAsProgressing(rev, ocv1.ReasonSucceeded, fmt.Sprintf("Revision %s has rolled out.", revVersion))
212+
markAsProgressing(cer, ocv1.ReasonSucceeded, fmt.Sprintf("Revision %s has rolled out.", revVersion))
208213
} else {
209-
markAsProgressing(rev, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion))
214+
markAsProgressing(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion))
210215
}
211216

212217
//nolint:nestif
213218
if rres.IsComplete() {
214219
// Archive previous revisions
215-
previous, err := c.listPreviousRevisions(ctx, rev)
220+
previous, err := c.listPreviousRevisions(ctx, cer)
216221
if err != nil {
217222
return ctrl.Result{}, fmt.Errorf("listing previous revisions: %v", err)
218223
}
@@ -226,17 +231,17 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
226231
}
227232
}
228233

229-
markAsAvailable(rev, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, "Objects are available and pass all probes.")
234+
markAsAvailable(cer, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, "Objects are available and pass all probes.")
230235

231236
// We'll probably only want to remove this once we are done updating the ClusterExtension conditions
232237
// as its one of the interfaces between the revision and the extension. If we still have the Succeeded for now
233238
// that's fine.
234-
meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{
239+
meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{
235240
Type: ocv1.ClusterExtensionRevisionTypeSucceeded,
236241
Status: metav1.ConditionTrue,
237242
Reason: ocv1.ReasonSucceeded,
238243
Message: "Revision succeeded rolling out.",
239-
ObservedGeneration: rev.Generation,
244+
ObservedGeneration: cer.Generation,
240245
})
241246
} else {
242247
var probeFailureMsgs []string
@@ -266,27 +271,40 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
266271
}
267272

268273
if len(probeFailureMsgs) > 0 {
269-
markAsUnavailable(rev, ocv1.ClusterExtensionRevisionReasonProbeFailure, strings.Join(probeFailureMsgs, "\n"))
274+
markAsUnavailable(cer, ocv1.ClusterExtensionRevisionReasonProbeFailure, strings.Join(probeFailureMsgs, "\n"))
270275
} else {
271-
markAsUnavailable(rev, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion))
276+
markAsUnavailable(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion))
272277
}
273278
}
274279

275280
return ctrl.Result{}, nil
276281
}
277282

278-
func (c *ClusterExtensionRevisionReconciler) teardown(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
279-
if err := c.TrackingCache.Free(ctx, rev); err != nil {
280-
markAsAvailableUnknown(rev, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error())
283+
func (c *ClusterExtensionRevisionReconciler) delete(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
284+
if err := c.TrackingCache.Free(ctx, cer); err != nil {
281285
return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err)
282286
}
287+
if err := c.removeFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil {
288+
return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err)
289+
}
290+
return ctrl.Result{}, nil
291+
}
283292

293+
func (c *ClusterExtensionRevisionReconciler) archive(ctx context.Context, revisionEngine RevisionEngine, cer *ocv1.ClusterExtensionRevision, revision *boxcutter.Revision) (ctrl.Result, error) {
294+
tdres, err := revisionEngine.Teardown(ctx, *revision)
295+
if err != nil {
296+
setRetryingConditions(cer, fmt.Sprintf("error archiving: %v", err))
297+
return ctrl.Result{}, fmt.Errorf("error tearing down revision: %v", err)
298+
}
299+
if tdres != nil && !tdres.IsComplete() {
300+
setRetryingConditions(cer, "tearing down revision")
301+
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
302+
}
284303
// Ensure conditions are set before removing the finalizer when archiving
285-
if rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived && markAsArchived(rev) {
304+
if markAsArchived(cer) {
286305
return ctrl.Result{}, nil
287306
}
288-
289-
if err := c.removeFinalizer(ctx, rev, clusterExtensionRevisionTeardownFinalizer); err != nil {
307+
if err := c.removeFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil {
290308
return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err)
291309
}
292310
return ctrl.Result{}, nil

0 commit comments

Comments
 (0)