Skip to content

fix(kafka): delete the release's node pools on uninstall - #4490

Merged
Aleksei Sviridkin (lexfrei) merged 2 commits into
mainfrom
fix/kafka-uninstall-nodepools
Sep 26, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 2 commits into
mainfrom
fix/kafka-uninstall-nodepools

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Deleting a Kafka application left its brokers running. The migration hook creates the KafkaNodePools with kubectl apply, so they are not in the release manifest and helm uninstall does not remove them, and each pool owns the StrimziPodSet that runs its brokers. The pre-delete hook now deletes the release's pools, selected by strimzi.io/cluster=<release>, which is the label the migration hook stamps. Its Role gains list and delete on kafkanodepools, which is what a label-selected delete issues. Fixes #4488.

The pools are deleted after the KafkaTopics, also when the topic wait timed out. The topic operator clears topic finalizers through the brokers, so the brokers have to outlive it.

Deleting the pools before Helm deletes the Kafka CR does not bring brokers back. For the few seconds in between, Strimzi 0.45.1-rc1 (the version the operator chart pins) fails the reconcile with KafkaNodePools are enabled, but no KafkaNodePools found before it builds any StrimziPodSet. A regular pool is the ownerReference of its StrimziPodSet (NodePoolUtils.createKafkaPools at that tag), so the pods go away with the pool by garbage collection. If the Kafka CR is already gone when the hook runs, the pools are still found by label.

An absent CRD exits 0, and so does a release with no pools, because a label-selected delete that matches nothing succeeds. Any other failure exits 1. The call uses --wait=false and no request timeout, so the #4276 invariants and the 270s deadline arithmetic in delete.yaml stay as they were.

The cleanup lives in the pre-delete hook because only there a failure gets retried. In Helm v4.1.1, which helm-controller v1.5.0 vendors, a pre-delete hook error returns from Uninstall.Run before anything is deleted, so the next reconcile runs the hook again. A post-delete error is only collected: the release is still purged and Helm never runs its hooks again, so a failure there would leave the pools for good.

An ownerReference from each pool to the Kafka CR would also work. I did not take it because it changes what the migration hook applies on every install and upgrade, including the ZooKeeper migration path that adopts a namespace-wide kafka pool.

PVCs are out of scope. The pools set deleteClaim: false, so Strimzi leaves the broker and controller volumes in place when a pool is deleted, before and after this change.

Not covered by this change:

  • Pools leaked by releases deleted before this fix stay where they are. Find them with kubectl get kafkanodepools.kafka.strimzi.io -A -L strimzi.io/cluster; a pool whose cluster has no Kafka CR in its namespace (kubectl get kafkas.kafka.strimzi.io -A) is leaked, and kubectl -n <namespace> delete kafkanodepools.kafka.strimzi.io -l strimzi.io/cluster=<cluster> removes it and its brokers.
  • examples/backups/kafka-metadata/cleanup.sh returns without waiting for the Kafka uninstalls, unlike examples/backups/kafka/cleanup.sh, which is why the metadata suite's new checks carry a 12m budget. The new step already holds the suite until the pools and pods are gone, so the next suite no longer starts into a namespace that is still uninstalling. Adding the same wait_deleted calls to the script would let those checks drop to the default timeout; left as a follow-up because the example is outside this change.
  • The "Deletion and PVC retention" section of packages/apps/kafka/README.md does not yet say that the pre-delete hook now removes the node pools while deleteClaim: false keeps the PVCs. That one sentence is left as a follow-up.
  • Helm runs the hooks stored in the release's latest revision, so a release uninstalled before it has been upgraded onto this chart still runs the old hook and still leaks its pools.

