feat(firecracker): port freeze/migrate off host Docker (PRD 0069 / #397)
The last host-Docker dependency in the Firecracker launch path. Freeze and resume no longer touch the docker daemon, so the backend needs firecracker + KVM only — completing #348. Freeze: stream the guest rootfs over SSH straight into a persistent committed-rootfs.tar (the resumable/migratable artifact) instead of round-tripping through `docker build` from a scratch image. Written to a .partial sibling and atomically renamed so a failed freeze leaves no truncated artifact. Resume: extract the snapshot tar into a cached base dir and feed it to the existing rootless `mke2fs -d` pipeline, replacing the `docker create` + `docker export | tar` path. Recreate the proc/sys/dev/run mount points the freezer excludes so the guest init can mount them. `util.build_base_rootfs_dir` / `docker_image_id` stay — they still back the opt-in BOT_BOTTLE_INFRA_BUILD=local dev path and off-host publish_infra, which are out of scope. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoEZHDjv84ChoZbozQERhJ
This commit is contained in:
@@ -205,25 +205,28 @@ class TestFirecrackerFreezer(_FakeHomeMixin, unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_snapshots_running_vm_without_stopping(self):
|
||||
"""Commit should tar the running guest rootfs over SSH, not stop it."""
|
||||
"""Commit should tar the running guest rootfs over SSH into the
|
||||
committed-rootfs artifact (no Docker), not stop the VM."""
|
||||
slug = "dev-abc12"
|
||||
self._write_meta(slug)
|
||||
self._stage_run_dir(slug)
|
||||
freezer = FirecrackerFreezer()
|
||||
agent = _make_agent(slug, "firecracker")
|
||||
|
||||
with patch("bot_bottle.backend.firecracker.freezer._commit_via_ssh") as mock_commit, \
|
||||
with patch("bot_bottle.backend.firecracker.freezer._commit_rootfs_via_ssh") as mock_commit, \
|
||||
patch("bot_bottle.backend.freeze.info"), \
|
||||
patch("bot_bottle.backend.firecracker.freezer.info"):
|
||||
freezer.commit(agent)
|
||||
|
||||
image_tag = f"bot-bottle-committed-{slug}:latest"
|
||||
tar_path = bottle_state.committed_rootfs_path(slug)
|
||||
self.assertEqual(1, mock_commit.call_count)
|
||||
# (private_key, guest_ip, image_tag) — guest_ip parsed from config.
|
||||
# (private_key, guest_ip, tar_path) — guest_ip parsed from config.
|
||||
args = mock_commit.call_args.args
|
||||
self.assertEqual("100.64.0.1", args[1])
|
||||
self.assertEqual(image_tag, args[2])
|
||||
self.assertEqual(image_tag, bottle_state.read_committed_image(slug))
|
||||
self.assertEqual(tar_path, args[2])
|
||||
# The committed-image state records the artifact path; resume boots
|
||||
# from the tar rather than a Docker image.
|
||||
self.assertEqual(str(tar_path), bottle_state.read_committed_image(slug))
|
||||
self.assertTrue(bottle_state.is_preserved(slug))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user