Repository navigation
test(cloud-lab): run-scoped lab runner + shared-host runbook - #557
Conversation
|
Technical review (evidence pass) Reviewed at 415986a (single commit, MERGEABLE, 0 commits behind What I actually ran (all on this host, today)Another session's
Against the incident classes this PR targets: (a) Must-fix before mergeCHANGELOG links the wrong PR. The entry at CHANGELOG.md:184-191 cites Substantive gaps (follow-ups, ideally in this PR)1. 2. 3. Smaller notes
PR hygiene is clean: single commit with an accurate subject/body, no attribution footers, purely additive diff with no drive-by changes, and the PR body's claims each check out against the diff — including the verified-live table, which I independently reproduced (up/smoke/tap/down/reap) on this host. Overlap with open lane PRs (#522, #535, #425) is complementary, with the service-list coupling noted above. No security concerns beyond the tap exposure noted in finding 2. Disposition: land-with-followups — fix the CHANGELOG PR number before merge (must); |
|
First real mission found a bug: taps lacked |
57864b5 to
a15be70
Compare
|
Review — PR #557 at Reviewed at head The branch is not what this PR says it is.
How to fix (pick one):
To be clear about the code that is here: the wallet-recover series itself looks sound — PENDING is treated as unknown, not spent ( |
felixfelix-bot
left a comment
There was a problem hiding this comment.
Review — PR #557 at a15be70f — CHANGES REQUESTED
Reviewed at head a15be70ff3b414b0139d12f884e9c3c06ad494db (verified live via gh pr view 557 --json headRefOid), diffed against main (2796d96c): 23 files, +955/−2 — all of it src/tollwallet, src/cli, src/merchant wallet-recover code plus CHANGELOG/docs. Not one byte of cloud-lab test infrastructure.
The branch is not what this PR says it is.
-
[BLOCK] The advertised work is gone from the branch. This PR opened 2026-09-23 22:13Z as the run-scoped lab runner at
415986ae(+348/−0 across 4 files:tests/cloud-lab/lab.sh223 lines,tests/cloud-lab/RUNBOOK.md106 lines), then gained57864b5b(the tapforkfix) as its tip. At 2026-09-24 20:40Zfeat/cloud-lab-runnerwas force-pushed and neither commit is reachable from the branch anymore — neither file exists in the tree at head (git ls-tree -r a15be70f -- tests/cloud-lab/lab.sh→ empty; the PR's file list has no cloud-lab entries at all). The title, the body, and the "Verified live" table all describe code that is not in this diff. -
[BLOCK] The new head is a duplicate of open PR #549.
a15be70fis the head offeat/wallet-recover— PR #549 (feat(cli): wallet recover — NUT-07 liveness check for journaled drain tokens), still open at the identical SHA, with the identical 9 commits. The CHANGELOG hunk in this very diff links the entry to #549 (CHANGELOG.md:27, :35). Two open PRs at the same head is a merge hazard: whichever merges first lands these bytes and leaves the other carrying a stale title and body. The wallet-recover work has its home and its discussion trail on #549 — including your self-review passes that fixed PENDING-as-spent and bounded checkstate against wedged mints (that head has since moved toa15be70f, so #549 will need a re-review pass at the new tip regardless). -
[INFO] No CI has run on this head — or anywhere in this repo since 2026-08-27.
gh pr checks 557→ "no checks reported on the 'feat/cloud-lab-runner' branch"; the head commit has 0 check-runs, and the workflow history shows this is repo-wide, not specific to this PR (lasttest.ymlrun 2026-08-27; since 2026-09-01 only queued Dependency Graph runs). No CI ever ran on the original lab-runner commits either — your live evidence pass on #557 remains the only validation they got.
How to fix (pick one):
- Restore the branch — the lab-runner work is not lost, both commits are still in GitHub's object store. The pre-force-push tip was
57864b5b(includes the tapforkfix):This PR then becomes what its title says again, and I'll review the runner properly. For what it's worth, the timing —git fetch origin 57864b5b5aa8de397c027e6b48a8de3b537ee9f3 git push origin +57864b5b:feat/cloud-lab-runnerfeat/wallet-recover,pr/wallet-recover, andfeat/cloud-lab-runnerall moving toa15be70fwithin ~2 minutes — reads like a mis-targeted push of the wallet branch onto the cloud-lab branch name, not a deliberate retarget. - Or close this PR and let the wallet-recover bytes live only in #549.
To be clear about the code that is here: the wallet-recover series itself looks sound — PENDING is treated as unknown, not spent (src/tollwallet/checkstate.go:78), checkstate is bounded against wedged mints (checkstate.go:27), and partial mint answers are rejected (checkstate.go:72). It will get its full review on #549. Carrying the same head on two PRs adds nothing but confusion.
|
Technical review (evidence pass) — PR head What the head actually is. What happened, and the fix. The comment history pins it down: on 09-24 the branch was at git push --force origin 57864b5b5aa8de397c027e6b48a8de3b537ee9f3:feat/cloud-lab-runnerThat resurrects the real tip (lab.sh at 415986ae, RUNBOOK.md at 415986ae) — though note it forks from Consequences worth naming. Every claim in the PR body fails verification against this head — no One gentle observation: the runbook's own lesson list names "wrong-worktree command execution" as a real incident on this host. The clobbering of this branch is that failure mode wearing a different hat — which is as good an argument as any for the runbook itself. Disposition: request-changes. Blocking: restore |
57864b5 to
2f30927
Compare
|
Branch restored and rebased (post-review maintenance). This branch had been force-pushed over with #549's wallet-recover series; the runner survived only as dangling commits. Restored from Still outstanding from the original |
|
Review — PR #557 at Reviewed at head The design is right, and the isolation core holds: one run = one compose project + container names + allocated [BLOCK] [RISK] [RISK]
[RISK] [RISK] [NIT] [NIT] [NIT] [NIT] [NIT] [INFO] Branch state. Head is 9 commits behind Disposition: CHANGES REQUESTED — the wrong-PR CHANGELOG link and the profile/ |
lab.sh wraps the whole lab lifecycle in one run-scoped lease (labgrid's acquire/use/release adapted to a single docker host): per-checkout run id, own compose project, container names and allocated subnet, no host ports, up --wait, managed TCP taps with pidfiles, deterministic down -v, and a bounded label-based reap that reclaims a run killed any way — including SIGKILL. RUNBOOK.md documents the patterns and the twelve operating lessons that motivated them, each learned the hard way on this host. Verified live: full lifecycle (up → smoke 6/6 → tap 200 → down) leaves zero containers, networks, volumes, taps or state; five crashed runs fully reclaimed by reap; the age-based janitor reaps stale runs and leaves fresh ones alone.
…erved Caught by first real use: without fork, socat serves exactly one connection and exits, so the second probe through the tap got connection-refused. Found while running the #497 mint-replay experiment through the runner.
…heir listener — the 415986a review round upstream-ext was missing from the rename/remap loop, so the one service behind profile "external-mints" kept its pinned 172.28.0.12 address (outside the run's allocated /24) and its published 21214 host port — the exact collision class this runner exists to kill. It now carries a static .12 in the run's subnet and its ports entry is reset like every other service. Taps bound on every host interface and reported success without checking that socat had actually bound: with the port already taken, socat died instantly, the pidfile pointed at a dead pid, and the next probe silently reached whatever neighbour owned the port — the wrong-target confusion the RUNBOOK is about. Taps are now loopback-only (they front unauthenticated mints and portals) and verify, bounded to ~1s, that a listener owned by their own pid exists, dying loudly otherwise. Plus the review's one-liners: usage() no longer spills script body past the header (2,33 not 2,40); the run id is lowercased to match compose's project-name normalization so reap's ${PROJECT}_ volume grep cannot diverge on an uppercase branch/user; and reap --all skips runs whose CreatedAt fails to parse instead of force-removing them. CHANGELOG: the runner entry now lives under [Unreleased]'s Changed / Internal (it had landed at the tail of the section) and links
2f30927 to
4ef118c
Compare
|
Review-round fixes applied (the 415986a round's must-fix + two substantive gaps + its explicit one-liners), rebased onto current Must-fix — CHANGELOG linked the wrong PR. The runner entry now links Gap 1 — Gap 2 — The round's one-liners: Verification: Not addressed (out of this round's scope, for the next pass): the 2026-10-01 review's lease-record bootstrap issue, profile-aware service derivation beyond |
…n, derived service set — the 2f30927 round The lease record was never written: generate_override's subnet append creates $META, so init_state's file-existence guard placed after it always skipped — the run state held only the subnet line. The record (run_id/project/cwd/started/user, then the subnet append) is now written before the override is generated. The subnet allocator decided at /24 granularity from a three-octet grep, so a live 172.31.64.0/23 reported only 172.31.64 and the allocator handed out 172.31.65.0/24 inside it. Allocation now parses the full subnet/len and blocks every third octet a live network covers (alignment computed from the prefix — the true base of 172.31.65.0/23 is 172.31.64.0; a /16 blocks the whole candidate range and fails loudly instead of overlapping). The override's service set is derived from the base compose file itself (docker compose --profile '*' config), so a new lane service is covered without editing lab.sh — both review rounds asked for the derivation. Static IPs mirror each service's base-file octet; a service the base file leaves to compose keeps that freedom. Lanes are selected through COMPOSE_PROFILES (compose reads it natively; --profile is not a lab.sh argument — now documented in the header and RUNBOOK). RUNBOOK gains the host prerequisites (compose v2.24+ for !reset, socat, ss, date -d) and states the deliberate docker-compose.override.yml divergence between lab.sh and the lane scripts.
…677) #674 mapped the test estate and pointed at cloud-lab/RUNBOOK.md as being on the runner branch, with the three-point summary inline until it landed. #557 merged the runbook into main just before that, so the parenthetical is now false and the map under-sells what it links to. Link it directly and keep the summary. No CHANGELOG entry: this corrects text that is already in main in an unreleased section, and nothing user-visible changes. Co-authored-by: Felix <felix@opentollgate.org>
What
Adapts the patterns that make shared lab infrastructure survivable — labgrid's acquire/use/release model, per-run ephemeral compose stacks, preemption-safe CI runners — to our single-docker-host cloud-lab, and writes down the twelve lessons that motivated each rule.
tests/cloud-lab/lab.sh— the run-scoped runnerOne lab = one run = one lease. A single
upcreates, and nothing else shares: a compose projectcl-<run-id>(stable per checkout: user + branch slug + worktree-path hash), container names, an allocated172.31.64–250/24subnet (inspected against live networks), its own images, and zero host port bindings. Plus:up --wait --wait-timeout 300— kills the boot racetap <port> <service>:<port>— managed TCP taps (pidfile + log in run state; killed by recorded pid, never by pattern)down— taps, containers, network, volumes, state, all of itreap/reap --all— bounded, label-based (cl.lab.run=<id>) reclaim after any death, including SIGKILL;--allis a janitor withREAP_MINUTEStests/cloud-lab/RUNBOOK.md— the whyThe pattern/source table (labgrid, ephemeral-CI stacks, preemption-safe runners) and the lessons catalog — each one a real incident on this host: unstable run ids minting unreachable runs, stale shared
:latestimages probed as new code, globalcontainer_namecollisions, the one-lab-per-host pinned subnet, pattern-matchedpkillreaping a neighbor's taps, relative-$0breakage after self-locatingcd,nohupbatteries dying with their spawning shell, vendored trees breakinggofmt, wrong-worktree command execution, shell-eaten backticks ingh --body, and unbounded teardown wedging callers.Verified live (this branch, this host, beside another session's lab)
upruntest_smoke_payment.py6 passedtap 18885 mint:8085downreapon 5 crashed runsreap --all(fresh run, default age)reap --all(REAP_MINUTES=0)make go-batteryNotes
COMPOSE_PROJECT_NAME/.env guidance stays valid for simple cases;lab.shis the no-footgun path for anything concurrent.