#!/usr/bin/env python3 """R-660 positive control: a throwaway app (actualbudget) stopped OUT OF BAND must raise app_start_failed while the held nextcloud (held since 06:27:40Z) must not. Then the throwaway is removed through the product.""" import json, sys, time sys.path.insert(0, ".") import walk as w w.login() t0 = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) ok = w.deploy("actualbudget", "budget-c660") w.wait_app("budget-c660", "/", tries=40, delay=3) w.say("stopping the throwaway's container out of band (not through the product): " + w.guest("docker stop actualbudget 2>&1").strip()) found = None for i in range(24): time.sleep(10) c, d = w.ctl("GET", "/api/debug/logs?level=DEBUG&lines=6000") ents = ((d.get("data") or {}).get("entries") or []) if isinstance(d, dict) else [] evs = [e["timestamp"] + " " + e["message"] for e in ents if e.get("timestamp", "") >= t0 and ("app_start_failed" in e.get("message", "") or "currently down" in e.get("message", ""))] if any("app_start_failed" in x for x in evs): found = evs break w.say("events/heartbeats since the control started:\n " + "\n ".join(found or evs)) names = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -iE 'app_start_failed|not running|DeadApp|nem fut' | tail -8") w.say("controller log lines:\n" + names) st = w.stack("nextcloud") w.say(f"nextcloud meanwhile: state={st.get('state')} held={bool(st.get('hold_reason'))}") w.remove("actualbudget") json.dump({"events": found or evs, "log": names, "nextcloud_state": st.get("state")}, open("../partB/03-r660-control.json", "w"), indent=2) open("../partB/03-r660-control.log", "w").write("\n".join(w.LOG) + "\n")