Files
felhom.eu/documentation/audits/update-night-2026-09-21/repoint_drill.py
T
admin 186546d562
gates / gates (push) Successful in 27s
THE TWENTY-EIGHT: every app no drill had touched, walked in one night
All 28 walked on scratch guest 9202 against the private drill catalog. 26 deployed, 6 proven,
5 inconclusive, 14 with no upstream edge, 1 failed honestly (outline 1.9.1->1.10.1, HELD with the
right sentence), 2 undeployable - one (plant-it) by design, refused by the lifecycle gate, proven
live for the first time. Each app also got the half the update night skipped: a restore from its own
copy with the seed read back again - 21 restored, 2 correctly REFUSED per 07 6.2.

R-630 RAISED TO P1 by measurement: a stack with NO probe container does not skip verifying - it
waits out the full health timeout and HOLDS, stopping an app whose three containers read healthy.
The controller's own words: "not healthy within 5m0s (last: no probe container)".

R-633 opened: a remove sent during a restore reports success and leaves a container restarting with
a live public route. The product already refuses that clash for update and for restore, naming the
blocker; remove has no such guard.

R-634 opened: an app can be running, healthy and serving while recorded as deployed=false, and is
then unremovable. Reproducible alone on sparkyfitness; concurrency-linked on two others.

R-631 and R-632 CLOSED. Register 321 -> 323. Seven interventions, six of them my own harness -
named, with what each cost. No product code. The live catalog's image: lines are byte-identical to
the start of the night.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-22 16:00:56 +02:00

52 lines
1.8 KiB
Python

#!/usr/bin/env python3
"""Point guest 9202 at the drill catalog, or back at the live one (09 §6.5, R-615).
`git.repo_url` alone is INERT: `Syncer.gitCloneOrPull` clones only when the cache has no `.git`,
otherwise it fetches from the remote the existing clone stores. The cache directory must be removed
too, or the box goes on following the live catalog AND REPORTS SUCCESS. That is R-615, and it is why
step 3 exists.
"""
import re, sys, io
sys.path.insert(0, '.')
import walk as w
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data"
LIVE = "https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git"
DRILL_REPO = "https://gitea.dooplex.hu/admin/app-catalog-drill.git"
def creds():
for l in io.open("/home/kisfenyo/.git-credentials").read().strip().split("\n"):
m = re.match(r'https://(admin):([^@]+)@gitea\.dooplex\.hu', l)
if m:
return m.group(1), m.group(2)
raise SystemExit("no admin credential for gitea.dooplex.hu")
def repoint(to_drill):
u, t = creds() if to_drill else ("", "")
url = DRILL_REPO if to_drill else LIVE
script = f"""
set -e
test -f {VOL}/controller.yaml.pre-28 || cp {VOL}/controller.yaml {VOL}/controller.yaml.pre-28
python3 - <<'EOF'
import re
p = "{VOL}/controller.yaml"
s = open(p).read()
s = re.sub(r'(^\\s+repo_url: ).*$', r'\\g<1>{url}', s, count=1, flags=re.M)
s = re.sub(r'(^\\s+token: ).*$', r'\\g<1>"{t}"', s, count=1, flags=re.M)
s = re.sub(r'(^\\s+username: ).*$', r'\\g<1>"{u}"', s, count=1, flags=re.M)
open(p, "w").write(s)
EOF
# R-615: the cache is the thing that actually decides which remote is fetched.
rm -rf {VOL}/catalog-cache {VOL}/data/catalog-cache
docker restart felhom-controller >/dev/null
sleep 12
grep -A6 '^git:' {VOL}/controller.yaml | sed 's/token:.*/token: <redacted>/'
"""
print(w.guest(script))
if __name__ == "__main__":
repoint(sys.argv[1] == "drill")