fix(ci): delete every ephemeral runner registration, not just the last one per cycle #148
Loading…
Reference in a new issue
No description provided.
Delete branch "jfraeys/fix/ephemeral-runner-registration-leak"
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?
The documented end state of every provisioner cycle is "no VM, no runner registration, no cycle.json". Checked on 2026-10-14: VMs and cycle.json converge correctly (the 10-job-cap VM was destroyed,
tofu destroyclean), but 5 stale ephemeral runner registrations remain in Forgejo (ids 514, 516, 518 from one cycle, 424 from ~1 day earlier, 106 from ~11 days earlier; allephemeral-*, offline,last_online0; the only live one, id 526, belongs to the running cycle).Cause. A reused VM registers one ephemeral runner per job.
_reuse_vm_for_jobsetscycle["runner_id"] = None, so a previous registration that never consumed itself is forgotten, andcleanup_cycleonly deletes the lastrunner_id. Each leaked registration is a valid runner credential that was never revoked (the tokens only ever lived on VMs that are now destroyed, so the practical risk is low, but it should converge to none).Fix.
_reuse_vm_for_job: delete the previous registration (if it still exists) before forgetting it. Reuse only happens after the previous runner has exited.cleanup_cycle: after a successfultofu destroy, sweep every offlineephemeral-*registration (best effort, never blocks convergence, skipped while a VM may still exist). This also collects the old orphans on the first cycle after deploy.test_runner_cleanup.py) with an in-memory fake of the admin runners API: sweep deletes only offlineephemeral-*runners; reuse deletes an unconsumed previous registration and leaves a consumed one alone; cleanup sweeps after destroy and skips the sweep when destroy failed.pytest roles/ci_ephemeral_provisioner/tests: 23 passed.Unverified: the sweep relies on the admin runners list returning
status: "offline"for stale runners (the ForgejoActionRunnerfield). I could not call that API from my token (needsread:admin). If the field is absent the sweep deletes nothing (safe default), but then the first deployed cycle should be checked.Deploy: touches
roles/ci_ephemeral_provisioner(deploy with./infra deploy --tags ci_ephemeral_provisioner --limit services; use./infra, not a bare ansible-playbook, since the Tailscale key is minted there). Until then, delete ids 514, 516, 518, 424, 106 by hand in Site Administration > Actions > Runners.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