Tests:

  • packages/apps/kafka/tests/delete_test.yaml: a new case pins, in one pattern, that the pool delete follows the topic delete, is scoped to this release, and does not wait, and pins the exact Role rule.
  • hack/kafka-pre-delete-hook.bats runs the rendered hook against a fake kubectl. Its fake now answers the pool call separately, and four new cases pin the exit codes and ordering: pools are deleted after the topics, still deleted after a topic-wait timeout, an absent pool CRD exits 0, and a failed pool delete exits non-zero.
  • Each new pin was checked by a mutation that turned it red: widening the selector, moving the pool delete before the topics, waiting on it, exiting on an absent CRD, not exiting on a failure, skipping the pools on the timeout branch, and removing or widening the Role rule.
  • E2E: the kafka, kafka-2-backup-roundtrip and kafka-3-metadata-roundtrip tests each gain a step, verify-uninstall-removes-node-pools, that checks, after the apps are deleted, that no KafkaNodePool and no pod labelled strimzi.io/cluster=<cluster> remains. The pod check covers every Strimzi pod of the cluster, not only the brokers, on purpose. The kafka test now deletes its app itself so it can look afterwards.

E2E success criterion, registered before the run: in this PR's E2E run, every verify-uninstall-removes-node-pools step (one in each of the tests kafka, kafka-2-backup-roundtrip and kafka-3-metadata-roundtrip) passes on the first attempt.

Screenshots

No UI change.

Downstream repositories

The diff touches one chart template and its unit tests, one hack unit test, and two e2e suites. No values, schema, README, package list, hack tooling, CRD or node requirement changes, so no entry of the trigger map applies.

Release note

fix(kafka): deleting a Kafka application now also deletes its KafkaNodePools, so its brokers stop instead of running on after the application is gone. Volumes are kept, as before. Node pools left behind by applications deleted before this fix are not removed automatically: delete them with `kubectl -n <namespace> delete kafkanodepools.kafka.strimzi.io -l strimzi.io/cluster=<cluster>`.

Summary by CodeRabbit

  • Bug Fixes
    • Kafka app uninstall now removes associated Kafka node pools after attempting to remove Kafka topics. If topic cleanup times out, uninstall continues; node pool cleanup failures are reported.
    • End-to-end checks now verify that Kafka node pools and pods are removed after uninstall and backup cleanup. If resources remain, diagnostic details are provided.
  • Tests
    • Added coverage for node pool cleanup outcomes, including missing resource definitions and deletion failures.

The migration hook creates the KafkaNodePools with kubectl apply, so
they are not in the release manifest and helm uninstall leaves them.
Each pool owns the StrimziPodSet that runs its brokers, so after the
application is deleted every broker keeps running, and keeps its memory
request, with nothing left to remove it.

The pre-delete hook now deletes the pools selected by
strimzi.io/cluster=<release> once the KafkaTopic delete has finished,
including when that wait timed out: the topic operator clears topic
finalizers through the brokers, so the brokers have to outlive it. The
call does not wait, so the Job's deadline arithmetic is unchanged. An
absent CRD is not a failure. Any other failure exits non-zero: once the
uninstall completes Helm never runs the hook again, so a retry is the
chart's last chance to remove the pools.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
The Kafka suites passed while the node pools of the apps they had
deleted, and the brokers those pools own, kept running into the suites
that came after. Each suite now checks, after its apps are gone, that
no KafkaNodePool and no pod labelled strimzi.io/cluster=<cluster>
remains in the namespace.

The kafka suite deletes its app explicitly so it can look afterwards.
The metadata roundtrip's cleanup does not wait for the uninstalls, so
its checks carry a budget sized from the pre-delete Job's deadline.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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 configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: d2f1eda4-1a1c-4268-96ce-9b76c67d0296

📥 Commits

Reviewing files that changed from the base of the PR and between 4a2dea2 and 7002397.

📒 Files selected for processing (5)
  • hack/e2e-chainsaw/kafka-metadata/chainsaw-test.yaml
  • hack/e2e-chainsaw/kafka/chainsaw-test.yaml
  • hack/kafka-pre-delete-hook.bats
  • packages/apps/kafka/templates/hooks/delete.yaml
  • packages/apps/kafka/tests/delete_test.yaml

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The pre-delete hook now removes release-scoped KafkaNodePools after topic cleanup. End-to-end tests check that node pools and pods are absent after uninstall and backup-flow cleanup.

Changes

Kafka uninstall cleanup

