feat(vm): atomic hibernate; restore accepts stopped VMs#87
Conversation
vm hibernate captures like snapshot save but terminates the VMM inside the pause window instead of resuming, so the snapshot point and the stop coincide and nothing written after the snapshot is lost on resume. Restore's running-only guard relaxes to running-or-stopped: the sequence cold-spawns a fresh VMM from the snapshot either way and the kill step already tolerates a dead one. Restore paths keep the entropy-only reseed, so a resumed VM retains its machine identity.
HibernateSpec carries the terminate hook (now sharing the sequence's keep-alive client) and runtime files, matching the Spec-struct pattern; terminateVMM dedups the restore/hibernate kill closures per backend; runWrapped collapses the four Wrap-or-direct dispatch sites; a state flip failure after a successful kill downgrades to warn-and-continue so a transient DB error cannot delete the only memory image; restore of a non-running VM now runs Start's network self-heal (host-reboot resume); cmdcore.CaptureSnapshot folds the shared save/hibernate tail and drops the double-prefixed error wrap.
|
Review round applied (reuse/simplification/efficiency/altitude): HibernateSpec struct (terminate hook shares the sequence's keep-alive client), per-backend terminateVMM dedup with the restore kill path, runWrapped for the four Wrap dispatch sites, warn-and-continue on the post-kill state flip (a transient DB error must not delete the only memory image), Start-parity network self-heal when restoring a non-running VM (host-reboot resume), and cmdcore.CaptureSnapshot folding the save/hibernate tail. Re-verified on hardware: hibernate 458ms / restore-on-stopped 29ms, identity + tmpfs marker intact; full control-plane e2e regression green with the updated binary. |
The previous order terminated the VMM and marked the VM stopped before the snapshot reached the store; a failed save (disk full, snapshot DB error) then closed the TarDirStreamWithRemove stream, deleting the only memory image — VM stopped, no resume point. Persist now runs inside the pause window and the VMM dies only after it succeeds: any failure up to that point resumes the VM and the command simply fails. Hibernator takes the persist callback so the hypervisor layer stays ignorant of snapshot storage. Also updates README/KNOWN_ISSUES for hibernate and restore-on-stopped.
|
Fixed the unsafe save-failure path: persist now happens inside the pause window and the VMM is terminated only after the snapshot is durably stored — a failed save (disk full, DB error) resumes the VM and the command fails with nothing lost. Verified on hardware with fault injection: a 1MB tmpfs mounted over the snapshot store made hibernate fail with ENOSPC and the VM stayed running (guest state intact, memory marker readable); after unmounting, hibernate 326ms / restore 35ms / marker intact. Full control-plane e2e regression green. README and KNOWN_ISSUES updated for hibernate and restore-on-stopped. |
RecordSnapshot runs before persist, so a persist failure resumed the VM but left a snapshot id in the record that maps to nothing. Drop it via best-effort UnrecordSnapshot on that path, and cover both hibernate sequence outcomes with unit tests driven by a stub VMM process.
|
Persist-failure rollback added: |
…st tail cmd.Start returns before bash execs into the argv[0]-renamed tail, so the cmdline liveness check raced on Linux (green on darwin only via the no-/proc fallback) — poll until the check passes. Add the third hibernate outcome test (terminate failure marks error, snapshot stays recorded). PersistSnapshotStream dedups the save/hibernate persist tail; finishSnapshot inlined into SnapshotSequence.
Closes #85.
What
vm hibernate VM [--name --description]— atomic pause → capture → terminate:HibernateSequenceshares the capture skeleton withSnapshotSequence(extractedprepareSnapshot/finishSnapshot) but ends the pause window by force-terminating the VMM instead of resuming, then cleans runtime files and flips the record to stopped (metering interval closes via the existingUpdateStatespath). Capture failure resumes the VM; terminate failure marks it error. Exposed as the optionalhypervisor.Hibernatorinterface (theDirectpattern), implemented by CH and FC via their sharedsnapshotSpec.ResolveForRestoreallowsstoppedalongsiderunning. Mechanically nothing else changes:RestoreSequencecold-spawns a fresh VMM from the snapshot either way,KillForRestorealready toleratesErrNotRunning, andemitRestoreComputeStopno-ops on an already-closed interval, so the ledger stays sane.cmdcore.EnsureSnapshotNameFree(shared bysnapshot saveandvm hibernate— hibernate must fail on a taken name before the VM is stopped).Why
Sandbox hibernation: idle VM releases memory, resumes later with identity intact. The previous
save→stopcomposition loses whatever the guest writes after the snapshot point; restore refused stopped VMs outright.Verified (bare-metal node, FC guest, silkd-baked image)
vm hibernate: 455ms, state=stopped, zero VMM processes (memory freed)vm restoreon the stopped VM: 27ms;/etc/machine-idunchanged, a tmpfs marker written before hibernate survives (memory state intact), the in-guest daemon keeps runningmake lint testgreen (GOOS linux+darwin matrix); newTestResolveForRestoreStatescovers the guard