|
| 1 | +--- |
| 2 | +# Generic, product-agnostic teardown step for Stackable kuttl tests. |
| 3 | +# |
| 4 | +# Runs BEFORE kuttl deletes the test namespace. It removes every Stackable workload so the built-in |
| 5 | +# StatefulSet controller can no longer recreate Pods during namespace deletion (the recreation race |
| 6 | +# that wedges Pods on ephemeral-PVC ownership / listener-secret CSI mount and blocks teardown). |
| 7 | +# |
| 8 | +# It hardcodes nothing about the product under test: |
| 9 | +# 1. discovers and deletes ALL `*.stackable.tech` custom resources in the namespace (Stackable |
| 10 | +# StatefulSets are managed by their CR, so they must be removed this way), |
| 11 | +# 2. deletes the NON-Stackable Pod-owning workload controllers directly — StatefulSets, Deployments, |
| 12 | +# ReplicaSets, DaemonSets, Jobs NOT labelled stackable.tech/vendor=Stackable (unmanaged |
| 13 | +# test-helper / infra deps such as MinIO/Keycloak/LDAP/KDC; disposable, and removing them stops |
| 14 | +# Pod recreation). Stackable-managed workloads are excluded on purpose — they are removed via |
| 15 | +# their CR (step 1) + GC, so deleting them here would race the operator into recreating them, |
| 16 | +# 3. waits for the Stackable-managed StatefulSets to be gone (selected by the |
| 17 | +# `stackable.tech/vendor=Stackable` label, so a StatefulSet the step can't remove — owned by |
| 18 | +# another controller — doesn't make it wait out its timeout), so nothing recreates Pods, |
| 19 | +# 4. force-deletes any remaining Pods (safe now) instead of waiting out product shutdown grace. |
| 20 | +# |
| 21 | +# Drop this file in as the highest-numbered step of a test (e.g. 99-teardown.yaml), or — better — |
| 22 | +# have it injected into every test from a single source (see teardown-step-placement.md). |
| 23 | +apiVersion: kuttl.dev/v1beta1 |
| 24 | +kind: TestStep |
| 25 | +timeout: 600 |
| 26 | +commands: |
| 27 | + - timeout: 600 |
| 28 | + script: | |
| 29 | + set -u |
| 30 | + NS="$NAMESPACE" |
| 31 | +
|
| 32 | + # 1. Delete ALL Stackable CRs in the namespace (dynamic kind discovery — no per-product names). |
| 33 | + # Stackable StatefulSets are MANAGED: the operator recreates them from the CR, so they must be |
| 34 | + # removed via the CR, not by deleting the StatefulSet directly. |
| 35 | + kinds="$(kubectl api-resources --verbs=delete --namespaced -o name 2>/dev/null \ |
| 36 | + | grep '\.stackable\.tech$' | paste -sd, -)" |
| 37 | + [ -n "$kinds" ] && kubectl -n "$NS" delete "$kinds" --all --ignore-not-found --wait=false || true |
| 38 | +
|
| 39 | + # 2. Best-effort: delete the NON-Stackable Pod-owning workload controllers directly (test helpers |
| 40 | + # like *-test-helper / checks, and infra deps — MinIO/Keycloak are Deployments and use PVCs; |
| 41 | + # OpenLDAP/KDC may be bare StatefulSets). These are disposable and, crucially, unmanaged: no |
| 42 | + # operator watches them, so deleting the top-level object sticks and nothing recreates it. |
| 43 | + # Removing them stops them recreating Pods, so the force-delete/wait below can't churn and |
| 44 | + # nothing re-adds pvc-protection during namespace deletion. |
| 45 | + # |
| 46 | + # We deliberately EXCLUDE Stackable-managed workloads (stackable.tech/vendor=Stackable). Those |
| 47 | + # are removed by deleting their CR (step 1) + owner-reference GC, and waited on in step 3. |
| 48 | + # Deleting them directly here would RACE the operator: our delete fires the operator's |
| 49 | + # owned-object watch, and if its informer cache hasn't yet seen the CR deletion (no |
| 50 | + # deletionTimestamp cached), the reconcile re-applies the StatefulSet — recreation overtaking |
| 51 | + # our delete. `notin (Stackable)` also matches objects with no vendor label, i.e. exactly the |
| 52 | + # unmanaged ones. |
| 53 | + kubectl -n "$NS" delete statefulsets,deployments,replicasets,daemonsets,jobs \ |
| 54 | + -l 'stackable.tech/vendor notin (Stackable)' \ |
| 55 | + --ignore-not-found --wait=false >/dev/null 2>&1 || true |
| 56 | +
|
| 57 | + # 3. Wait for the STACKABLE StatefulSets to be gone (their controller then can't recreate Pods). |
| 58 | + # Scoped to the Stackable vendor label so a foreign StatefulSet the step can't remove (owned by |
| 59 | + # another controller) can't make this loop burn its full 300s timeout. We only need to |
| 60 | + # guarantee the operator-managed STSs — the wedge-prone ones — are cleared here. |
| 61 | + sel="stackable.tech/vendor=Stackable" |
| 62 | + for _ in $(seq 1 150); do |
| 63 | + [ -z "$(kubectl -n "$NS" get statefulsets -l "$sel" -o name 2>/dev/null)" ] && break |
| 64 | + sleep 2 |
| 65 | + done |
| 66 | +
|
| 67 | + # 4. Force-delete any lingering Pods (nothing recreates them now); skips product grace periods. |
| 68 | + kubectl -n "$NS" delete pods --all --grace-period=0 --force --ignore-not-found >/dev/null 2>&1 || true |
| 69 | +
|
| 70 | + # 5. Best-effort: let Pods clear so the namespace delete has no work left. |
| 71 | + for _ in $(seq 1 60); do |
| 72 | + [ -z "$(kubectl -n "$NS" get pods -o name 2>/dev/null)" ] && break |
| 73 | + sleep 2 |
| 74 | + done |
| 75 | + echo "teardown: stackable CRs deleted; pods remaining in $NS: $(kubectl -n "$NS" get pods --no-headers 2>/dev/null | wc -l)" |
0 commit comments