fix(firecracker): serialize infra-VM create + agent builds (PR #354 review)
lint / lint (push) Successful in 2m8s
test / unit (pull_request) Successful in 1m8s
test / integration (pull_request) Successful in 27s
test / coverage (pull_request) Successful in 1m22s

Codex flagged that parallel `start`s race on shared state: two cold
launches could both stop/build/boot the singleton on the same rootfs/PID,
concurrent builds share the infra VM's buildah store, the cleanup did
`buildah rm -a` (nuking a peer build's container), and the rootfs cache
was populated non-atomically.

- `infra_vm.ensure_running`: a host flock (`singleton.lock`) around the
  cold stop/build/boot path, with a double-checked health re-test under
  the lock so a second launcher adopts rather than re-boots. The healthy
  fast-path stays lock-free.
- `image_builder.build_agent_rootfs_dir`: a host flock (`.build.lock`)
  around cache-lookup + build + publish; build into `.building-<digest>`
  and publish by atomic `os.rename`, so a partial build never appears as
  `agent-<digest>`.
- scoped cleanup: per-build named working containers
  (`<tag>-smoke`/`-export`) removed by name instead of `buildah rm -a`;
  also cleared before the build to recover from a crashed prior run.

Verified: agent image builds via the locked/atomic path, infra VM stays
healthy, agent boots, `claude --version` = 2.1.172. Full unit suite +
pyright green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
2026-07-16 16:15:58 -04:00
parent 43c3d4408e
commit 914f01fa8f
3 changed files with 100 additions and 36 deletions
+3 -3
View File
@@ -44,7 +44,7 @@ class TestBuildAgentRootfsDir(unittest.TestCase):
build.assert_called_once()
# smoke_test threads through to the VM build.
self.assertEqual(("claude", "--version"), build.call_args.args[2])
inject.assert_called_once_with(out)
inject.assert_called_once()
self.assertTrue((out / ".bb-ready").is_file())
def test_content_addressed_cache_key(self):
@@ -59,7 +59,7 @@ class TestBuildAgentRootfsDir(unittest.TestCase):
class TestSmokeTest(unittest.TestCase):
def test_empty_argv_is_noop(self):
with patch.object(image_builder, "_ssh") as ssh:
image_builder._smoke_test(Path("/k"), "10.0.0.1", "tag", ())
image_builder._smoke_test(Path("/k"), "10.0.0.1", "tag", "ctr", ())
ssh.assert_not_called()
def test_failed_smoke_dies(self):
@@ -67,7 +67,7 @@ class TestSmokeTest(unittest.TestCase):
result = subprocess.CompletedProcess([], 1, stdout="broken", stderr="")
with patch.object(image_builder, "_ssh", return_value=result), \
self.assertRaises(SystemExit):
image_builder._smoke_test(Path("/k"), "10.0.0.1", "tag", ("claude", "--version"))
image_builder._smoke_test(Path("/k"), "10.0.0.1", "tag", "ctr", ("claude", "--version"))
if __name__ == "__main__":