vk-cocoon implements v-k’s NotifyPods interface, so the framework
treats it as an async provider: Kubernetes only sees the pod status
vk-cocoon actively pushes through notify, and v-k never polls
GetPodStatus on its own. That makes a real per-pod probe loop
load-bearing — any status change that happens after CreatePod returns
is invisible to the cluster unless vk-cocoon re-fires notify.
A successful probe is necessary but not sufficient for Ready=True: vk’s
in-memory lifecycle intent must also be ready. This keeps a reachable VM Not
Ready while clone setup runs, after lifecycle failure, or during hibernation.
The current status is published before the lifecycle-state=ready annotation.
This ordering does not guarantee Ready=True in that status: ready intent
waits for a resolvable IP on every managed path, not for a successful probe.
PodReady remains probe-gated.
The probes/ package owns that loop:
CreatePod (and startup reconcile) call Manager.Start(key, probe,
onUpdate). The probe closure the provider supplies performs three
checks in order:
setVMIP; the
tracked IP is only a cache. Static NICs keep their inspected IP.vm.cocoonstack.io/probe-port annotation, dial
TCP on that port instead of ICMP. Otherwise fall back to
Pinger.Ping(ctx, ip) — a single ICMPv4 echo. This matches the
cocoon Windows golden image contract
(cocoonstack/windows’s
autounattend.xml explicitly opens icmpv4:8 and disables all
firewall profiles), and it decouples readiness from specific
services so the same probe works for Linux and Windows guests alike.os=macos pods get a different closure: when
vm.cocoonstack.io/probe-port is declared, the probe considers a bare TCP
accept on that port ready. Otherwise it falls back to the guest’s sshd on
:22 and requires the SSH- banner — a cold macOS boot takes minutes and
its DHCP lease appears long before sshd answers. On finding the QEMU process
dead, it restarts the record in place (rate-limited); nothing else can see a
crashed QEMU guest.
Start so the
refreshStatus/notify pass that CreatePod does before returning
already reflects the initial reachability decision.onUpdate on every readiness flip: the not-ready → ready
transition (delivering the first Ready when the synchronous probe in
step 2 was not yet ready), and again after 3 consecutive failures flip
readiness back to false. onUpdate re-reads the pod, rebuilds the
status, and calls notify so the kubelet observes the change.DeletePod and a completed hibernate call Manager.Forget, which
cancels the per-pod goroutine; Manager.Close is called once at
shutdown to tear every remaining agent down.If the ICMP raw socket cannot be opened — typically because the binary is
running without CAP_NET_RAW — the provider falls back to
network.NopPinger and the ICMP branch degrades to “an IP was resolved ==
Ready”. A pod carrying vm.cocoonstack.io/probe-port still gets a real TCP
dial, and macOS pods keep their SSH-banner probe. The degraded branch is
weaker than a real end-to-end ping but still strictly better than the
previous behaviour of marking the pod Ready the instant
cocoon vm clone/run returned. The systemd unit in
packaging/vk-cocoon.service runs vk-cocoon as root, so the production path
gets the real pinger.