Release run 2026-10-26: hub CHANGELOG names v0.170.0 (R-1254) for the build; the run's evidence folder (fix A/B red-proofs, probe)
gates / gates (push) Successful in 13m10s
gates / gates (push) Successful in 13m10s
This commit is contained in:
1 parent
9647f1d158
commit
1d4abca54a
6 files changed
+193
-1
No files matched your search
@@ -0,0 +1,13 @@
|
||||
# Fix A red-proof: the first running sample counts as started (= e8d2151). 2026-10-25T10:50:56Z
|
||||
stable_start_test.go:70: a flapping app must keep its way back, found []
|
||||
--- FAIL: TestFixA_AFlappingAppKeepsTheCopy (0.60s)
|
||||
--- PASS: TestFixA_AStableAppReleasesTheCopyAfterTheWindow (0.00s)
|
||||
stable_start_test.go:106: a healthcheck still starting must keep the copy, found []
|
||||
--- FAIL: TestFixA_AHealthcheckStillStartingKeepsTheCopy (0.50s)
|
||||
stable_start_test.go:121: a new restore owns the app: the watch ends and the copy stays, found []
|
||||
--- FAIL: TestFixA_ANewRestoreDuringTheWatchLeavesTheCopy (0.70s)
|
||||
stable_start_test.go:142: a flapping app must never turn the record into a success
|
||||
--- FAIL: TestFixA_TheLateStartWatcherNeedsAStableStart (0.01s)
|
||||
FAIL
|
||||
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 1.831s
|
||||
FAIL
|
||||
@@ -0,0 +1,8 @@
|
||||
# Fix A (own-restore race) red-proof: the watch ends on ANY running restore of the app, its own included. 2026-10-25T11:35:34Z
|
||||
restore_verdict_test.go:103: a late start must turn the record into a success: &{Op:restore Stack:app OK:false Message:A(z) app: az adatokat visszatöltöttük, de az alkalmazás még nem indult el. Várj pár percet, majd nyisd meg újra. Ha így marad, szólj nekünk. FinishedAt:2026-10-25 12:35:38.16229144 +0100 CET m=+0.067010370 Interrupted:false PutBack:false PutBackWaiting:false NotStarted:true}
|
||||
--- FAIL: TestR1270_ALateStartTurnsTheRecordIntoASuccess (2.06s)
|
||||
stable_start_test.go:92: a stable app releases the copy after the window, found [/tmp/TestFixA_AStableAppReleasesTheCopyAfterTheWindow3012085805/001/drive/backups/restore-undo/app/20261025T113540Z/app_data.tar]
|
||||
--- FAIL: TestFixA_AStableAppReleasesTheCopyAfterTheWindow (3.01s)
|
||||
FAIL
|
||||
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 5.086s
|
||||
FAIL
|
||||
@@ -0,0 +1,11 @@
|
||||
# Fix A (page verdict) red-proof: the first verdict is the one-sample health wait alone (= e8d2151). 2026-10-25T11:47:36Z
|
||||
stable_start_test.go:71: a running → restarting → running app is „not started yet" on the page too, got <nil>
|
||||
--- FAIL: TestFixA_AFlappingAppKeepsTheCopy (0.00s)
|
||||
--- PASS: TestFixA_AStableAppReleasesTheCopyAfterTheWindow (0.16s)
|
||||
stable_start_test.go:114: a healthcheck still starting is not started, got <nil>
|
||||
--- FAIL: TestFixA_AHealthcheckStillStartingKeepsTheCopy (0.00s)
|
||||
--- PASS: TestFixA_ANewRestoreDuringTheWatchLeavesTheCopy (0.70s)
|
||||
--- PASS: TestFixA_TheLateStartWatcherNeedsAStableStart (0.40s)
|
||||
FAIL
|
||||
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 1.272s
|
||||
FAIL
|
||||
@@ -0,0 +1,8 @@
|
||||
# Fix B red-proof: the import does not ask the D48 helper (= e8d2151). 2026-10-25T10:52:50Z
|
||||
r1279_pull_first_test.go:60: the helper must be asked about the BUNDLE's definition (the one that will run), got ""
|
||||
--- FAIL: TestR1279_AnUnpullableImageRefusesTheImportBeforeAnythingIsRemoved (0.05s)
|
||||
r1279_pull_first_test.go:89: the pull must come BEFORE the running app's volumes are removed: removed at ask -1, after 1
|
||||
--- FAIL: TestR1279_APullableImageIsAskedFirstAndTheImportGoesOn (0.05s)
|
||||
FAIL
|
||||
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/appexport 0.110s
|
||||
FAIL
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""fixprobe.py — fixes A and B live on a throwaway Vikunja this run installs (never a household's data).
|
||||
|
||||
A (the undo copy only after a stable start): the app's unit edited to run `busybox` with `sleep 20; exit 1` (restart
|
||||
policy on, no healthcheck — it reads „running" for 20 s, then crashes and restarts) → „Visszaállítás": the page says
|
||||
not started yet, the undo copy stays, the operator line is logged once; the original unit back → a normal restore:
|
||||
the copy is there right after, gone after the 3-minute window.
|
||||
B (the `.fab` import downloads first): the app exported through the page's API, a COPY of the bundle under its own name
|
||||
with the image tag edited to one that does not exist → import: refused, the containers never restarted (StartedAt), the
|
||||
volumes untouched (their CreatedAt), the item still there. The test bundle and the export are removed after.
|
||||
The unit / bundle edits are the only hand-made changes (`pct exec`, files only).
|
||||
|
||||
usage: SC=… SWEEP_EV=… TARGET=9202 DRIVE_ROOT=… python3 fixprobe.py
|
||||
"""
|
||||
import json, os, re, sys, time
|
||||
|
||||
TOOLS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../catalog-sweep-2026-10-24/tools")
|
||||
sys.path.insert(0, TOOLS)
|
||||
import sweep as sw # noqa: E402
|
||||
|
||||
w, fxm = sw.w, sw.fxm
|
||||
APP, REPO = "vikunja", "vikunja/vikunja"
|
||||
NS = "/mnt/sys_drive/felhom-data"
|
||||
UNIT = f"{NS}/backups/primary/{APP}"
|
||||
ORIG = f"{NS}/backups/fix-a-test-original/{APP}"
|
||||
UNDO = f"{NS}/backups/restore-undo/{APP}"
|
||||
DRIVE = os.environ.get("DRIVE_ROOT", "/mnt/felhom-drives/scratch_hdd")
|
||||
|
||||
|
||||
def started():
|
||||
return sw.g(f"for c in $(docker ps -a --filter label=com.docker.compose.project={APP} -q); do "
|
||||
"docker inspect -f '{{.Name}} {{.State.StartedAt}} {{.RestartCount}} {{.State.Status}}' $c; done").strip()
|
||||
|
||||
|
||||
def vols():
|
||||
return sw.g(f"for v in $(docker volume ls -q --filter label=com.docker.compose.project={APP}); do "
|
||||
"docker volume inspect -f '{{.Name}} {{.CreatedAt}}' $v; done").strip()
|
||||
|
||||
|
||||
def sums(p):
|
||||
return sw.g(f"cd {p} 2>/dev/null && find . -type f | sort | xargs -r sha256sum")
|
||||
|
||||
|
||||
def main():
|
||||
d = os.path.join(sw.EV, "fixprobe")
|
||||
os.makedirs(d, exist_ok=True)
|
||||
log = open(os.path.join(d, "walk.log"), "a")
|
||||
|
||||
def say(*a):
|
||||
line = time.strftime("%H:%M:%SZ", time.gmtime()) + " " + " ".join(str(x) for x in a)
|
||||
print(line, flush=True)
|
||||
log.write(sw.scrub(line) + "\n")
|
||||
log.flush()
|
||||
|
||||
w.say = say
|
||||
fx = fxm.FIXTURES[APP]
|
||||
sub = getattr(fx, "sub", APP)
|
||||
rec = {}
|
||||
t_start = int(time.time())
|
||||
w.login()
|
||||
test_fab = ""
|
||||
try:
|
||||
rec["install"] = w.deploy(APP, sub) and w.wait_app(sub, "/", tries=90, delay=5)
|
||||
tok = fx.seed(w, sub, say)
|
||||
rec["seed"] = bool(tok)
|
||||
sw.backup_now(rec, say)
|
||||
sw.g(f"rm -rf {ORIG}; mkdir -p {NS}/backups/fix-a-test-original && cp -a {UNIT} {ORIG}")
|
||||
s0 = sums(UNIT)
|
||||
|
||||
# --- A: an app that runs 20 s, crashes, restarts
|
||||
out = sw.g(f"cd {UNIT} && sed -i -E 's#{REPO}:[^\"[:space:]]*#busybox:1.36.1#g' compose/docker-compose.yml manifest.json data-stamps.json && "
|
||||
"sed -i -E 's#^( image: busybox:1.36.1)$#\\1\\n command: [\"sh\", \"-c\", \"sleep 20; exit 1\"]#' compose/docker-compose.yml && "
|
||||
"grep -n 'image:\\|command:' compose/docker-compose.yml")
|
||||
say(" [A] the unit now runs: " + out.replace("\n", " | "))
|
||||
sw.restore_unit(APP, rec, say)
|
||||
rec["a_restore"] = rec.pop("restore")
|
||||
say(" [A] restore: " + rec["a_restore"].get("status", "")[:400])
|
||||
rec["a_containers"] = started()
|
||||
rec["a_undo"] = sw.g(f"ls -1 {UNDO} 2>&1").strip()
|
||||
say(f" [A] containers: {rec['a_containers']} | kept undo copy: {rec['a_undo']}")
|
||||
time.sleep(60)
|
||||
rec["a_containers_60s_later"] = started()
|
||||
say(f" [A] 60 s later: {rec['a_containers_60s_later']}")
|
||||
# --- A: the original unit back, a normal restore, the copy released after the window
|
||||
sw.g(f"rm -rf {UNIT} && cp -a {ORIG} {UNIT}")
|
||||
rec["original_back"] = sums(UNIT) == s0
|
||||
sw.g(f"rm -rf {UNDO}") # the copy the not-started run kept (mine), so the next count is clean
|
||||
sw.restore_unit(APP, rec, say)
|
||||
rec["a_normal_restore"] = rec.pop("restore")
|
||||
say(" [A] normal restore: " + rec["a_normal_restore"].get("status", "")[:300])
|
||||
rec["a_undo_right_after"] = sw.g(f"ls -1 {UNDO} 2>&1").strip()
|
||||
say(f" [A] the copy right after the success: {rec['a_undo_right_after']}")
|
||||
for _ in range(30):
|
||||
time.sleep(10)
|
||||
left = sw.g(f"ls -1 {UNDO} 2>&1").strip()
|
||||
if "No such file" in left or not left:
|
||||
break
|
||||
rec["a_undo_after_window"] = sw.g(f"ls -1 {UNDO} 2>&1").strip()
|
||||
rec["a_item_after"] = bool(tok) and bool(fx.verify(w, sub, tok, say))
|
||||
say(f" [A] after the window: {rec['a_undo_after_window'] or '(gone)'}; item {rec['a_item_after']}")
|
||||
|
||||
# --- B: export, a copy with a missing tag, import
|
||||
c, r = w.ctl("POST", "/api/export/start", {"stack_name": APP, "dest_drive": DRIVE, "password": "", "stop_app": False})
|
||||
say(f" [B] export start -> {c} {str(r)[:160]}")
|
||||
for _ in range(60):
|
||||
time.sleep(5)
|
||||
c, st = w.ctl("GET", "/api/export/status")
|
||||
stt = json.dumps(st, ensure_ascii=False)
|
||||
if '"running": false' in stt or '"state": "done"' in stt or '"done"' in stt:
|
||||
break
|
||||
c, b = w.ctl("GET", "/api/export/bundles")
|
||||
paths = re.findall(r'"path": "([^"]+vikunja[^"]*\.fab)"', json.dumps(b))
|
||||
rec["b_bundle"] = paths[:1]
|
||||
if not paths:
|
||||
say(" [B] no bundle found: " + json.dumps(b)[:300])
|
||||
return
|
||||
src = paths[0]
|
||||
test_fab = os.path.join(os.path.dirname(src), "vikunja-r1279-test.fab")
|
||||
out = sw.g(f"set -e; t=$(mktemp -d); cd $t; f={src}; if gzip -t $f 2>/dev/null; then tar xzf $f; z=z; else tar xf $f; z=; fi; "
|
||||
f"sed -i -E 's#{REPO}:[^\"[:space:]]*#{REPO}:0.0.0-felhom-r1279-missing#g' config/docker-compose.yml; "
|
||||
f"grep -n 'image:' config/docker-compose.yml; tar c${{z}}f {test_fab} .; cd /; rm -rf $t; ls -la {test_fab}")
|
||||
say(" [B] the test bundle: " + out.replace("\n", " | "))
|
||||
before_c, before_v = started(), vols()
|
||||
c, r = w.ctl("POST", "/api/export/import", {"path": test_fab, "password": ""})
|
||||
say(f" [B] import -> {c} {str(r)[:160]}")
|
||||
last = None
|
||||
for _ in range(60):
|
||||
time.sleep(5)
|
||||
c, st = w.ctl("GET", "/api/export/import/status")
|
||||
last = json.dumps(st, ensure_ascii=False)
|
||||
if "nem lehet letölteni" in last or '"error"' in last and '"running": false' in last or '"done"' in last:
|
||||
break
|
||||
rec["b_import_status"] = last[:900] if last else ""
|
||||
after_c, after_v = started(), vols()
|
||||
rec["b_containers_before"], rec["b_containers_after"] = before_c, after_c
|
||||
rec["b_volumes_before"], rec["b_volumes_after"] = before_v, after_v
|
||||
rec["b_untouched"] = bool(before_c) and before_c == after_c and before_v == after_v
|
||||
rec["b_item_still_there"] = bool(tok) and bool(fx.verify(w, sub, tok, say))
|
||||
say(f" [B] import status: {rec['b_import_status'][:400]}")
|
||||
say(f" [B] containers and volumes untouched: {rec['b_untouched']}; item {rec['b_item_still_there']}")
|
||||
finally:
|
||||
rec["controller_lines"] = sw.scrub(sw.g(
|
||||
f"docker logs --since {t_start} felhom-controller 2>&1 | grep -E '{APP}' | grep -iE 'R-1270|R-1279|D48|stabl|not start|released|REFUSED|Import' | cut -c1-320"))
|
||||
sw.g(f"rm -f {test_fab} {os.path.dirname(test_fab)}/vikunja*.fab" if test_fab else "true")
|
||||
sw.remove(APP, keep=False, say=say)
|
||||
rec["after_remove"] = sw.leftovers(APP, sub)
|
||||
rec["cleanup"] = sw.g(f"rm -rf {ORIG} {UNDO}; rmdir {NS}/backups/fix-a-test-original 2>/dev/null; ls -d {ORIG} {UNDO} 2>&1").strip()
|
||||
json.dump(rec, open(os.path.join(d, "record.json"), "w"), ensure_ascii=False, indent=1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
## Unreleased (next: v0.170.0) — the escrow package in one transaction (R-1254) (2026-10-24 night) — committed, NOT built for release, NOT deployed
|
||||
## v0.170.0 (2026-10-25) — the escrow package in one transaction (R-1254) — built for release in the attended release run; deployed only on the operator's yes
|
||||
|
||||
- **The new escrow key, its sealed-password hash and the identity blob are stored in ONE transaction**
|
||||
(`Store.SaveHostEscrowWithIdentity`; `SaveHostEscrow` is it with no identity blob). Until now the escrow PUT committed
|
||||
|
||||
Reference in new issue
Block a user