Repository navigation
fix(api): let the garbage collector finalize Applications - #4610
Aleksei Sviridkin (lexfrei) merged 1 commit into
Conversation
An Application and its HelmRelease share one UID, so the garbage collector keeps a single graph node for both and may finalize it through the Application endpoint. The API never returned finalizers on an Application, and every update restored the HelmRelease finalizers from the live object. When the collector came to drop foregroundDeletion or orphan through the Application, removeFinalizer found no such finalizer and returned as if it were already gone. The HelmRelease then kept foregroundDeletion after Helm had uninstalled the release, and the deletion never completed. The Application now carries the two garbage collection finalizers, and an update that adds or removes one of them applies the same change to the HelmRelease. Controller finalizers such as finalizers.fluxcd.io stay owned by the live object, so helm uninstall still runs before the release goes away. The deletion timestamp and grace period are copied from the live object, which keeps an update of a terminating release valid. Assisted-by: LLM Signed-off-by: Yan Bondarenko <[email protected]>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: cozystack/cozystack/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughApplication conversion now exposes and writes only Kubernetes garbage-collection finalizers. Update assembly merges requested finalizer changes with live HelmRelease state, including on conflict retries, and preserves the live deletion timestamp and grace period. Tests cover deletion ordering, concurrent changes, controller protection, and cached-object immutability. ChangesApplication garbage-collection finalizers
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to No actionable merge-blocking issue is established. The change appears mergeable after normal checks; reported validation uses a fake client rather than a live garbage collector. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change limits writable cleanup metadata to two Kubernetes finalizers and preserves Helm cleanup protection during concurrent updates. No introduced security bypass was established. Authorization enforcement and real-controller behavior remain incompletely verified. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
europrinter (@yankawai) LGTM.
Business context: an Application deleted with foreground or orphan propagation never finishes deleting, because the garbage collector can finalize the shared UID through the Application endpoint, where the finalizer is not visible.
The bug is real on current main. A foreground delete of an Application puts foregroundDeletion on the HelmRelease, since Delete passes the caller's options through. The collector keys its graph by UID, and both objects share one, so the node can end up with the Application identity. Then removeFinalizer reads the Application, sees no finalizer and returns without a patch, and nothing else ever removes it from the HelmRelease.
The fix is at the right place and I don't see a smaller one. Only the two collector finalizers are exposed, and a change to them is applied as a delta against the list the update started from. So finalizers.fluxcd.io stays owned by the live object, and the conflict retry keeps concurrent writes.
What I ran on a trial merge with main (merges clean):
go test -race ./pkg/registry/apps/application/passes.- With
rest.gofrom main, all five new tests fail, as the description says. - Replacing the delta with the absolute list from the Application fails
TestApplicationGarbageCollectionConflict. Dropping the deletion timestamp copy failsTestApplicationGarbageCollectionandTestApplicationGarbageCollectionWithInPlaceUpdate.
Sign-off matches the author email, the only trailer is Assisted-by: LLM, and the description matches the diff, including the note that it was not run against a live garbage collector yet.
…pplications (#4683) ## What this PR does Backport of #4610 to `release-1.6`. `Update` on this branch has no `applyLiveMetadata`, so the finalizer merge and the copy of the deletion metadata are done inline, in `Update` and in its conflict retry. The tests are the same as on main. An Application and its HelmRelease share one UID, so the garbage collector keeps one graph node for both and may finalize it through the Application endpoint. The API never returned finalizers on an Application, so when the collector dropped `foregroundDeletion` or `orphan` through it, the HelmRelease kept `foregroundDeletion` after Helm had uninstalled the release and the deletion never completed. The Application now carries the two garbage collection finalizers, and an update that adds or removes one of them applies the same change to the HelmRelease. Controller finalizers such as `finalizers.fluxcd.io` stay owned by the live object. ### Validation - `go test ./pkg/registry/apps/application/...` passes on this branch. - Making the finalizer merge return the live list turns four tests red, the conflict-retry test among them. - No API types change, so code generation is not affected. ### Screenshots Not a UI change. ### Downstream repositories Walked the trigger map against the diff. It changes the Application REST handler and its test, with no chart, values, schema, CRD or `hack/` file, so no companion repository needs an update. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: ### Release note ```release-note fix(api): an application deleted with foreground or orphan propagation no longer leaves its HelmRelease stuck with the foregroundDeletion or orphan finalizer ```
What this PR does
An application deleted with foreground or orphan propagation (
kubectl delete --cascade=foreground, or any client that sends thatpropagationPolicy) could stay in deletion forever. Helm uninstalled the release and helm-controller removedfinalizers.fluxcd.io, but the HelmRelease keptforegroundDeletioninmetadata.finalizers, so the HelmRelease and its Application stayed terminating. We hit this on v1.6.4 with a Bucket and an OpenBAO application.An Application and its HelmRelease share one UID, so the garbage collector keeps a single graph node for both and may finalize it through the Application endpoint. The API never returned finalizers on an Application, and every update restored the HelmRelease finalizers from the live object.
removeFinalizerreads the owner, does not find the finalizer there and returns as if it had already been removed, so nothing ever took it off the HelmRelease.This PR:
foregroundDeletionandorphaninmetadata.finalizersof an Application, and only those two;finalizers.fluxcd.ioowned by the live object, sohelm uninstallstill runs before the release goes away;rest_garbage_collection_test.gocovers both finalizers with Helm finishing first and with the garbage collector finishing first, concurrent finalizer changes during the conflict retry, controller finalizers surviving an update, the cached HelmRelease not being mutated by the conversion, and anUpdatedObjectthat edits the object in place. All five tests fail on the currentmainwithout this change.go test -race ./pkg/registry/...passes. The tests use the controller-runtime fake client, so this has not been run against a live garbage collector with a patched cozystack-api yet.The same bug is in release-1.6. There the Update path has no finalizer carry-over at all, because #2914 is not backported yet, and its automatic backport #4577 currently carries conflict markers. A 1.6 backport of this fix should go on top of a working backport of #2914.
Screenshots
Not a UI change.
Downstream repositories
Walked the trigger map against the diff: it only changes how cozystack-api handles
metadata.finalizersof Applications inpkg/registry/apps/application. No package, values, schema, CRD,hack/file or convention is touched.Release note
Summary by CodeRabbit