0030cbd1de
gates / gates (push) Successful in 26s
The records the catalog's ladder entries cite (apps/<app>/bench and apps/<app>/verdict.json), the drill tools, the spike, R-626's run, R-612/R-613 red-proofs, the chaos schedule (seed 20260923) drawn BEFORE round 1. Docs and register follow at the end of the night. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
61 lines
3.3 KiB
Python
61 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""R-626 — does a removed app come back? Measured through the product on 9202.
|
|
|
|
deploy navidrome -> remove through the product -> `docker events` for the compose project for 5 min,
|
|
a controller restart at +150 s -> then a GUEST REBOOT and a look after it. Positive observable: the
|
|
events stream must show the remove's own destroy events (proves the watcher saw the project), and
|
|
any `create` after the remove's answer is the defect.
|
|
"""
|
|
import sys, time, json
|
|
sys.path.insert(0, ".")
|
|
import walk as w
|
|
|
|
APP = sys.argv[1] if len(sys.argv) > 1 else "navidrome"
|
|
w.login()
|
|
w.say(f"R-626 run on guest {w.GUEST}, app {APP}")
|
|
if not w.deploy(APP, APP):
|
|
sys.exit("deploy failed")
|
|
w.wait_app(APP, "/")
|
|
w.say("starting docker events watcher (400 s) in the guest")
|
|
w.guest(f"rm -f /root/r626-events.txt; nohup timeout 400 docker events --format '{{{{.Time}}}} {{{{.Type}}}} {{{{.Action}}}} {{{{.Actor.Attributes.name}}}} project={{{{index .Actor.Attributes \"com.docker.compose.project\"}}}}' --filter label=com.docker.compose.project={APP} > /root/r626-events.txt 2>&1 &\nsleep 1; echo started")
|
|
time.sleep(3)
|
|
t_remove = time.time()
|
|
code = w.remove(APP)
|
|
w.say(f"remove answered {code} at epoch {int(t_remove)}")
|
|
checks = {}
|
|
for at in (60, 150, 300, 390):
|
|
while time.time() - t_remove < at:
|
|
time.sleep(2)
|
|
if at == 150:
|
|
w.say("+150 s: restarting the controller (docker restart felhom-controller)")
|
|
w.guest("docker restart felhom-controller >/dev/null; echo restarted")
|
|
time.sleep(20)
|
|
w.login()
|
|
out = w.guest(f"docker ps -a --filter label=com.docker.compose.project={APP} --format '{{{{.Names}}}} {{{{.Status}}}}'; docker volume ls -q | grep -c '^{APP}_' || true")
|
|
st = w.stack(APP)
|
|
checks[at] = {"containers_and_volcount": out.strip(), "deployed": st.get("deployed")}
|
|
w.say(f"+{at} s: {checks[at]}")
|
|
ev = w.guest("cat /root/r626-events.txt")
|
|
w.say("events after the remove:\n" + ev)
|
|
w.say("rebooting guest 9202 (pct reboot)")
|
|
w.sh(["ssh", w.HP, f"pct reboot {w.GUEST}"], timeout=300)
|
|
for _ in range(60):
|
|
time.sleep(5)
|
|
r = w.sh(["ssh", w.HP, f"pct exec {w.GUEST} -- docker ps --format '{{{{.Names}}}}'"], timeout=30)
|
|
if "felhom-controller" in (r.stdout or ""):
|
|
break
|
|
time.sleep(60)
|
|
w.login()
|
|
after = w.guest(f"docker ps -a --filter label=com.docker.compose.project={APP} --format '{{{{.Names}}}} {{{{.Status}}}}'; docker volume ls -q | grep '^{APP}_' || true; ls -d /opt/docker/stacks/{APP} 2>&1")
|
|
st = w.stack(APP)
|
|
w.say(f"after reboot (+60 s): containers/volumes/dir = {after.strip()!r} deployed={st.get('deployed')}")
|
|
creates = [l for l in ev.splitlines() if " create " in l]
|
|
destroys = [l for l in ev.splitlines() if " destroy " in l]
|
|
verdict = {"app": APP, "remove_http": code, "checks": checks, "events_create_after_remove": creates,
|
|
"events_destroy": destroys, "after_reboot": after.strip(), "deployed_after_reboot": st.get("deployed"),
|
|
"positive_observable_destroy_events_seen": bool(destroys),
|
|
"came_back": bool(creates) or bool([l for l in after.splitlines() if l.strip() and "No such file" not in l and not l.startswith("ls:")])}
|
|
json.dump(verdict, open("A3-r626-verdict.json", "w"), indent=2)
|
|
w.say("VERDICT " + json.dumps({k: verdict[k] for k in ("came_back", "positive_observable_destroy_events_seen")}))
|
|
open("A3-r626-log.txt", "w").write("\n".join(w.LOG) + "\n")
|