diff --git a/testing/ci-cleanup.sh b/testing/ci-cleanup.sh index 407b11b..010394d 100755 --- a/testing/ci-cleanup.sh +++ b/testing/ci-cleanup.sh @@ -15,9 +15,15 @@ # with that prefix). # # The generic CI label is shared by every run on the host, so an unscoped label -# sweep would tear down a CONCURRENT run's resources. Pass --run-id to narrow -# the label sweep to one run; a run's own teardown always does. Without it the -# label sweep stays broad, which is what a manual "reap everything" wants. +# sweep would tear down a CONCURRENT run's resources. So would an unscoped +# compose-project sweep, and reap_containers runs BOTH. Neither flag alone is +# therefore enough to spare a concurrent run: --run-id narrows only the label +# sweep, --project-prefix only the project sweep, and whichever is left broad +# reaps everything by itself. This cost three concurrent runs on 2026-07-29, +# via a caller that passed --run-id alone and reasonably believed that scoped +# it. --run-id now implies the matching project prefix, and --project-prefix +# without --run-id is refused, so the dangerous half-scoped states are no +# longer reachable. Passing neither is still the broad "reap everything" form. # # Host-namespace veth interfaces are the one non-docker resource reaped here. # The chaos simulation creates each pair in the host namespace and then moves @@ -37,12 +43,17 @@ # # Usage: # ci-cleanup.sh Reap ALL fips-ci resources (any run) +# ci-cleanup.sh --run-id ID Scope the reap to one run: narrows the +# label sweep to ID and, unless +# --project-prefix says otherwise, +# narrows the project sweep to that run +# too. Does NOT scope the host veth +# sweep — see --veth-suffixes # ci-cleanup.sh --project-prefix P Restrict the compose-project sweep to -# names starting with P (scopes the reap -# to a single run). Does NOT scope the -# host veth sweep — see --veth-suffixes -# ci-cleanup.sh --run-id ID Restrict the label sweep to the run -# labelled ID (leaves other runs alone) +# names starting with P. Requires +# --run-id: on its own it leaves the +# label sweep broad, which reaps every +# concurrent run regardless of P # ci-cleanup.sh --label L Override the CI label (default above) # ci-cleanup.sh --images "a b,c" Also `docker rmi -f` these image tags # (space- or comma-separated) @@ -75,11 +86,12 @@ VETH_SUFFIXES="" # empty AND no --run-id: every simulation veth name # only ever run the harness it need not exist at all, and the reap this script # advertises as the remedy for orphaned interfaces would be a permanent no-op. VETH_IMAGE="" +PROJECT_PREFIX_GIVEN=0 while [[ $# -gt 0 ]]; do case "$1" in --label) LABEL="$2"; shift 2 ;; - --project-prefix) PROJECT_PREFIX="$2"; shift 2 ;; + --project-prefix) PROJECT_PREFIX="$2"; PROJECT_PREFIX_GIVEN=1; shift 2 ;; --run-id) RUN_ID="$2"; shift 2 ;; --images) IMAGES="$2"; shift 2 ;; --veth-suffixes) VETH_SUFFIXES="$2"; shift 2 ;; @@ -89,6 +101,22 @@ while [[ $# -gt 0 ]]; do esac done +# A reap scoped on one axis and broad on the other still destroys every +# concurrent run, because both selectors run. Close both half-scoped states +# here rather than trusting each caller to pass the pair. +if [[ -n "$RUN_ID" && "$PROJECT_PREFIX_GIVEN" -eq 0 ]]; then + # Every run's projects are named "_", so the + # run id is all that is needed. Derived from the base rather than a second + # copy of the literal, so the two cannot drift. + PROJECT_PREFIX="${PROJECT_PREFIX}${RUN_ID}" +fi +if [[ -z "$RUN_ID" && "$PROJECT_PREFIX_GIVEN" -eq 1 ]]; then + echo "ci-cleanup.sh: --project-prefix requires --run-id." >&2 + echo " Without it the label sweep stays broad and reaps every concurrent" >&2 + echo " run regardless of the prefix. Pass both, or neither for a full reap." >&2 + exit 2 +fi + # Resolve the image to run ip(8) in: the caller's choice, then the run's own # image, then any surviving fips test image. That last fallback is what keeps # an unscoped `ci-local.sh --reap` working — it execs this script from inside @@ -116,8 +144,9 @@ fi # never wedge a caller (ci-local's signal trap relies on this being bounded). TMO=30 -# Selector for the label sweep. With --run-id it matches only the named run, so -# a concurrent run's resources are left alone; without it, every CI run. +# Selector for the label sweep. With --run-id it matches only the named run; +# without it, every CI run. Note this is only half of what scopes a reap — the +# compose-project sweep below is the other half, and both must be narrow. if [[ -n "$RUN_ID" ]]; then SWEEP_LABEL="${RUN_LABEL_KEY}=${RUN_ID}" else