vk-cocoon maps the three pod operations virtual-kubelet delivers onto cocoon VM operations. Genuine spec changes are handled by the operator deleting and recreating the pod; vk-cocoon only acts on create, delete, and the hibernate transition.
- Parse
meta.VMSpecfrom the pod annotations. - If a VM with
spec.VMNamealready exists locally, adopt it (idempotent on restart). Adoption hinges onStartupReconcilehaving populatedvmsByName; before reconcile completes, CreatePod treats the pod as new and may collide on VM name. - Otherwise
bringUpVMselects a path — restore-from-hibernate and fork-from take precedence, thenspec.Managed, thenspec.Mode:- Restore-from-hibernate: taken when the operator set
vm.cocoonstack.io/restore-from-hibernate(a cross-node wake arriving via CreatePod rather than UpdatePod), or derived from evidence: a managed pod with no marker whose VM name still owns a:hibernateregistry tag (local snapshot presence in registry-less deployments) is a wake lost to a vk restart, and fresh-booting it would let the next hibernate overwrite the guest's state. The derived path fails closed on registry errors (HibernateEvidenceUnavailable), conflicts loudly with explicit clone sources, and rejects a pod whose image ref differs from the ref recorded on the hibernate artifact at push time (cocoonstack.snapshot.baseimage). Identity is compared by ref: a ref change signals operator intent for a different image, while content drift under an unchanged ref is governed by the hibernate-state-is-authoritative contract (discarding hibernated state requires deleting the tag). Registry-less deployments have no identity guard. The derived marker stays in-memory — a later restart re-derives it. Then pull the:hibernatesnapshot and clone from it, same as the UpdatePod wake path. - Fork-from (
spec.ForkFrom): snapshot the named source VM once (deduped viaensureForkSnapshot) and clone every fork off that shared snapshot. The fork snapshot is a per-lineage baseline: every fresh (non-restore) bring-up dropsfork-<vm>before booting, so a recreated same-name VM can never hand a dead incarnation's baseline to new sub-agents; a hibernate restore keeps it, since a wake continues the same lineage. Managed=false(static / externally-managed VMs, e.g. Windows toolboxes on an external QEMU host): skip the runtime entirely and adopt the pre-assignedVMID/IP/VNCPortthe operator pre-wrote into theVMRuntimeannotations.Managedis the single source of truth for "vk-cocoon owns this VM's lifecycle".- Mode
clone(default,Managed=true): look up the snapshot locally using a tag-aware name (repo:tag, or barerepowhen the tag islatestfor backward compatibility). If the local snapshot does not exist, pull it from the registry viaPuller.PullSnapshot. Before cloning,assertSnapshotBackendvalidates the snapshot's recorded hypervisor matchesspec.Backend— a CH snapshot cannot be cloned onto a FC target and vice-versa. When the snapshot carries a base image,Pull: trueis passed toCloneOptions, which translates tococoon vm clone --pull; cocoon constructs a digest reference (repo@sha256:xxx) from the snapshot metadata and pulls the exact image version recorded at snapshot time. ThenRuntime.Clone(from=<local>, to=spec.VMName). Pod-side CPU/memory/storage are not plumbed into clone — cocoon clone inherits all guest resources from the snapshot. Only thevm runpath translates pod resources into VM resources. - Mode
run(Managed=true):ensureRunImagemakes the image available locally before launching the VM. It peeks the OCI manifest viaPuller.Registry: cocoonstack cloud-image artifacts (artifactType=application/vnd.cocoonstack.os-image.v1+json) take the qcow2 streaming path throughPuller.EnsureCloudImageFromRaw→cocoon image import, snapshot artifacts are rejected with a "use mode=clone" error, and everything else (HTTP(S) URLs, container images, refs that don't resolve against the registry) falls through toRuntime.EnsureImage→cocoon image pull.--forcewhenspec.ForcePullis true. ThenRuntime.Run(image=spec.Image, name=spec.VMName). Whenspec.Backendisfirecracker,--fcselects the FC backend; whenspec.OSiswindows,--windowsis passed. Whenspec.NoDirectIOis true,--no-direct-iodisables O_DIRECT on writable disks (CH only, useful for dev/test). vm.cocoonstack.io/clone-from-diroverride (managed-only, takes precedence over mode/fork-from): clone viacocoon vm clone --from-dir <abs-path> --pull, bypassing the local snapshot DB. Pairs withcocoon snapshot export --to-dirfor cross-node staging. Conflicts withmode=runorfork-fromfast-fail.
- Restore-from-hibernate: taken when the operator set
- For clone/fork/wake paths, check whether the VM needs manual network
setup (see Post-clone hints). If so, write the
required commands as a base64-encoded annotation
(
vm.cocoonstack.io/post-clone-hint) and log a warning. The pod stays Running but Not Ready until the user executes the commands viacocoon vm consoleand the probe detects network connectivity. - Resolve the IP from the cocoon-net JSON lease file by MAC.
meta.VMRuntime{VMID, IP}.Apply(pod)writes the runtime annotations back so the operator and other consumers can pick them up.VNCPortis intentionally left unset here — cloud-hypervisor has no VNC server, so only the pre-seeded static-toolbox path ever carries a non-zero value.- Launch a per-pod probe agent (see Readiness probing). The
agent's first probe runs synchronously so the initial
notifypush already reflects reachability; later probes run on a ticker and call back into the provider whenever readiness flips so the async notify hook re-fires.
- Decode
meta.VMSpec. meta.ShouldSnapshotVM(spec, meta.RoleForPod(pod, spec.VMName))— the shared cocoon-common decoder — decides whether to snapshot before destroy. The role comes from the pod's CocoonSet owner (viaRoleForPod), not a VM-name suffix heuristic, so a toolbox named with a trailing-0is never mistaken for the main agent:always:Runtime.SnapshotSavethenPusher.PushSnapshot(tag=meta.DefaultSnapshotTag)to the registry.main-only: same, but only for the main agent (roleRoleMain, i.e. slot 0 of its CocoonSet).never: skip snapshots entirely.
Runtime.Remove(vmID)to destroy the VM.- Drop the local snapshot and its fork snapshot, unless the pod carries
vm.cocoonstack.io/keep-snapshot-on-delete. The operator sets that flag when the delete is ahibernatePolicy: releaseseat release: the VM state stays claimable from the:hibernatetag, so the node-local snapshot is kept as the warm-wake cache that lets a wake landing back on this node skip the registry pull.resolveWakeSourcestill verifies any local copy against the tag'sSnapshotID, so keeping it cannot restore stale state. A missing flag only costs a pull, never correctness. - Forget the pod from the in-memory tables.
The only update vk-cocoon honors is a HibernateState transition.
Anything else is a no-op (the operator deletes and recreates the pod for
genuine spec changes).
| Transition | Behavior |
|---|---|
false → true |
NetResize (CH+Windows) → SnapshotSave → Push → clear VMID before Remove → Remove (rollback on failure). Pod stays alive (PodRunning) so K8s controllers do not recreate it. VMID/IP annotations clear between Push and Remove so the operator's manifest+VMID race window collapses to one patch RTT. Compensating rollback: if Runtime.Remove fails after a successful push, vk-cocoon best-effort Registry.DeleteManifest the hibernate tag and re-applies VMID/IP so the pod stays recoverable. Push and Save are idempotent, so a compensated retry re-publishes the tag cleanly on the next attempt. |
true → false (with no live VM) |
Puller.PullSnapshot(tag=meta.HibernateSnapshotTag) → Runtime.Clone. vk-cocoon does not touch the registry tag on wake; the operator's CocoonHibernation reconciler drops the :hibernate tag once the woken VM is running. |
The operator's CocoonHibernation reconciler tracks the transition by
polling the registry for the hibernate manifest.