gates / gates (push) Successful in 6m34s
The catalog published SABnzbd and NZBGet today (app-catalog 8ee687e). Here: - two cards in the media category on both language sets, each with the project's own logo and each saying IN BOLD that a paid Usenet subscription is the household's own - the published count 60 -> 62 everywhere it is stated; the category badges are derived from the cards now (media read 15 with 17 cards, otthon/home read 14 with 16 — both stale) - documentation/audits/usenet-2026-10-12/: both bench runs, both box steps and verdicts, the nine defects with their measurements, what a missing provider left unmeasured, the teardown - R-988 closed (moved to CLOSED-ITEMS in this commit); R-989 filed: the drill-catalog route of 09 §6.5 cannot be followed — that repo is private and no box has catalog credentials, so the templates were staged in the box's own catalog cache instead - walk.app_env: a SECRET field in app.yaml is stored ENC:, so that helper returns ciphertext — the docstring says so now, measured the hard way - STATUS.md and REPORT-usenet-2026-10-12.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
97 lines
5.2 KiB
Python
97 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Teardown after the Usenet campaign. Three layers, each verified, and the control for the catalog
|
|
layer comes from a different channel than the controller's own verdict (R-286).
|
|
|
|
What this campaign changed on 9202 and therefore has to put back:
|
|
1. the two apps are installed -> removed THROUGH THE PRODUCT, volumes and drive folders
|
|
2. two templates are STAGED in the cache -> deleted from the cache AND from /opt/docker/stacks
|
|
3. the sync interval was raised to 12h -> back to 15m
|
|
4. the catalog pointer -> already the live one (the staging needed it); verified
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
os.environ.setdefault("SC", "/tmp/usenet-walk")
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/pg-last-six-2026-09-30/tools")
|
|
import walk as w # noqa: E402
|
|
|
|
D = "/opt/docker/felhom-controller"
|
|
CACHE = D + "/data/catalog-cache/templates"
|
|
APPS = ("sabnzbd", "nzbget")
|
|
OUT = {}
|
|
|
|
w.login()
|
|
|
|
# ---------------------------------------------------------------- 1. the apps, through the product
|
|
for app in APPS:
|
|
st = w.stack(app)
|
|
if not st.get("deployed"):
|
|
w.say("[X] %s: not deployed" % app)
|
|
OUT[app] = {"was_deployed": False}
|
|
continue
|
|
code = w.remove(app)
|
|
OUT[app] = {"was_deployed": True, "remove_code": code,
|
|
"deployed_after": w.stack(app).get("deployed")}
|
|
|
|
# ---------------------------------------------------------------- 2. the staged templates
|
|
before = w.guest("docker exec felhom-controller sh -c 'ls %s | wc -l'" % CACHE).strip()
|
|
for app in APPS:
|
|
w.guest("docker exec felhom-controller sh -c 'rm -rf %s/%s'" % (CACHE, app))
|
|
w.guest("rm -rf /opt/docker/stacks/%s" % app)
|
|
after = w.guest("docker exec felhom-controller sh -c 'ls %s | wc -l'" % CACHE).strip()
|
|
left = w.guest("docker exec felhom-controller sh -c 'ls %s | grep -E \"sab|nzb\" | tr \"\\n\" \" \"'" % CACHE).strip()
|
|
stacks_left = w.guest("ls -d /opt/docker/stacks/sabnzbd /opt/docker/stacks/nzbget 2>/dev/null").strip()
|
|
OUT["staging"] = {"cache_before": before, "cache_after": after,
|
|
"staged_names_left": left or "(none)", "stack_dirs_left": stacks_left or "(none)"}
|
|
w.say("[S] cache %s -> %s templates; staged names left: %r; stack dirs left: %r"
|
|
% (before, after, left, stacks_left))
|
|
|
|
# ---------------------------------------------------------------- 3. the sync interval
|
|
cur = w.guest("docker exec felhom-controller sh -c 'grep sync_interval %s/controller.yaml'" % D).strip()
|
|
w.guest("docker exec felhom-controller sh -c \"sed -i 's|sync_interval: .*|sync_interval: 15m|' %s/controller.yaml\"" % D)
|
|
w.guest("docker restart felhom-controller")
|
|
time.sleep(25)
|
|
w.login()
|
|
now = w.guest("docker exec felhom-controller sh -c 'grep sync_interval %s/controller.yaml'" % D).strip()
|
|
OUT["sync_interval"] = {"before": cur, "after": now}
|
|
w.say("[I] sync interval %r -> %r" % (cur, now))
|
|
|
|
# ---------------------------------------------------------------- 4. the catalog pointer, from a different channel
|
|
repo = w.guest("docker exec felhom-controller sh -c 'grep repo_url %s/controller.yaml'" % D).strip()
|
|
w.sync_rescan()
|
|
time.sleep(10)
|
|
import subprocess
|
|
CAT = "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu"
|
|
subprocess.run(["git", "-C", CAT, "fetch", "-q", "origin"], capture_output=True)
|
|
checks = {}
|
|
for app in ("sabnzbd", "nzbget", "grocy"):
|
|
pushed = subprocess.run(["bash", "-c",
|
|
"git -C %s show origin/main:templates/%s/.felhom.yml | sha256sum | cut -c1-16" % (CAT, app)],
|
|
capture_output=True, text=True).stdout.strip()
|
|
onbox = w.guest("docker exec felhom-controller sh -c 'sha256sum %s/%s/.felhom.yml 2>/dev/null'" % (CACHE, app))
|
|
onbox = (onbox.split() or [""])[0][:16]
|
|
checks[app] = {"published": pushed, "on_the_box": onbox, "same": pushed == onbox}
|
|
w.say("[C] %-9s published=%s box=%s %s" % (app, pushed, onbox or "(absent)",
|
|
"SAME" if pushed == onbox else "DIFFERENT"))
|
|
decoy = subprocess.run(["bash", "-c", "git -C %s show origin/main:README.md | sha256sum | cut -c1-16" % CAT],
|
|
capture_output=True, text=True).stdout.strip()
|
|
checks["decoy_readme_vs_sabnzbd_template"] = {"same": decoy == checks["sabnzbd"]["on_the_box"]}
|
|
OUT["catalog"] = {"repo_url": repo, "templates_in_cache": w.guest(
|
|
"docker exec felhom-controller sh -c 'ls %s | wc -l'" % CACHE).strip(), "sha_checks": checks}
|
|
|
|
# ---------------------------------------------------------------- what is left running
|
|
OUT["containers"] = w.guest("docker ps --format '{{.Names}}' | sort | tr '\\n' ' '").strip()
|
|
OUT["volumes_matching"] = w.guest("docker volume ls --format '{{.Name}}' | grep -E 'sab|nzb' | tr '\\n' ' '").strip()
|
|
OUT["drive_folders"] = w.guest("ls -d /mnt/felhom-drives/scratch_hdd/userdata/media/usenet "
|
|
"/mnt/felhom-drives/scratch_hdd/userdata/media/nzbget 2>/dev/null").strip() or "(gone)"
|
|
w.say("[R] containers: " + OUT["containers"])
|
|
w.say("[R] volumes matching: " + (OUT["volumes_matching"] or "(none)"))
|
|
w.say("[R] drive folders: " + OUT["drive_folders"])
|
|
|
|
p = os.path.join(os.environ["SC"], "teardown-usenet.json")
|
|
json.dump(OUT, open(p, "w"), indent=2, ensure_ascii=False)
|
|
print("WROTE " + p)
|