Skip to content

🐛 Add teardown on revision archival - #2502

Merged
openshift-merge-bot[bot] merged 1 commit into
operator-framework:mainfrom
perdasilva:fix-cer-archival-teardown
Feb 23, 2026
Merged

🐛 Add teardown on revision archival#2502
openshift-merge-bot[bot] merged 1 commit into
operator-framework:mainfrom
perdasilva:fix-cer-archival-teardown

Conversation

@perdasilva

@perdasilvaperdasilva commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

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.

  • 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 (propagate for controller retry)
  • Rename rev variable to cer for consistency with the type name
  • Add e2e step ClusterExtensionRevision "<name>" phase objects are not found or not owned by the revision: fetches all objects from the CER's phases and asserts each one either does not exist on the cluster or does not list the CER in its ownerReferences
  • Replace the specific configmap/dummy-configmap not-found assertion in the update e2e scenario with the new generic phase-objects step

Test plan

  • Unit tests pass (Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion)
  • New unit test: requeue when archived revision teardown is incomplete
  • New unit test: error propagation when archived revision teardown fails
  • New unit test: error propagation when factory fails to create engine during archived teardown
  • E2e: "Each update creates a new revision" verifies all phase objects from the archived CER are either deleted or no longer owned by the revision

🤖 Generated with Claude Code

Reviewer Checklist

  • API Go Documentation
  • Tests: Unit Tests (and E2E Tests, if appropriate)
  • Comprehensive Commit Messages
  • Links to related GitHub Issue(s)

CopilotAI review requested due to automatic review settings February 11, 2026 13:49
@netlify

netlifyBot commented Feb 11, 2026

Copy link
Copy Markdown

Deploy Preview for olmv1 ready!

NameLink
🔨 Latest commit88f1f38
🔍 Latest deploy loghttps://app.netlify.com/projects/olmv1/deploys/699c80695c114800084d1e63
😎 Deploy Previewhttps://deploy-preview-2502--olmv1.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates ClusterExtensionRevision teardown behavior to actively tear down Boxcutter-managed resources when a revision is archived, and extends test coverage/data to validate resource removal across bundle versions.

Changes:

  • Invoke RevisionEngine.Teardown during teardown for archived revisions, with retry + requeue when teardown is incomplete.
  • Expand deletion/archival reconciliation tests to cover incomplete teardown and teardown failures (including expected requeue behavior).
  • Add a dummy ConfigMap manifest to the v1.0.0 test bundle to validate removal of resources that disappear in later bundle versions.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

FileDescription
internal/operator-controller/controllers/clusterextensionrevision_controller.goCalls Boxcutter teardown for archived revisions and requeues while teardown is incomplete.
internal/operator-controller/controllers/clusterextensionrevision_controller_test.goAdds test cases for archived teardown retry/error paths and asserts reconcile results.
testdata/images/bundles/test-operator/v1.0.0/manifests/dummy.configmap.yamlIntroduces a version-specific manifest used to test removal of resources across upgrades.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecovBot commented Feb 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.20690% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.18%. Comparing base (1ef820f) to head (88f1f38).
⚠️ Report is 3 commits behind head on main.

Files with missing linesPatch %Lines
...controllers/clusterextensionrevision_controller.go86.20%2 Missing and 2 partials ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #2502 +/- ##
==========================================
- Coverage 73.27% 73.18% -0.09% 
==========================================
Files 103 103 Lines 8516 8533 +17 ==========================================
+ Hits 6240 6245 +5 - Misses 1801 1808 +7 - Partials 475 480 +5 
FlagCoverage Δ
e2e45.69% <0.00%> (-0.14%)⬇️
experimental-e2e53.43% <55.17%> (-0.03%)⬇️
unit57.95% <75.86%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 06afd4a to 5ca3f6eCompareFebruary 11, 2026 15:01
@perdasilvaperdasilva changed the title 🐛 Teardown revision on archival🐛 Add teardown on revision archivalFeb 12, 2026
CopilotAI review requested due to automatic review settings February 16, 2026 12:10
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 5ca3f6e to 6e1e134CompareFebruary 16, 2026 12:10

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtest/e2e/features/update.feature Outdated
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 6e1e134 to 7c6037bCompareFebruary 17, 2026 11:33
CopilotAI review requested due to automatic review settings February 17, 2026 14:04
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 7c6037b to b51648bCompareFebruary 17, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@pedjakpedjak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if two revisions are sharing the same resource? how do ensure that we do not delete something accidentally?