Layer / File(s) Summary
Pre-delete hook and deletion tests
packages/apps/kafka/templates/hooks/delete.yaml, hack/kafka-pre-delete-hook.bats, packages/apps/kafka/tests/delete_test.yaml
The hook deletes release-scoped KafkaNodePools after topic cleanup without waiting. It tolerates an absent NodePool CRD and fails on other deletion errors. The Role and tests cover the required permissions and deletion cases.
End-to-end uninstall checks
hack/e2e-chainsaw/kafka/chainsaw-test.yaml, hack/e2e-chainsaw/kafka-metadata/chainsaw-test.yaml
The tests check for remaining node pools and pods after app uninstall and backup-flow cleanup. Failure scripts list matching node pools, StrimziPodSets, and pods.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 70023

The uninstall cleanup and its checks present no established merge-blocking issue. The deleted pools are not recreated by Strimzi during uninstall.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 70023

The cleanup addresses brokers left running after uninstall. Its new permission is limited to the release namespace, but it can delete any node pool in that namespace while the hook runs; release scoping depends on the command the job executes. No unauthorized route to the job’s identity was demonstrated.

Retained concerns

  • Low · security · inferred: The new node-pool Role permission applies to every pool in the release namespace. The release label limits the hook’s command, but does not limit what a holder of the hook’s service-account credentials could delete. Exposure depends on controls over that identity during uninstall.
Security review details

Security Blast Radius

  • inferred — Misuse of the new job identity could affect other Kafka releases’ node pools in the same namespace, but the Role does not grant cluster-wide access and the normal command selects only this release’s cluster label.

Trust Boundaries and Controls

  • observed — The cleanup container runs as non-root with privilege escalation disabled and uses a dedicated service account. The hook fails on unexpected pool-delete errors rather than treating an authorization failure as successful cleanup.

Resilience and Maintainability Implications

  • observed — The flagged public-entrypoint ranges configure and assert Bats fixtures. They do not themselves add a deployed entrypoint or grant runtime authority.

Hardening Proposals

  • proposed — If less-privileged workloads share a namespace with Kafka releases, verify that they cannot adopt the cleanup service account or alter the labels used to identify another release’s pools; exercise interrupted and repeated uninstall against live resources.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: deleting the release's KafkaNodePools during uninstall.
