Repository navigation
fix(apps): let the cleanup hooks watch the objects they delete - #4135
Aleksei Sviridkin (lexfrei) merged 2 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughChangesCleanup hooks
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Low Suggested reviewers: Merge Risk: ⚪ Minimal · up to The cleanup hooks now have bounded deletion waits and the required watch permissions, with no actionable merge risk identified. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
kubectl delete waits for the object to disappear unless told otherwise, and that wait is a watch. The post-delete hook Roles of harbor, clickhouse, mariadb and qdrant granted get, list and delete only, so every uninstall logged a forbidden watch, the wait was skipped and the hook reported success before the PVCs were actually gone. The rules now carry watch for every resource the hooks delete, including the named Secrets the mariadb hook removes. Signed-off-by: Yan Bondarenko <[email protected]>
1106b41 to
711fcc1
Compare
scooby87
left a comment
There was a problem hiding this comment.
NOT LGTM — one MAJOR blocker (independent cozy-review pass).
The diagnosis and the RBAC shape are right: kubectl delete defaults to --wait=true and the wait needs watch, so the missing verb made the hook skip the wait and report "complete" while PVCs were still terminating. watch is added to all Roles and the tests are green (clickhouse 3/3, mariadb 4/4).
[MAJOR] Enabling the real wait makes the post-delete hooks an unbounded teardown blocker with no limit added. clickhouse cleanup-pvc.yaml:54, harbor cleanup.yaml:106, mariadb cleanup-pvc.yaml:61, qdrant cleanup-pvc.yaml:107 all run kubectl delete pvc with the default --wait=true and no --timeout, and none of the Jobs set activeDeadlineSeconds. Before this diff the missing verb made kubectl skip the wait, so the hang was unreachable; the diff makes the wait real and the hazard attributable to this change. A stuck pvc-protection finalizer (node NotReady, stuck detach — a real LINSTOR/DRBD situation) blocks the delete forever, the Job hangs to the hook timeout, and the HelmRelease gets stuck uninstalling. Fix: a bounded wait — --timeout=<N> on the delete and/or activeDeadlineSeconds (60-300s) on the Job. Note --wait=false alone would reintroduce the original "success while terminating" bug.
[MINOR] The watch added to the mariadb resourceNames-scoped secrets rule (cleanup-pvc.yaml:135) is a dead grant: those secrets have no finalizer (deleted synchronously, watch phase never reached) and a resourceNames rule does not authorize a collection watch anyway. Remove it.
With watch granted, kubectl delete waits for the claims to be gone, and its default for that wait is a week. A claim held by a pod that will not stop, or a stuck detach, would hold the post-delete hook until helm-controller's 300s uninstall budget ran out and leave the HelmRelease stuck uninstalling. The PVC deletes in the clickhouse, harbor, mariadb and qdrant hooks now wait 120s at most and report the claims still terminating by name, and each Job carries activeDeadlineSeconds 270 with a 10s termination grace period, the numbers packages/apps/tenant/templates/cleanup-job.yaml documents against that budget, so a kubectl that never returns cannot hold the hook either. The mariadb Role no longer grants watch on the named Secrets: they carry no finalizer, so the delete never reaches the watch, and a resourceNames rule does not authorize a collection watch anyway. Signed-off-by: Yan Bondarenko <[email protected]> Assisted-by: LLM
|
Bounded in f886f26: the PVC deletes now run with |
scooby87
left a comment
There was a problem hiding this comment.
Verdict
LGTM with non-blocking notes
This is a re-review. My own prior pass (2026-09-17, on 711fcc168) was NOT LGTM with one MAJOR blocker: enabling the real wait (granting watch) turned the PVC deletes into an unbounded teardown blocker with no limit added, so a stuck pvc-protection finalizer would hang the hook to the timeout and leave the HelmRelease stuck uninstalling. The new commit f886f26ad closes it exactly as requested — --wait=true --timeout=120s on every PVC delete plus activeDeadlineSeconds: 270 and terminationGracePeriodSeconds: 10 on every Job, the same bounded-wait shape packages/apps/tenant/templates/cleanup-job.yaml already documents against the helm-controller 300s uninstall budget. My prior MINOR (a dead watch grant on the mariadb resourceNames-scoped Secrets rule) is also gone: that rule is back to ["get","delete"]. The reversal lands on changed code, not on the revision I blocked, so the flip is earned.
Verified hermetically on f886f26ad: helm unittest green (clickhouse 24/24, harbor 21/21, mariadb 111/111, qdrant 104/104) and the behavioural suite hack/app-cleanup-hooks_test.bats 11/11. The new assertions are not inert — mutating clickhouse (dropping --wait=true --timeout=120s and changing 270→260) turns the suite red on both the activeDeadlineSeconds equal and the command matchRegex, and restoring goes green.
The diagnosis and the RBAC shape are correct: kubectl delete defaults to --wait=true, that wait is a watch, and the missing verb made the hook skip the wait and report "complete" while claims were still terminating. watch is added only to the collection-scoped rules that sit behind a delete (PVCs in all four; certificates/challenges/solver ingresses in harbor), and the mariadb named-Secret rule correctly keeps get/delete only — least privilege is preserved and everything stays namespace-scoped, so there is no tenant-isolation concern.
Findings
[MINOR] clickhouse & mariadb cleanup Jobs keep the default backoffLimit (6) — a stuck PVC now surfaces as DeadlineExceeded, not the hook's own error. packages/apps/clickhouse/templates/hooks/cleanup-pvc.yaml:15, packages/apps/mariadb/templates/hooks/cleanup-pvc.yaml:12. harbor and qdrant deliberately set backoffLimit: 1 with a comment explaining that six retries would outlast the hook timeout and make "Helm report that timeout instead of the hook's own error." This PR is what makes that failure mode reachable for the other two charts: with the new 120s wait, a genuinely stuck claim burns ~120s per attempt, and under the default backoffLimit: 6 the Job hits activeDeadlineSeconds: 270 mid-second-attempt, so Helm sees DeadlineExceeded while the hook's named-PVC diagnostic (the whole point of this change) is buried in an earlier pod's log rather than being the proximate error. The 270s backstop still prevents the hang, so this is not a blocker — but adding backoffLimit: 1 to these two would make all four charts fail the same way and preserve the signal the PR is built to surface.
[NIT] harbor's Certificate / Challenge / solver-Ingress deletes now perform a real wait but carry no --timeout. packages/apps/harbor/templates/hooks/cleanup.yaml:126,153,163. With watch granted on these kinds, kubectl delete … --ignore-not-found (no --timeout) now waits with kubectl's default, which the tenant precedent documents as "a week." Only the PVC delete is explicitly bounded; these three rely solely on the Job's activeDeadlineSeconds. cert-manager objects rarely block on their finalizers and the 270s deadline caps the pathological case, so this is defensive completeness only — but bounding them (or a one-line note that the deadline is the intended bound for them) would match the PR's own "bound the wait" thesis.
Found outside this diff (non-blocking)
- clickhouse and qdrant pin the cleanup image as
docker.io/clastix/kubectl:v1.32(mutable tag, not routed throughcozy-lib.image), whereas harbor and mariadb use the digest-pinned, registry-routedcozy-lib.imageform. Mirrored / air-gapped installs would fail to pull the clickhouse/qdrant cleanup image at uninstall (then hit the 270s deadline). Pre-existing, untouched by this PR.
Static-checker false positives (rejected)
- 4×
cleanup-hook-unbounded [MAJOR](clickhouse/harbor/mariadb/qdrant): the heuristic wants--wait=falseon everykubectl delete. The PR deliberately uses a bounded--wait=true --timeout=120sbacked byactiveDeadlineSeconds: 270— which is both the only way to fix the actual bug (reporting complete-while-terminating) and the exact patternpackages/apps/tenant/templates/cleanup-job.yamlalready establishes. The teardown-hang the invariant warns about is prevented because270 + 10s grace < 300shelm budget. Rejected. - 1×
hook-succeeded-on-job [MINOR](harbor): the message claimshook-succeededGCs a failed Job immediately. It does not —hook-succeededdeletes only on success; failed Jobs are retained (onlyhook-failedwould GC on failure, and harbor does not set it). The harbor script is explicitly designed around keeping failed Jobs for their logs. Also pre-existing (not in the diff). Rejected.
Phase 5b (upgrade + fresh install)
These are post-delete hook templates — they render only at uninstall, so a fresh install and an in-place upgrade of an existing release are both unaffected until the operator uninstalls, at which point the new hook RBAC + bounded Job render fresh. No default flip, no RBAC contraction on running workloads, no cold-start dependency. Nothing to migrate.
|
Created backport PR for
Please cherry-pick the changes locally and resolve any conflicts. git fetch origin backport-4135-to-release-1.6
git worktree add --checkout .worktree/backport-4135-to-release-1.6 backport-4135-to-release-1.6
cd .worktree/backport-4135-to-release-1.6
git reset --hard HEAD^
git cherry-pick -x 711fcc168c5976466b66ab9d3ac029891e5327e6 f886f26ad43d305849aa78e183d008f97b4bb82b
git push --force-with-lease |
What this PR does
Fixes #4085.
kubectl deletewaits for the object to disappear unless--wait=falseis given, and that wait is a watch on the object. The post-delete hook Roles ofharbor,clickhouse,mariadbandqdrantgrantget,listanddeleteonly, so every uninstall logsthe wait is skipped, and the hook prints
PVC cleanup completewhile the claims are still terminating.Every collection-scoped rule behind a delete in these four hooks now also carries
watch:persistentvolumeclaimsin all four;certificates,challengesand the solveringressesin harbor. The four named Secrets the mariadb hook removes keepgetanddeleteonly: they carry no finalizer, so the delete never reaches the watch, and aresourceNamesrule does not authorize a collection watch anyway.A real wait needs a bound. kubectl's default is a week, and a claim held by a pod that will not stop, or a stuck detach, would hold the hook until helm-controller's 300s uninstall budget ran out and leave the HelmRelease stuck uninstalling. The PVC deletes now run with
--wait=true --timeout=120s, so the expiry is reported by the script with the claims still terminating named, and each Job carriesactiveDeadlineSeconds: 270withterminationGracePeriodSeconds: 10, the numberspackages/apps/tenant/templates/cleanup-job.yamldocuments against that budget, as the backstop for a kubectl that never returns.--wait=falsewas not an option: it is exactly the report-complete-while-terminating behaviour this PR removes.The chart unit tests are updated and pass: harbor 21/21, clickhouse 24/24, mariadb 111/111, qdrant 104/104. The behavioural suite
hack/app-cleanup-hooks_test.batspasses 11/11 against the changed scripts.The second observation in #4085, the harbor hook pod occasionally starting after its ServiceAccount is gone, is a hook-weight question and is not touched here.
Downstream repositories
Hook templates and their tests only: no values, schema, README or defaults change.
Release note
Summary by CodeRabbit
Enhancements
Tests