Files
felhom.eu/documentation/audits/night-2026-10-28/tools/walk_lifecycle.py
T

53 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""walk_lifecycle.py — capability map B2 („App lifecycle") re-walked on scratch 9202 (night 2026-10-28) through the
dashboard's own API: install a throwaway Radicale, stop, start, restart, logs; the protected stacks refuse a stop; remove
with its data — and its labelled volume is gone (the re-check the map row asks for after R-1117)."""
import json, os, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
os.environ.setdefault("SC", "/tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/rt-9202")
os.environ.setdefault("TARGET", "9202")
import box_walk as w
EV = "/tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/night/p5"
APP = "radicale"
w.login()
res = {}
def state():
return w.stack(APP).get("state")
def running_containers():
return w.guest(f"docker ps --filter label=com.docker.compose.project={APP} --format '{{{{.Names}}}} {{{{.Status}}}}'").strip()
res["deploy"] = w.deploy(APP, "radicale")
res["volumes_installed"] = w.guest(f"docker volume ls --filter label=com.docker.compose.project={APP} --format '{{{{.Name}}}}'").split()
for act in ("stop", "start", "restart"):
code, d = w.ctl("POST", f"/api/stacks/{APP}/{act}")
for _ in range(24):
time.sleep(5)
s = state()
if (act == "stop" and s != "running") or (act != "stop" and s == "running"):
break
res[act] = {"http": code, "state_after": state(), "containers": running_containers()}
w.say(f"B2 {act}: http={code} state={res[act]['state_after']} containers={res[act]['containers']!r}")
code, d = w.ctl("GET", f"/api/stacks/{APP}/logs?lines=20")
logs = (d.get("data") or {}) if isinstance(d, dict) else {}
txt = json.dumps(logs)
res["logs"] = {"http": code, "bytes": len(txt), "names_radicale": "radicale" in txt.lower()}
w.say(f"B2 logs: http={code}, {len(txt)} bytes, mention radicale: {res['logs']['names_radicale']}")
for prot in ("traefik", "felhom-controller", "cloudflared"):
code, d = w.ctl("POST", f"/api/stacks/{prot}/stop")
alive = w.guest(f"docker ps --filter name=^{prot}$ --format '{{{{.Status}}}}'").strip()
res[f"protected_{prot}"] = {"http": code, "msg": str(d)[:160], "still_running": alive}
w.say(f"B2 protected stop {prot}: http={code} ({str(d)[:100]}) still running: {alive!r}")
code = w.remove(APP)
time.sleep(5)
res["remove_http"] = code
res["volumes_after"] = w.guest(f"docker volume ls --filter label=com.docker.compose.project={APP} --format '{{{{.Name}}}}'").split()
res["containers_after"] = w.guest(f"docker ps -a --filter label=com.docker.compose.project={APP} --format '{{{{.Names}}}}'").split()
w.say(f"B2 remove with data: http={code}, labelled volumes left {res['volumes_after']}, containers left {res['containers_after']}")
json.dump(res, open(os.path.join(EV, "lifecycle-walk.json"), "w"), indent=1)
open(os.path.join(EV, "lifecycle-walk.log"), "w").write("\n".join(w.LOG) + "\n")