Repository navigation
fix(hack): seaweedfs naming audit reports a false clean when a kubectl LIST fails #3431
Description
What happens
hack/seaweedfs-naming-audit.sh prints an empty table when a kubectl LIST fails, which is indistinguishable from the output for a genuinely clean fleet. Observed on a cluster where namespace-wide secret LISTs time out (>45s in tenant-root, cozy-system and default) while etcd and /readyz report healthy: the script exits 0 and reports nothing.
Why
system_releases() enumerates Helm releases with a namespace-wide secret LIST whose type filter is applied client-side, so the API returns every secret in the namespace — and stderr is discarded:
https://github.com/cozystack/cozystack/blob/main/hack/seaweedfs-naming-audit.sh#L95-L97
for rel in $(kubectl get secret -n "$1" \
-o jsonpath='{range .items[?(@.type=="helm.sh/release.v1")]}{.metadata.labels.name}{"\n"}{end}' 2>/dev/null \
| grep -E -- '-system$' | sort -u); doA timeout produces empty stdout, the for body never runs, and the namespace is reported as having no SeaweedFS instances. The script has no error handling anywhere — there is no path that distinguishes "the query failed" from "nothing found".
The same fail-open shape applies to the other enumerations, so the blast radius is not limited to one call:
kubectl get ns -o name 2>/dev/null(whole-cluster mode) — a failure here audits zero namespaces and still prints an empty tablekubectl get pvc -n "$ns" -o name 2>/dev/nullkubectl get sts -n "$ns" -l app.kubernetes.io/name=seaweedfs -o name 2>/dev/null
Why it matters
This output is the documented gate in front of a destructive action. docs/operations/seaweedfs-431-rename-recovery.md makes it Step 1, and says of the script:
it is what the chart's refusal hands you to and acting on it deletes PVCs
It is also the verification step after the deletion, and the precondition for upgrading:
Re-run the audit. The tenant must now read
L(orS, if you are on the Step 2 path). Only then upgrade.
Every tenant must read
LorSin the audit before you start
So a silent false-clean is the exact output an operator is told to trust — both before deleting PVCs and when confirming the deletion worked. The same runbook notes that two earlier inline versions of this classification shipped wrong, which is why it became a script; this is a third failure in the same surface, one layer below the classification logic.
Test coverage
hack/seaweedfs-naming-audit.bats has 11 tests. All of them exercise classification — prefix reconstruction and generation direction. None injects a kubectl failure, so the runbook's "it is a tested script now" reassurance does not extend to the enumeration layer where this bug lives.
Suggested direction
Not a prescription, but the shape that seems right:
- Stop discarding stderr on the enumerations, or capture the exit status and abort loudly. "Could not determine" must be a distinct, non-zero outcome — never an empty row.
- Push the type filter server-side with
--field-selector type=helm.sh/release.v1instead of client-side jsonpath. That removes the full-namespace secret LIST that is timing out here, which addresses the trigger as well as the symptom. Noterevisions()already uses a label selector (-l name=$2,owner=helm), so the script is inconsistent with itself on this point. - Add a test that makes the fake
kubectlfail and asserts the script exits non-zero rather than printing a clean table.
Activity
fix(seaweedfs): make naming audit fail closed on kubectl and payload …
fix(seaweedfs): make naming audit fail closed on kubectl and payload …