setRetryingConditions(rev, err.Error())
return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to move this further down?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends on whether we want to remove revision creation from the deletion path or not. Since we want the garbage collector to handle deletion, it could avoid transient (or maybe even persistent) errors from getting in the way. If so, it might also make sense to move it closer to where revision is first needed

}

func (c *ClusterExtensionRevisionReconciler) archive(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (machinery.RevisionTeardownResult, error) {
revision, _, err := c.toBoxcutterRevision(ctx, cer)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we convert this multiple times, the caller can pass it?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was a suggestion by copilot to remove revision creation from the deletion path

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've restructured things to just create those structures once

if err != nil {
return nil, fmt.Errorf("converting to boxcutter revision: %v", err)
}
revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, cer)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revision engine is also already available at the caller side?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a similar argument as with creating the revision. It's not needed in the deletion path. Maybe we could just separate the deletion and archival paths further. Then we only repeat finalizer removal.
Actually, what do you think about removing the owner ref from archived revisions? They would still carry the owner labels we apply, so they are still attached. But, it gives a cleaner kubectl lineage output XD and might avoid CE reconciliations (although we don't really expect any changes to archived revisions...)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revision engine also has a dependency on the service account, which might be deleted together with the revision. So, it's probably safer to not have revision engine creation on the deletion path.

Comment threadtest/e2e/features/update.feature Outdated
And ClusterExtensionRevision "${NAME}-2" reports Available as True with Reason ProbesSucceeded
And ClusterExtensionRevision "${NAME}-1" is archived
# dummy-config map exists only in v1.0.0 - once the v1.0.0 revision is archived, it should be gone from the cluster
And resource "configmap/dummy-configmap" is eventually not found

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check if other resources are there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move that check to a more general upgrade test?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, there is overlap between the resources. So, some of the v1.0.0 resources would still be present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, there is overlap between the resources. So, some of the v1.0.0 resources would still be present.

right, should have steps that checks for that explicitly in this test?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude and I updated things to check that the revision-1 resources either don't exist or are no longer in the object's ownerrefs

@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from b51648b to fdcc6d9CompareFebruary 18, 2026 13:45
CopilotAI review requested due to automatic review settings February 18, 2026 14:31
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from fdcc6d9 to 9105af6CompareFebruary 18, 2026 14:31

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is really needed in this PR to rename arg from rev to cer? It makes the diff bigger than it needs to be.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it reads better, especially when you have two parallel revision objects. It makes it easier to know that you're operating over the CER vs the Boxcutter revision. It might even be worth going forward and renaming revision to boxcutterRevision or bcRev, or something like that.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it would be helpful, I could revert it for this PR and do a follow up with just that change

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it would be helpful, I could revert it for this PR and do a follow up with just that change

Yeah, I would keep the PR focused on the teardown, the renamings could be another focused PR.

Comment threadtest/e2e/steps/hooks.go Outdated
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 9105af6 to b306567CompareFebruary 19, 2026 09:31
CopilotAI review requested due to automatic review settings February 19, 2026 13:06
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from b306567 to d9f672aCompareFebruary 19, 2026 13:06

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

internal/operator-controller/controllers/clusterextensionrevision_controller_test.go:658

  • In the "teardown finalizer is removed" test case, the revision is neither deleting (DeletionTimestamp is unset) nor archived (Spec.LifecycleState stays Active), but the validation expects the teardown finalizer to be removed. With the new reconciler flow, finalizer removal happens in delete() (deletion) or archive() (archival), so this case will not exercise either path. Set a DeletionTimestamp (to test deletion) or mark the revision Archived (to test archival), or adjust the expectation accordingly.
 name: "teardown finalizer is removed",
revisionResult: mockRevisionResult{},
existingObjs: func() []client.Object {
ext := newTestClusterExtension()
rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme)
rev1.Finalizers = []string{
"olm.operatorframework.io/teardown",
}
return []client.Object{ext, rev1}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtest/e2e/steps/steps.go
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 57250df to 2a93572CompareFebruary 23, 2026 13:36
}

func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {
func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it would be helpful, I could revert it for this PR and do a follow up with just that change

Yeah, I would keep the PR focused on the teardown, the renamings could be another focused PR.

Comment on lines +296 to +297
setRetryingConditions(cer, fmt.Sprintf("error archiving: %v", err))
return ctrl.Result{}, fmt.Errorf("error tearing down revision: %v", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason that pass fmt.Sprintf("error archiving: %v", err) to setRetryingConditions and then return back to the caller fmt.Errorf("error tearing down revision: %v", err). Can we have the same error?

return ctrl.Result{}, fmt.Errorf("error tearing down revision: %v", err)
}
if tdres != nil && !tdres.IsComplete() {
setRetryingConditions(cer, "tearing down revision")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we be here a bit more specific and share with user what is actually going on? tearing down revision can mean many things. Why do we requeue here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the archival process is clearing the tracking cache. If the tear down process hasn't finished completely yet, we should retry (but given that we have cleared the cache, deletions won't trigger reconciliation)

@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 2a93572 to b0af690CompareFebruary 23, 2026 15:09
CopilotAI review requested due to automatic review settings February 23, 2026 15:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

internal/operator-controller/controllers/clusterextensionrevision_controller_test.go:659

  • In the "teardown finalizer is removed" test case, the revision is neither marked for deletion (DeletionTimestamp) nor archived, so the reconciler will take the active reconcile path and will not remove the teardown finalizer. Either set a DeletionTimestamp on rev1 (to exercise delete()) or adjust the test name/expectations to match the active path.
 name: "teardown finalizer is removed",
revisionResult: mockRevisionResult{},
existingObjs: func() []client.Object {
ext := newTestClusterExtension()
rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme)
rev1.Finalizers = []string{
"olm.operatorframework.io/teardown",
}
return []client.Object{ext, rev1}
},

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtest/e2e/steps/steps.go

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtest/e2e/steps/steps.go
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) to
validate teardown on archival in e2e tests
- Add e2e step ClusterExtensionRevision phase objects are not found or
not owned by the revision: fetches all objects from the CER's phases
and asserts each one either does not exist or does not list the CER
in its ownerReferences
- Change extensionObjects in scenarioContext from []client.Object to
[][]client.Object to track objects per revision rollout; add
GetClusterExtensionObjectsForRevision helper
- Replace specific configmap-not-found assertion in the update e2e
scenario with the new generic phase-objects step
Signed-off-by: Per Goncalves da Silva <pegoncal@redhat.com>
Signed-off-by: Per G. da Silva <pegoncal@redhat.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Per G. da Silva <pegoncal@redhat.com>
@perdasilva
perdasilvaforce-pushed the fix-cer-archival-teardown branch from 110d036 to 88f1f38CompareFebruary 23, 2026 16:29

@camilamacedo86camilamacedo86 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems great now 🎉

@openshift-ciopenshift-ciBot added the lgtm Indicates that a PR is ready to be merged. label Feb 23, 2026
@openshift-ci

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: camilamacedo86

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ciopenshift-ciBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 23, 2026
@camilamacedo86

Copy link
Copy Markdown
Contributor

/lgtm cancel

To let other person just LGTM and we win the second review

@openshift-ciopenshift-ciBot removed the lgtm Indicates that a PR is ready to be merged. label Feb 23, 2026

@pedjakpedjak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@openshift-ciopenshift-ciBot added the lgtm Indicates that a PR is ready to be merged. label Feb 23, 2026
@openshift-merge-bot
openshift-merge-botBot merged commit 06da96a into operator-framework:mainFeb 23, 2026
28 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@perdasilva@camilamacedo86@pedjak