Linked Issues check ✅ Passed The PR implements the coding requirements in issue #4488. The pre-delete hook deletes KafkaNodePools labeled strimzi.io/cluster=<release> after topic cleanup. The Role grants list and delete on …
Out of Scope Changes check ✅ Passed The reviewed changes stay within issue #4488. The hook changes provide the requested uninstall cleanup. The unit, hook, and E2E changes verify that cleanup and its failure cases. No unrelated product …
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (4 skipped: 4 …
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files labels Sep 26, 2026
@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as ready for review September 26, 2026 04:48
@lexfrei
Aleksei Sviridkin (lexfrei) merged commit b113cc7 into main Sep 26, 2026
20 checks passed
@lexfrei
Aleksei Sviridkin (lexfrei) deleted the fix/kafka-uninstall-nodepools branch September 26, 2026 11:38
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Oct 1, 2026
## What this PR does

A Chainsaw suite could pass while the workloads it created kept running
after its own cleanup, and nothing noticed until a later suite failed.
On one E2E run the brokers of four Kafka releases from suites that had
passed and deleted their applications (#4488), plus a TenantControlPlane
sitting on its finalizer with two apiserver pods (#4489), pushed pod
memory requests to the node limit. Harbor then failed on `Insufficient
memory` two suites later, under the wrong name.

This makes the suite that leaked fail, and name what it left behind.

- Chainsaw v0.2.15 has no hook that runs after a test's cleanup: its
`Configuration` carries `error.catch`, which runs only on failure. So
`make test-chainsaw` now runs `hack/e2e-chainsaw-run.sh`, which invokes
`chainsaw test` once per suite directory and checks the cluster between
invocations with `hack/e2e-chainsaw/_lib/leak-guard.sh`.
- Attribution: a pod is charged to a suite when all three hold. It is
not terminal. Its UID was not in the pod list taken before the suite.
The root of its ownerReferences chain was created at or after the suite
started. A platform pod replaced mid-suite by a rollout, an eviction or
a crash traces back to an object the install created, so it is not
charged. A pre-existing pod adopted by a controller recreated mid-suite
is excluded by the UID baseline. A leak is charged once, to its own
suite.
- The suite start is read off an apiserver, from the `creationTimestamp`
of a server-side dry-run create, rather than off the sandbox. With
several control-plane nodes the reading and a root's timestamp can come
from different apiservers, so the rule assumes their clocks agree more
closely than the gap between the reading and the suite's first object;
no skew margin is added. Source for that behaviour: the generic registry
`Store.create` stamps it through `rest.FillObjectMetaSystemFields`, then
`DryRunnableStorage.Create` copies it back without persisting
(`staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go`
and `dryrun.go` at Kubernetes v1.33.0).
- Budget: charged pods get 300s to disappear, the default
`timeouts.cleanup` in `.chainsaw.yaml`; a unit test keeps the two equal.
A suite that overrides its own cleanup timeout (kafka sets 8m) is
covered the same way, because the check runs only after `chainsaw test`
has returned, which is after every cleanup in the suite has finished
under its own timeout; the 300s start from there. The leaks this targets
sat about 110 minutes (Kafka brokers) and 30+ minutes (the
TenantControlPlane) past their suites. Every green suite logs
`leak-guard: suite=<dir> clear after Ns`, so the number can be checked
against later runs.
- Past the budget the run prints `LEAKED-PODS suite=<dir> budget=300s
count=N`, then one line per pod with its phase, deletion state, root and
`app.kubernetes.io/instance`. It raises a `leaked-pods` error annotation
and adds a failed `leak-guard` test case to `chainsaw-report.xml`. The
run continues through the remaining suites and exits non-zero at the
end, as the single invocation did on a red test.
- A suite that already failed on its own is checked once without the
wait. Its leftovers are reported as a `::warning` with `LEAKED-PODS
suite=<dir> checked-once=chainsaw-failed`, since some may still be
tearing down.
- Each pod gets one `kubectl wait` inside the remaining budget, and
nothing is retried. A timeout or a spent budget gives the verdict from
the last good pod list, also when the re-list after it fails, so a leak
fails the suite. A wait that fails only after running for its whole
`--timeout` counts as a timeout whatever it printed: kubectl maps an
expired context to "timed out waiting for the condition" only inside the
watch, so a deadline that passes during its initial lookup surfaces as
"context deadline exceeded" (`wait.go` and `delete.go` in kubectl
v1.33.0). A spent budget is caught before calling kubectl and never
passed as `--timeout=0`, which means "check once".
- Any other error on a wait or a re-list, before the budget is spent and
before any timeout, stops the waiting and is reported as `LEAKED-PODS
suite=<dir> count=N inconclusive="<error>"`. That is a warning, not a
leak failure, so an apiserver blip cannot become a leak red.
- A failed first reading after a suite, including a pod list that cannot
be parsed, leaves the suite unchecked. It fails with a `leak-guard`
annotation and a report case saying the cluster could not be read, not
that it leaked. Errors that are not leaks carry the annotation title
`leak-guard`, not `leaked-pods`.
- Known leaks are listed in `hack/e2e-chainsaw/_lib/leak-allowlist.txt`
as `<suite-dir> <issue-ref>`: `kubernetes-latest` and
`kubernetes-previous` against #4489 (both suites create a
TenantControlPlane through `_lib/run-kubernetes.sh`). `kafka` and
`kafka-metadata` were listed against #4488 until #4490 fixed it; those
entries are gone. An allowlisted suite prints its leak as a `::warning`
and does not fail. An entry that names a directory that is not a suite
stops the run before the first suite, so a rename cannot keep an
exemption alive. The PR fixing each issue deletes its lines.

Suite selection is unchanged. The runner runs the named directories when
there are any, and otherwise every directory directly under
`hack/e2e-chainsaw/`, each handed to Chainsaw's own discovery; it
refuses a tree with a test at its top level, where that split would stop
being exact. I verified this with Chainsaw v0.2.15's own
`discovery.DiscoverTests`, built from source at that tag. For the full
tree, for `kafka redis harbor` and for `kubernetes-latest`, the union of
the per-suite discoveries equals what a single `chainsaw test [suites]`
discovers: 57, 7 and 2 tests, identical lists. 57 matches the test count
in an earlier full run's report. A unit test pins that the runner passes
chainsaw the suites and flags it was given, in order. The order changes
too: suites used to run in Chainsaw's own order and now run one
directory at a time in alphabetical order, which moves the kubernetes
suites after the kafka ones. Before #4490 fixed #4488 this starved them
(see the E2E criterion below). With the fix merged the order has no
effect, and a future leak that lands before a memory-heavy suite is
named at the suite that leaked. One behaviour does change: a selected
name that is not a directory used to fail the single invocation before
any test ran; now only that suite fails and the others run.

`chainsaw-report.xml` keeps its path and shape: one `<testsuites>` root
with the summed counts, one `<testsuite>` per suite directory. Its
consumers are the `Collect chainsaw report` / `Upload chainsaw report`
steps in `pull-requests.yaml`, `e2e-fork.yaml`, `nightly.yaml` and
`e2e-tag.yaml`, which `docker cp` the file and upload it as an artifact.
Nothing in the repository parses it. The merge is unit-tested on two
per-suite reports in Chainsaw's real shape, one of them failed, and the
result is checked to parse as XML.

Tests: `hack/chainsaw-leak-guard_test.bats` (31 tests, in `make
unit-tests`; named without the `e2e-` prefix, which the unit lane
excludes) covers the attribution rule, the budget, the allowlist, the
merge and the runner, all against stub `kubectl` and `chainsaw`. Every
rule was mutation-checked: removing the guard call, having the Makefile
bypass the runner, inverting the allowlist match, flipping or tightening
the root-timestamp comparison, removing the UID baseline, removing the
terminal-phase filter, skipping the budget wait, changing the budget,
lowering or raising the depth cap, accepting a stale allowlist entry,
reading an unreadable pod list as empty, dropping the leak case from the
report, skipping directories in the selection, treating any failed owner
read as a missing owner, dropping the separate report for a suite that
already failed, dropping the not-run reason from the report, reading any
wait error as a timeout, removing the inconclusive branch, letting a
failed re-list after a timeout turn a leak inconclusive, ignoring a
failed re-list, reporting an unreadable cluster as a leak, reading an
unparsable pod list as clean, reporting a kubectl warning instead of the
error, passing the report name without its extension, wording a failed
re-list as a failed wait, dropping an allowlist's last line when it has
no newline, dropping the check that `--set-string` carries a value, and
reading a wait that ran its whole timeout but printed a context error as
inconclusive. Each one turns its named test red, and restoring turns it
green.

E2E criterion, registered before the run: in the `Run E2E tests` log of
this PR, every suite that ran prints `leak-guard: suite=<dir> clear
after`, a `LEAKED-PODS suite=<dir>` line carrying `allowlisted=`, or one
carrying `checked-once=` (a suite that failed on its own) or
`inconclusive=` (waiting stopped on a failed read), and no line matches
`LEAKED-PODS suite=.* budget=`. With #4490 merged, `kafka` and
`kafka-metadata` are no longer allowlisted and must print `leak-guard:
suite=kafka clear after` and `leak-guard: suite=kafka-metadata clear
after`, which is the live check that #4490 fixed #4488. The first run of
this PR (before #4490, run 36223896793) is the positive control: it
printed `LEAKED-PODS suite=kafka count=15` and `suite=kafka-metadata
count=8`, allowlisted, and every other suite cleared in 0-37s. That run
also went red on both kubernetes suites, because those 23 leaked brokers
left no memory for the second tenant worker VM (`0/3 nodes are
available: 3 Insufficient memory`), which the order change described
above made possible. The guard has not yet run against a real cluster,
so the first run of this PR should carry the `full-e2e` label; its
`clear after` lines are the first measured teardown times, and any
unexpected `LEAKED-PODS` line is either a real leak or a false positive
to understand before merge.

Follow-ups, known and out of scope here:

- The allowlist exempts a whole suite, so a new leak in an allowlisted
suite stays hidden until the issue's fix removes the line.
- Attribution is per suite directory, not per Chainsaw `Test`; the leak
lines carry the release instance, which usually names the test.
- Only pods are checked. A leaked PVC, LoadBalancer IP or bucket holds
no node memory and is not seen.
- The 300s budget rests on Chainsaw's cleanup timeout, not on a measured
pod-gone latency; the `clear after` lines are where that measurement
will come from.
- A platform object recreated mid-suite (for example a HelmRelease
uninstall/reinstall remediation, or a force-replace on an immutable
field) has a root newer than the suite start, and its pods would be
charged to whichever suite was running as a leak; the first CI runs are
where to look for this.
- The per-pod wait recomputes the remaining budget before each pod, and
no unit test pins that.
- `kubernetes-previous` is allowlisted for #4489 although the stuck
finalizer was seen only in `kubernetes-latest`; both suites create a
TenantControlPlane the same way, and the exemption hides any new leak
there until #4489 is fixed.
- Each clean suite now waits for its own pods to finish terminating
before the next starts, where the tail used to overlap the next suite.
The first full run's `clear after` lines say what that costs against the
E2E job's 215-minute cap.
- After a timeout followed by a failed re-list, the leak lines come from
the list taken before the wait, so a pod whose own wait had already
succeeded is still named and the count can be too high; the verdict is
unaffected.
- Likewise, when every wait succeeded and the re-list then fails, the
inconclusive report still lists the pods whose deletion was just
confirmed. In the boundary case where the budget runs out during the
last successful wait and the re-list then fails, the result is
inconclusive rather than a verdict from the last good list, because the
guard only treats the budget as spent when it finds it exhausted at a
check.
- A mistyped `COZY_LEAK_ALLOWLIST` path passes allowlist validation; it
fails closed, since no suite is then exempt.
- An allowlisted suite is not waited on, so `kubernetes-latest` and
`kubernetes-previous` will print `LEAKED-PODS ... allowlisted=` on most
runs even for pods still in normal teardown; waiting out the budget
first and applying the allowlist only to what remains would stop that
noise.
- A slow teardown in each of several suites can add up to 300s apiece,
against the E2E job's 215-minute cap. On the first full run of this PR
(run 36223896793) the `clear after` values of the suites that were
waited on summed to 210s; the last full run before this change took 158
of the 215 minutes.
- A pod whose owner's resource type is no longer served gets "the server
doesn't have a resource type" rather than NotFound, so its suite is
reported unchecked instead of leaked; it still fails.
- `kubectl wait --for=delete` waits for deletion, while the guard skips
pods in `Succeeded` or `Failed`; a pod that finishes but is never
deleted (a Job under a leaked root) spends the rest of the budget before
the suite shows `clear`. The verdict is right; only job time is lost.
- The unit tests do not pin the one-line empty report Chainsaw writes
for a directory with no tests (`_lib`), nor the branch that catches a
spent budget before calling `kubectl wait`; both are verified against
the Chainsaw and kubectl sources rather than by a test.
- The runner's per-suite report directory from `mktemp -d` is not
removed; the sandbox is discarded after the job.
- Each non-suite directory under `hack/e2e-chainsaw/` (today `_lib`)
costs one empty Chainsaw invocation, because the runner leaves the
decision of what is a test to Chainsaw.

### Screenshots

Not a UI change.

### Downstream repositories

- [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:

The diff adds files under `hack/`, changes the `test-chainsaw` target,
and rewords one comment and the failure line in the four E2E workflows.
The trigger map's `hack/` entry is for cozystack/ccp, whose skills
anchor on `hack/package.mk`, `hack/common-envs.mk` and `make generate`,
none of which change here. A GitHub code search for `chainsaw` across
the cozystack organisation finds hits only in this repository.

### Release note

```release-note
test(e2e): the E2E app suite now fails the suite whose pods outlive its own cleanup by more than 300s, naming each leftover pod and its owner; suites with a filed leak are listed in hack/e2e-chainsaw/_lib/leak-allowlist.txt and only warn.
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kafka: uninstall leaves KafkaNodePools and their brokers running

2 participants