gates / gates (push) Successful in 13m49s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
87 lines
4.6 KiB
Python
87 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""repoint.py mirror|drill|restore|status — point a box (scratch 9202; copied from audits/burndown-3-2026-10-17/tools/ for the D39 security lane, 2026-10-23) at the drill catalog WITHOUT a credential (R-992).
|
|
|
|
WHY A LOCAL MIRROR. `09` §6.5's route needs a catalog credential on the box: the drill repo is private (R-992). This
|
|
session could not mint a READ-ONLY one — the Gitea credential on DooPlex is itself a token, and Gitea answers 403 to a
|
|
token asking to list or create tokens (measured 2026-10-14) — and the night of 2026-10-06 put the admin WRITE token
|
|
into the box's controller.yaml, which this task forbids. The cache-staging workaround of 2026-10-12 does not hold for a
|
|
test that lasts days: every sync (and every controller start) re-applies the live catalog over the staged templates
|
|
and over the installed apps' compose files (Syncer.copyTemplates, no deployed check).
|
|
|
|
So the drill catalog is MIRRORED onto the box: a bare git repository inside the controller's own data volume, and
|
|
`git.repo_url: file:///opt/docker/felhom-controller/drill-catalog.git`. The controller's sync runs exactly as for a
|
|
remote (clone/fetch/reset, the R-615 re-clone on a changed URL); no secret is on the box. DooPlex updates the mirror
|
|
from the drill repo with a git bundle (`mirror`). The drill repo on Gitea stays the record.
|
|
|
|
SC=~/.felhom-tester1 TARGET=tester-1 python3 repoint.py mirror # (re)load the mirror from the drill repo main
|
|
... repoint.py drill # save controller.yaml (once), set repo_url to the mirror, restart, sync, rescan
|
|
... repoint.py restore # put the saved controller.yaml back (byte-identical), restart, sync, rescan
|
|
... repoint.py status
|
|
"""
|
|
import os, subprocess, sys, time
|
|
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
|
import box_walk as w
|
|
|
|
DRILL = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
|
|
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data" # guest path of the controller's data volume
|
|
CVOL = "/opt/docker/felhom-controller" # the same, inside the controller container
|
|
SAVE = f"{VOL}/controller.yaml.pre-seclane1023"
|
|
MIRROR_URL = f"file://{CVOL}/drill-catalog.git"
|
|
|
|
|
|
def status():
|
|
print(w.guest(f"""grep -nE 'repo_url|sync_interval' {VOL}/controller.yaml; ls -l {SAVE} 2>&1 | tail -1
|
|
docker exec felhom-controller sh -c 'git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main 2>&1;
|
|
git -C {CVOL}/data/catalog-cache log -1 --format="cache HEAD %h %s" 2>&1; git -C {CVOL}/data/catalog-cache remote get-url origin'"""))
|
|
|
|
|
|
def mirror():
|
|
b = "/tmp/drill-seclane1023.bundle"
|
|
subprocess.run(["git", "-C", DRILL, "bundle", "create", b, "main"], check=True, capture_output=True)
|
|
head = subprocess.run(["git", "-C", DRILL, "rev-parse", "--short", "main"], capture_output=True, text=True).stdout.strip()
|
|
ssh = w.ssh_args()
|
|
# bundle -> PVE host -> guest -> controller volume; the bare repo is made and fetched BY the controller's own user
|
|
subprocess.run(["scp", "-q", "-o", "StrictHostKeyChecking=accept-new"] + (["-J", w._T["jump"]] if w._T["jump"] else [])
|
|
+ [b, f"{w._T['ssh']}:{b}"], check=True)
|
|
subprocess.run(ssh + [f"pct push {w.GUEST} {b} {VOL}/drill-seclane1023.bundle && rm -f {b}"], check=True)
|
|
print(w.guest(f"""set -e
|
|
docker exec felhom-controller sh -c 'test -d {CVOL}/drill-catalog.git || git init -q --bare -b main {CVOL}/drill-catalog.git
|
|
git -C {CVOL}/drill-catalog.git fetch -q {CVOL}/drill-seclane1023.bundle +main:main && rm -f {CVOL}/drill-seclane1023.bundle
|
|
git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main'"""))
|
|
print("drill repo main:", head)
|
|
os.remove(b)
|
|
|
|
|
|
def restart_sync():
|
|
w.guest("docker restart felhom-controller >/dev/null")
|
|
for _ in range(60):
|
|
time.sleep(3)
|
|
try:
|
|
w.login()
|
|
break
|
|
except SystemExit:
|
|
continue
|
|
w.sync_rescan()
|
|
time.sleep(3)
|
|
print(w.guest("docker logs --since 3m felhom-controller 2>&1 | grep -E '\\[sync\\]' | grep -v DEBUG | tail -8"))
|
|
|
|
|
|
def drill():
|
|
print(w.guest(f"""set -e
|
|
test -f {SAVE} || cp -p {VOL}/controller.yaml {SAVE}
|
|
sed -i -E 's#^(\\s+repo_url: ).*$#\\1{MIRROR_URL}#' {VOL}/controller.yaml
|
|
grep -n 'repo_url' {VOL}/controller.yaml; diff {SAVE} {VOL}/controller.yaml || true"""))
|
|
restart_sync()
|
|
status()
|
|
|
|
|
|
def restore():
|
|
print(w.guest(f"""set -e
|
|
cp -p {SAVE} {VOL}/controller.yaml
|
|
cmp {SAVE} {VOL}/controller.yaml && echo RESTORED-IDENTICAL; grep -n repo_url {VOL}/controller.yaml"""))
|
|
restart_sync()
|
|
status()
|
|
|
|
|
|
{"mirror": mirror, "drill": drill, "restore": restore, "status": status}[sys.argv[1]]()
|