fix(bootstrap): repin the dead AMD64 image, and guard every pin #669
Loading…
Reference in a new issue
No description provided.
Delete branch "chore/hetzner-cleanup-and-pin-fix"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cleanup of the Hetzner account turned up a live fault, so this PR fixes the fault first and adds the durable half of the cleanup.
The fault
bootstrap/nodes.envpinnedWORKER_AMD64_IMAGEto snapshot 422011494, and that snapshot no longer exists. The AMD64 emergency-rollback pool the file documents could not create a server. The surviving image for the same seanfarm45 bake is 424843910.How it died, and why depth cannot fix it. The
node-snapshot-bakeprune keeps the newest 4 images labelledseanfarm-node-image=1. A second producer sets the rate of that window: the seanfarm46 and seanfarm47 RELEASE bakes come from the pipelines repository and never writenodes.env. They pushed the promoted TAG bake out of the window, and the pruner deleted it. RaisingKEEPbuys time and closes nothing.So prune now holds a Hetzner delete-protection lock on the image promote just wrote, after clearing the lock from every other node-image snapshot. One image is locked at a time, so the window still drains, and a re-run is idempotent.
A second fault, found while proving the first
promoterewritesMASTER_IMAGEas well asWORKER_AMD64_IMAGE, and it always writes the x86 bootc snapshot it just baked. Since Make ARM the durable cluster topology the master is acax31pinned to an arm image. The next green bake will repoint the ARM control plane at an x86 snapshot.This PR does not change
promote. It makes the guard compare image architecture against the architecture the pool's own*_TYPEimplies, so the break becomes loud instead of silent. Whatpromoteshould write for an ARM master is a topology decision, not a cleanup.The guard
scripts/check-node-image-pin.shproves every*_IMAGEpin exists and matches its pool architecture. A rebuild destroys before it creates, so it runs as a preflight phase inrun-rebuild.sh, before destroy. It is non-vacuous: finding no pins is a failure.make test-node-image-pinruns the hermetic unit test — 8 cases, no token, no network.The object-store half
The bucket carried no lifecycle configuration, so vmbackup's leaked multipart uploads accumulated forever: 44 of them from one failed run on 2026-08-10, holding 13.48 GB that no object listing shows. The declared document is now
bootstrap/hetzner-bucket-lifecycle.json, converged bymake bucket-lifecycle.A script, not a Flux Job, on purpose: the rule lives on the bucket, which is Hetzner account state, so it survives
make rebuild. The MinIO ILM rules are the opposite case and are Jobs for that reason.tests/hetzner-bucket-lifecycle.shasserts the document aborts incomplete uploads and expires nothing — that bucket holds the barman archive and the VictoriaMetrics mirror, and a blanket expiry rule on the MinIOworkflowsbucket deleted the macOS SDK once.Verification
tests/check.sh— green, includingtests/no-sigpipe-readers.sh, which caught two| grep -qreaders in the new guard before they could invert a match../scripts/tests/check-node-image-pin-test.sh— 8 passed../scripts/check-node-image-pin.shagainst the live account — 3 pins live, architectures correct.Expiration.Not run: creating a node from the repinned image. That changes the fleet.
https://claude.ai/code/session_01XdBRc9CHgFhAxCyHbdVtt6
Pull request closed