Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions hibernation/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,58 @@ func TestReconcileWakeRecoversFromFailed(t *testing.T) {
}
}

func TestReconcileWakeClearsHibernateResidueOnFastPath(t *testing.T) {
hib := &cocoonv1.CocoonHibernation{
ObjectMeta: metav1.ObjectMeta{Name: "hib", Namespace: "ns", Finalizers: []string{finalizerName}},
Spec: cocoonv1.CocoonHibernationSpec{
Desire: cocoonv1.HibernationDesireWake,
PodRef: cocoonv1.HibernationPodRef{Name: "demo-0"},
},
Status: cocoonv1.CocoonHibernationStatus{Phase: cocoonv1.CocoonHibernationPhaseWaking},
}
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "demo-0", Namespace: "ns"},
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{{
State: corev1.ContainerState{Running: &corev1.ContainerStateRunning{}},
}},
},
}
(&meta.VMSpec{VMName: "vk-ns-demo-0", Managed: true}).Apply(pod)
(&meta.VMRuntime{VMID: "vmid-live"}).Apply(pod)
meta.HibernateState(true).Apply(pod)

scheme := testScheme(t)
cli := ctrlfake.NewClientBuilder().
WithScheme(scheme).
WithObjects(hib, pod).
WithStatusSubresource(&cocoonv1.CocoonHibernation{}).
Build()
r := &Reconciler{Client: cli, Scheme: scheme, Epoch: &fakeRegistry{}}

if _, err := r.Reconcile(t.Context(), ctrl.Request{
NamespacedName: types.NamespacedName{Namespace: "ns", Name: "hib"},
}); err != nil {
t.Fatalf("Reconcile: %v", err)
}

var outHib cocoonv1.CocoonHibernation
if err := cli.Get(t.Context(), types.NamespacedName{Namespace: "ns", Name: "hib"}, &outHib); err != nil {
t.Fatalf("get hib: %v", err)
}
if outHib.Status.Phase != cocoonv1.CocoonHibernationPhaseActive {
t.Errorf("phase = %q, want Active (fast-path)", outHib.Status.Phase)
}

var outPod corev1.Pod
if err := cli.Get(t.Context(), types.NamespacedName{Namespace: "ns", Name: "demo-0"}, &outPod); err != nil {
t.Fatalf("get pod: %v", err)
}
if meta.ReadHibernateState(&outPod) {
t.Error("hibernate annotation must be cleared on the wake fast-path; still true")
}
}

func TestHibernateDeadlineExceeded(t *testing.T) {
staleReady := metav1.Condition{
Type: commonk8s.ConditionTypeReady,
Expand Down
12 changes: 6 additions & 6 deletions hibernation/wake.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ func (r *Reconciler) reconcileWake(ctx context.Context, hib *cocoonv1.CocoonHibe
logger := log.WithFunc("hibernation.Reconciler.reconcileWake")
r.announceRetryFromFailed(hib, cocoonv1.HibernationDesireWake)

if meta.ReadHibernateState(pod) {
if err := commonk8s.PatchHibernateState(ctx, r.Client, pod, false); err != nil {
return ctrl.Result{}, fmt.Errorf("clear hibernate annotation: %w", err)
}
}

if vmClonedAndRunning(pod) {
// Drop snapshot tag (non-fatal; stale tag gets overwritten on next hibernate).
if err := r.Epoch.DeleteManifest(ctx, vmName, meta.HibernateSnapshotTag); err != nil {
Expand All @@ -29,12 +35,6 @@ func (r *Reconciler) reconcileWake(ctx context.Context, hib *cocoonv1.CocoonHibe
return ctrl.Result{}, r.setPhase(ctx, hib, cocoonv1.CocoonHibernationPhaseActive, vmName)
}

if meta.ReadHibernateState(pod) {
if err := commonk8s.PatchHibernateState(ctx, r.Client, pod, false); err != nil {
return ctrl.Result{}, fmt.Errorf("clear hibernate annotation: %w", err)
}
}

if phaseDeadlineExceeded(hib, cocoonv1.CocoonHibernationPhaseWaking, wakeTimeout) {
if r.firstTransitionAt(hib) {
observePhaseExit(hib, "timeout")
Expand Down