Repository navigation
fix: a plain unsuspend reclaims each woken VM's :hibernate tag - #60
Conversation
A retain-policy CocoonSet without spec.nodeName woke its VMs on unsuspend but never deleted their <vm>:hibernate tags. vk-cocoon's CreatePod restores from any :hibernate it finds, so a later recreate of the pod (force delete, failed-main rebuild, drift rebuild) silently rolled the guest back to the suspend-time state. The CocoonHibernation wake, the release-policy wake and a migration already drop the tag once the VM is live. applyUnsuspend now records the set's generation and the managed VM names it wakes in the cocoonset.cocoonstack.io/hibernate-reclaim annotation before it clears any pod annotation. Each pass deletes a recorded VM's tag once its pod runs a live VM that vk-cocoon reports ready at that generation or later, and drops the VM from the record. A missing, rebuilding or unwoken pod keeps its VM recorded, a refused delete is logged and stays recorded, and the annotation goes with the last VM. A set with no record costs one map lookup per pass.
|
Hardware verification on 2026-09-25, on two bare-metal hosts with the two-host E2E kit. Every kit binary, the firmware and the running operator, webhook and vk-cocoon processes were checked by sha256 before and after each lane. Both arms ran H-06 and H-07 against the same live stack. Only the operator changed between them: main Each case creates a CocoonSet without
The wake itself brought marker 1 back on both arms. After the PR arm, main was restarted and the version gate passed again. Report and evidence: cocoonstack/cocoon-specs |
…ackoff A refused tag delete was logged and the pass went on with nothing requeued. If the pod was then force-deleted, that pass saw no woken pod, so the recreate restored from the tag that was still there, which is the rollback this change removes. The CocoonHibernation wake returns the error for the same reason. Progress on the other VMs is saved before the error returns. Docs step 10 also corrects the removed-slot case: its VM is reclaimed once the slot returns and wakes.
…tyle, and tests reuse mustGetCS Own-line comments start uppercase and state one fact, and the one that restated its return now says only why it waits. The applyUnsuspend test drops a field that only picked the failure message, and seven hand-rolled CocoonSet reads plus stashedVMNames call mustGetCS.
Go 1.27 ships encoding/json/v2 without GOEXPERIMENT, and new JSON code goes on it. The record encodes to the same bytes as with v1, so a value an earlier build wrote reads back and compares equal. rebuild.go keeps its existing v1 code.
…e JSON package v2 leaves map keys unsorted and no longer omits a false bool under omitempty, so the encode asks for deterministic output and Parked moves to omitzero. The annotation keeps the bytes v1 wrote, and a new test pins them.
…entifier The comment style puts an uppercase first letter on own-line comments; the ones that open with an identifier or a component name (vk, vk-cocoon, kubelet, core/log) keep it. Two comments also drop a restated wait and a joined second clause.
The refused delete fails the whole pass, so scaling and the status patch wait for the retry too. The CocoonHibernation wake has nothing after its delete, so the comparison with it was inexact.
…, on fixtures that reach the registry The sleep-overlap probe passed with the lock disabled: no fixture put two registry calls in flight. The deleting-vs-live case never calls the registry once CRs carry UIDs, so it is dropped; the retargeted case covers the delete-path lock.
|
Record-build chains, 2026-09-25 afternoon.
The two earlier H-08 runs were kit defects and are voided in the round report:
(Edited: an earlier version of this comment described the two voided runs incorrectly.) |
Problem
A CocoonSet with the default retain
hibernatePolicyand nospec.nodeName, suspended and then unsuspended, woke its VMs but never deleted their<vm>:hibernatetags.applyUnsuspendonly cleared each pod's hibernate annotation.vk-cocoon's
CreatePodrestores from any:hibernateit finds (deriveRestoreFromEvidence, vk-cocoon #55). So a later recreate of the pod silently rolled the guest back to the suspend-time state, leaving only aHibernateSnapshotExistswarning. Recreates happen on a force delete, a failed-main rebuild or a drift rebuild.The other wake paths already drop the tag once the VM is live: the CocoonHibernation wake (093eac8 names this exact rollback), the release-policy wake and a migration. The 2026-09-24 two-host round saw the tag survive a plain suspend and unsuspend.
Change
applyUnsuspendclears any pod annotation, it records two things incocoonset.cocoonstack.io/hibernate-reclaim: the set's generation, and the names of the managed VMs it wakes.lifecycle-state=readyat the recorded generation or later.The ready-at-the-recorded-generation gate means a stale pre-suspend
readyin the informer never passes. So does an unsuspend that lands mid-hibernate, while the VM is still live and vk reportshibernating. A spec edit after the wake does not hold the reclaim back.Cost
Tests
cocoonset/reconciler_test.go:applyUnsuspendmerges the woken VMs into an existing record at the new generation. It writes the record before any pod patch. A second pass over a stale pod cache does not rewrite an unchanged record.reclaimWokenSnapshotstable, one row per case:Reconciledeletes the woken VM's tag, clears the record, and makes no registry call on the next pass.Each of these mutations fails a test: dropping the live-VM, ready-state or annotation check; gating on the set's current generation; stopping at the first unwoken VM; recording after the clears; rewriting an unchanged record; dropping the prior record.
Gates, all with
GOWORK=off:make fmt-checkmake linton darwin and linux:0 issues.for eachgo test -race -count=1 ./...asl ./...on darwin and linux: cleanDocs
docs/cocoonset.mdsteps 6 and 10 describe the record and when a tag is reclaimed.Commits
21c9b2dthe fix.ede2d84a refused reclaim fails the pass so it retries, and docs step 10 corrects the removed-slot case.cdeedd2review only: own-line comments insuspend.goandreconciler.gostart uppercase with one fact each, and the reconciler tests reusemustGetCS.abcc20creview: the reclaim record usesencoding/json/v2; its bytes equal v1's, so a record an earlier build wrote reads back unchanged.9bf876creview: the rebuild history moves toencoding/json/v2too (Deterministic(true)keeps sorted keys,omitzerokeeps an unparked entry'sparkedout), so the repo imports one JSON package. A new test pins the exact bytes.9c072a0review: own-line comments start uppercase unless they open with an identifier, repo-wide.6b3944dreview: docs step 10 states what a refused reclaim holds back: the rest of that pass, scaling and the status patch included.99606a6test: the VM-lock tests passed with the lock disabled, because no fixture put two registry calls in flight. They now check withTryLockthat the VM's lock is held at each registry call, on fixtures that reach the registry. The deleting-versus-live case never reaches the registry once its CRs carry UIDs, so it is dropped; the retargeted case covers the delete-path lock. Disabling either lock fails a test.Hardware verification
Verified on 2026-09-25 at
21c9b2don two bare-metal hosts, as an A/B pair against the same live stack. H-06 and H-07 fail on main and pass on this branch.ede2d84changes only the refused-delete path, which the unit tests cover; the hardware round did not inject a registry failure. Results: #60 (comment)