Uh oh!
There was an error while loading. Please reload this page.
fix(#554): auto-recover from a pending-* helm wedge in installer and auto-upgrade - #649
Conversation
…auto-upgrade A helm process killed mid-operation (Ctrl-C, OOM, host reboot, laptop sleep) leaves the release in a pending-install/pending-upgrade state. The next `helm upgrade --install` then fails with "another operation is in progress" — exit 1, not 124 — so the existing unwedge hint (guarded by `-eq 124`) never fires and nothing auto-recovers. In the auto-upgrade cronjob this silently kills security-fix delivery: every hourly tick fails forever until a human runs `helm rollback`. Installer (scripts/lib/install-client-helm.sh): - add _recover_pending_helm_release: read `helm status` (jq-free) and `helm rollback` a pending-upgrade/pending-rollback, or `helm uninstall` a pending-install/uninstalling release, before the helm op runs. - wire it into both the normal install and the adopt-reconcile paths; fail closed if the wedge can't be cleared rather than marching into it. - surface the manual unwedge remedy on exit 1 too, not only the 124 timeout; add --cleanup-on-fail to both upgrades. - the uninstall branch is data-safe: the chart renders helm.sh/resource-policy: keep on every PVC and helm reads it from the stored manifest, so PVCs survive (docs/MIGRATIONS.md). Auto-upgrade cronjob (client/templates/auto-upgrade-cronjob.yaml): - roll back a pending-upgrade/pending-rollback wedge before retrying. - switch --wait to --atomic --cleanup-on-fail so a graceful failure self-reverts (the pre-flight rollback covers the abrupt-kill case --atomic cannot). Tests: 10 new installer bats cases (helper units, integration wedge-recovery, fail-closed, exit-1 hint) and 6 new cronjob unittest assertions. Chart bumped 1.9.28 -> 1.9.29 so the fix publishes; manifest.sha256 regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ad (Bugbot #649) Bugbot review of the pending-* wedge recovery found three issues: - Adopt recovery skipped pending releases (High): _reconcile_adopted_client discovered the release with `helm list -A`, which on Helm 3 shows only deployed releases unless states are named. A release wedged in pending-* was invisible, so adopt fell through to a password prompt it can't satisfy. - One-client guard blind to a wedge (High): detect_installed_client enumerated with the same bare `helm list -A`, so a foreign client wedged in pending-* was unseen — a re-run with a different clientId could overwrite it once recovery cleared the wedge (the wedge itself used to block that overwrite). - Recovery read unbounded (Medium): _recover_pending_helm_release ran an unbounded `helm status`, so a wedged/unreachable API could hang a headless run before the bounded upgrade. Fixes: - both enumeration sites now pass `--deployed --pending --failed`, so a wedged or failed client stays visible to the adopt discovery and the one-client guard. - bound the `helm status` READ with _bounded (the installer's timeout(1) probe wrapper); it gates the rest, so a timeout yields empty status -> no-op -> the bounded upgrade surfaces the API failure. The mutating rollback/uninstall are deliberately NOT wrapped in a kill-based bound (SIGKILLing them midway would recreate the wedge); uninstall stays bounded by helm's own --wait --timeout. Tests: +1 guard regression (a DIFFERENT client wedged in pending-* is still seen and blocked, asserting --pending is enumerated); the recovery unit + integration tests override _bounded to reach the helm mock. manifest.sha256 regenerated. 114 installer bats pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
…econcile path (Bugbot #649) Bugbot: making pending-* releases visible to the adopt discovery (the previous commit) newly exposed a credential-loss path. On a re-run after a killed FIRST install, _reconcile_adopted_client now finds the pending-install release, _recover_pending_helm_release uninstalls it (a never-deployed rev can't be rolled back), then the reconcile runs `helm upgrade --reuse-values` (no --install) -> release-not-found -> error. The adopted credential lived only in that release (the account password is write-only on the backend), so it's gone. Fix: add a no-destroy mode to _recover_pending_helm_release. The reconcile/adopt path passes it: rollback (non-destructive, keeps --reuse-values valid) is still performed, but the destructive uninstall branch is REFUSED and returns non-zero, so the caller fails closed with a manual remedy instead of silently destroying the sole copy of the credential. The normal install path keeps full mode — it writes a fresh values file with the just-verified credential, so uninstalling a pending-install there loses nothing. Tests: +3 no-destroy unit cases (pending-install/uninstalling refused without an uninstall; pending-upgrade still rolls back). manifest regenerated. 117 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
#649) detect_installed_client now enumerates pending releases, so assess sees a pending-upgrade release whose prior revision is still Ready and would classify the machine healthy — handing off before install_client_helm runs recovery, so the wedge stays dead. Add _assess_release_pending (bounded, jq-free) and degrade to the normal flow on a wedge. +2 assess tests; existing classify tests mock the new probe. manifest regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
… (Bugbot #649) The installer pins Helm v4.2.3, whose `helm list` default lists ALL statuses; Helm 3 defaults to deployed-only. Naming --deployed --failed --pending made the listing version-independent but dropped `uninstalling` — a state _recover_pending_helm_release already handles — so on Helm 4 a foreign client stuck mid-uninstall became invisible to the one-client guard, re-opening the overwrite fail-open. Add --uninstalling to both enumeration sites and to assess's wedge probe, so the full deployed/failed/pending-*/uninstalling set is named everywhere regardless of the helm default. manifest regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ard, real adopt remedy, cronjob uninstalling (Bugbot #649) One pass instead of round-by-round. Fixes the two open Bugbot findings plus two issues an adversarial self-audit surfaced first: - assess `_assess_release_pending` failed OPEN: a helm error/timeout read as "no wedge" and let the machine fast-path to healthy, skipping recovery. Now captures the probe's exit code and degrades on any error — matching the module's "never a false healthy" contract. (Bugbot) - adopt no-destroy refusal printed only `helm rollback`, which cannot clear pending-install/uninstalling and never said how to keep the write-only credential. The helper now prints the credential-safe path (get values -> uninstall -> re-run); the caller drops the misleading rollback hint. (Bugbot) - `_status=$(_bounded helm status | awk)` lacked the house `|| _status=""` errexit guard: safe today only because both callers use `if !` (which masks set -e), a latent trap on any future bare call. Guarded. (audit) - cronjob ignored `uninstalling`, so a killed `helm uninstall` failed every tick with an opaque "another operation is in progress". Now skips cleanly with an actionable log line. (audit) Tests: +1 assess (probe fails closed on helm error), +1 installer (bare call under set -e doesn't abort), no-destroy test asserts the credential-safe remedy, +1 cronjob unittest (uninstalling handled). 38 assess + 118 installer bats + cronjob unittest all pass. manifest regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7b0b8ca. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
…pe (Bugbot #649) The prior `|| _status=""` guard fixed the set -e abort but RE-INTRODUCED the SIGPIPE-under-pipefail wipe the codebase already learned about (_extract_yaml_value, #525): on a WEDGED release awk's `exit` SIGPIPEs helm (141) as it writes the rest of the status body, so under `set -o pipefail` the pipe is non-zero and the guard wiped the correctly-parsed "pending-upgrade" — recovery then silently no-op'd and re-runs still hit "another operation is in progress". Defeats the whole fix. Capture the full `helm status` first (guarded), then parse it from a here-string (no pipe, nothing can signal) — the same idiom _extract_yaml_value uses. Added a regression test: a wedged status with a >64KB body under set -o pipefail must still trigger rollback (fails on the old code, passes now). 119 installer + 38 assess bats pass; manifest regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
aptracebloc
left a comment
There was a problem hiding this comment.
@divyasinghds — Approved. Verified against a fresh clone of the head, not just the description.
- R8 manifest
shasum -a 256 -c scripts/manifest.sha256→ all 18 files OK (headb771d560); theclient/chart changes correctly sit outside the signed trust root. - bats:
assess.bats38/38,install-client-helm.bats119/119 — matches the claimed counts, incl. the SIGPIPE-under-pipefail regression. - Logic:
helm statusparsed from a here-string (no early-exit awk pipe → no pipefail wipe), mutating rollback/uninstall deliberately unbounded,_assess_release_pendingfails closed (any error → "wedged"), andno-destroymode refuses the destructive uninstall to protect the stored credential. Sound and internally consistent. - All 9 Bugbot findings resolved (each with a fix commit + backing test); the last (pipefail status-parse) is on the current head and covered by installer-test 119, though Bugbot hasn't re-run on
b771d560.
Note: this supersedes the now-closed #619 — smaller, more focused, adds the assess-gate degrade path + helm-unittest coverage #619 lacked. Code-owner @saadqbal already approved the current head, so it's mergeable.
🤖 Generated with Claude Code

Fixes#554.
Problem
A
helmprocess killed mid-operation (Ctrl-C, OOM, host reboot, laptop sleep) leaves the release in apending-install/pending-upgradestate. The nexthelm upgrade --installthen fails with "another operation is in progress" — exit 1, not 124 — so the existing unwedge hint (guarded by-eq 124) never fires and nothing auto-recovers.scripts/lib/install-client-helm.sh): a prior killed run wedges every subsequent re-run with only a generic "installation failed".client/templates/auto-upgrade-cronjob.yaml): a killed upgrade wedges every hourly tick forever, silently killing security-fix delivery until a human runshelm rollback.Fix
Installer:
_recover_pending_helm_releasehelper — readshelm status(jq-free), thenhelm rollbackapending-upgrade/pending-rollback, orhelm uninstallapending-install/uninstallingrelease, before the helm op.--cleanup-on-failto both upgrades.uninstallbranch is data-safe: the chart rendershelm.sh/resource-policy: keepon every PVC and helm reads it from the stored manifest, so PVCs survive (perdocs/MIGRATIONS.md).Cronjob:
pending-upgrade/pending-rollbackwedge before retrying.--wait→--atomic --cleanup-on-failso a graceful failure self-reverts (the pre-flight rollback covers the abrupt-SIGKILL case--atomiccannot).Chart bumped
1.9.28 → 1.9.29(version + appVersion) so the fix actually publishes;manifest.sha256regenerated.Testing
bash -n+ shellcheck clean; embedded cronjob script passessh -n.chart-version-guard.sh✓;gen-manifest.sh --check✓.🤖 Generated with Claude Code
Note
Medium Risk
Changes install/upgrade/reconcile and security-fix auto-upgrade paths with destructive recovery (rollback/uninstall) and Helm flag changes; behavior is heavily tested but mistakes could affect live releases or credentials on adopt paths.
Overview
Fixes #554: a Helm process killed mid-operation leaves releases stuck in
pending-*states, so laterhelm upgradefails with another operation is in progress (exit 1, not timeout 124) and recovery never ran automatically.Installer (
install-client-helm.sh,assess.sh): Adds_recover_pending_helm_releaseto readhelm statusand rollbackpending-upgrade/pending-rollbackor uninstallpending-install/uninstallingbefore install/reconcile; adopt/reconcile usesno-destroyso it won’t uninstall and drop stored credentials.detect_installed_clientand reconcile nowhelm listwith--deployed --failed --pending --uninstallingso wedged releases stay visible across Helm 3/4 defaults. The assess gate treats pending wedges as degraded (pending-wedge) instead of fast-path healthy. Upgrades gain--cleanup-on-fail; failure hints mention manual unwedge on exit 1 as well as timeout.Auto-upgrade cronjob: Pre-upgrade rollback for
pending-upgrade/pending-rollback;uninstallingskips the tick cleanly. Replaces--waitwith--atomic --cleanup-on-fail.Chart 1.9.28 → 1.9.29;
manifest.sha256updated; new bats/helm unittest coverage for recovery ordering and fail-closed behavior.Reviewed by Cursor Bugbot for commit b771d56. Bugbot is set up for automated code reviews on this repo. Configure here.