Resolves #17. Part A: scripts/cleanup-rogue-workspaces.sh deletes workspaces whose id or name starts with known test placeholder prefixes (aaaaaaaa-, etc.) and force-removes the paired Docker container. Documented in tests/README.md. Part B: add a pre-flight check in provisionWorkspace() — when neither a template path nor in-memory configFiles supplies config.yaml, probe the existing named volume via a throwaway alpine container. If the volume lacks config.yaml, mark the workspace status='failed' with a clear last_sample_error instead of handing it to Docker's unless-stopped restart policy (which otherwise loops forever on FileNotFoundError). New pure helper provisioner.ValidateConfigSource + unit tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
1.9 KiB
Bash
Executable File
60 lines
1.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# cleanup-rogue-workspaces.sh — delete stale test workspaces matching
|
|
# well-known placeholder ID patterns (#17).
|
|
#
|
|
# Usage:
|
|
# bash scripts/cleanup-rogue-workspaces.sh # against http://localhost:8080
|
|
# MOLECULE_URL=http://host:8080 bash scripts/cleanup-rogue-workspaces.sh
|
|
#
|
|
# Deletes any workspace whose id OR name starts with one of the
|
|
# hard-coded test prefixes. Also force-removes the matching Docker
|
|
# container (`ws-<id[:12]>`) in case Docker's unless-stopped policy
|
|
# kept it restarting on a missing config.yaml.
|
|
#
|
|
# Safe to run repeatedly — idempotent. Non-matching workspaces are left
|
|
# untouched.
|
|
|
|
set -euo pipefail
|
|
|
|
PLATFORM_URL="${MOLECULE_URL:-http://localhost:8080}"
|
|
|
|
echo "Scanning ${PLATFORM_URL}/workspaces for rogue test workspaces..."
|
|
|
|
payload="$(curl -sS --fail "${PLATFORM_URL}/workspaces" || true)"
|
|
if [[ -z "${payload}" ]]; then
|
|
echo " platform unreachable at ${PLATFORM_URL}"
|
|
exit 1
|
|
fi
|
|
|
|
# Extract {id\tname} for workspaces whose id or name starts with a test prefix.
|
|
# Patterns are passed via env so the heredoc doesn't need shell interpolation.
|
|
matches="$(PAYLOAD="${payload}" python3 - <<'PY'
|
|
import json, os
|
|
data = json.loads(os.environ["PAYLOAD"])
|
|
rows = data if isinstance(data, list) else data.get("workspaces", [])
|
|
patterns = ("aaaaaaaa-", "bbbbbbbb-", "cccccccc-", "test-ws-")
|
|
for w in rows:
|
|
wid = w.get("id", "") or ""
|
|
name = w.get("name", "") or ""
|
|
if any(wid.startswith(p) or name.startswith(p) for p in patterns):
|
|
print(f"{wid}\t{name}")
|
|
PY
|
|
)"
|
|
|
|
if [[ -z "${matches}" ]]; then
|
|
echo " no rogue workspaces found"
|
|
exit 0
|
|
fi
|
|
|
|
while IFS=$'\t' read -r wid wname; do
|
|
[[ -z "${wid}" ]] && continue
|
|
short="${wid:0:12}"
|
|
echo " deleting workspace ${wid} (${wname})"
|
|
curl -sS -X DELETE "${PLATFORM_URL}/workspaces/${wid}" >/dev/null || true
|
|
if command -v docker >/dev/null 2>&1; then
|
|
docker rm -f "ws-${short}" >/dev/null 2>&1 || true
|
|
fi
|
|
done <<<"${matches}"
|
|
|
|
echo "done."
|