76 lines
3.8 KiB
Python
76 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""walk_crash_t1_customer.py — the household's leg of a crash mail (fail.app-crash, fault.customer-email), Tester 1.
|
|
|
|
Tester 1's notification page has no address (found empty 2026-11-02), so the first walk mailed only the operator. A household
|
|
types its address on the page and saves it (the page's own form), then an app crash-loops. Want: the Hungarian mail in the
|
|
household's mailbox (the catch-all, read through the mailbox). Then the address is cleared through the same page (its own
|
|
„confirm_clear"), the app removed. What stays: the saved list of events = the page's defaults."""
|
|
import os, re, sys, time
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
|
os.environ.setdefault("TARGET", "tester-1")
|
|
import box_walk as w
|
|
|
|
APP, SUB = "radicale", "radicale"
|
|
EV, ADDRESS = os.environ["EV"], "tester1@felhom.eu"
|
|
|
|
|
|
def form(fields):
|
|
sess = open(f"{w.SC}/sess{os.getpid()}.txt").read().strip()
|
|
csrf = open(f"{w.SC}/csrf{os.getpid()}.txt").read().strip()
|
|
args = ["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", w.HOSTHDR, "-H", f"Cookie: {sess}", "-X", "POST", "--data-urlencode", f"_csrf={csrf}"]
|
|
for k, v in fields:
|
|
args += ["--data-urlencode", f"{k}={v}"]
|
|
r = w.sh(args + [f"{w.BASE}/settings/notifications"], timeout=60)
|
|
return (r.stdout or "").split("\n")[0].strip()
|
|
|
|
|
|
w.login()
|
|
page = w.page("/settings/notifications")
|
|
page = page if isinstance(page, str) else str(page)
|
|
fields = [("notification_email", ADDRESS), ("cooldown_hours", "6")]
|
|
for m in re.finditer(r'<input type="hidden" name="(group_[a-z]+_shown)" value="([^"]*)"', page):
|
|
fields.append((m.group(1), m.group(2)))
|
|
checked = []
|
|
for tag in re.findall(r"<input[^>]*type=\"checkbox\"[^>]*>", page):
|
|
n = re.search(r'name="([a-z_]+)"', tag)
|
|
if n and " checked" in tag:
|
|
fields.append((n.group(1), "on"))
|
|
checked.append(n.group(1))
|
|
w.say(f" checked as found: {checked}")
|
|
if not any(c in ("group_apps", "event_app_stopped_unhealthy") for c in checked):
|
|
fields.append(("group_apps", "on"))
|
|
w.say(" the group „my apps” was off: switched on, as a household that wants this mail would")
|
|
w.say(f" save with the address -> {form(fields)}")
|
|
time.sleep(20) # the page's save syncs the preferences to the hub
|
|
assert not w.stack(APP).get("deployed"), "radicale is on the box — not a throwaway"
|
|
assert w.deploy(APP, SUB), "install failed"
|
|
w.wait_app(SUB, "/", tries=40)
|
|
start = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
|
w.say(f" installed; kills start {start}")
|
|
for i in range(12):
|
|
pid = w.guest(f"docker inspect -f '{{{{.State.Pid}}}}' {APP} 2>/dev/null").strip()
|
|
if pid and pid != "0":
|
|
w.guest(f"kill -9 {pid}")
|
|
st = w.stack(APP)
|
|
w.say(f" kill -9 #{i + 1} pid {pid}: state {st.get('state')} hold {str(st.get('hold_reason'))[:40]!r}")
|
|
if st.get("hold_reason"):
|
|
break
|
|
time.sleep(15)
|
|
time.sleep(60)
|
|
w.say(" controller log:\n" + w.guest(f"docker logs --since {start} felhom-controller 2>&1 | grep -E 'STOPPING|Event pushed' | cut -c1-200 | tail -6"))
|
|
c, d = w.ctl("POST", f"/api/stacks/{APP}/start")
|
|
w.say(f" Start -> {c}")
|
|
for _ in range(24):
|
|
time.sleep(5)
|
|
st = w.stack(APP)
|
|
if st.get("state") == "running" and not st.get("hold_reason"):
|
|
break
|
|
w.remove(APP)
|
|
w.say(f" removed: deployed={w.stack(APP).get('deployed')}")
|
|
w.say(f" the address cleared -> {form([('notification_email', ''), ('confirm_clear', '1'), ('cooldown_hours', '6')] + [f for f in fields if f[0] not in ('notification_email', 'cooldown_hours')])}")
|
|
time.sleep(5)
|
|
p2 = w.page("/settings/notifications")
|
|
p2 = p2 if isinstance(p2, str) else str(p2)
|
|
w.say(" the page's address now: " + (re.search(r'name="notification_email"\s*value="([^"]*)"', p2) or [None, "?"])[1])
|
|
open(os.path.join(EV, "crash-walk-customer.log"), "w").write("\n".join(w.LOG) + "\n")
|