Skip to content
Open
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
4 changes: 3 additions & 1 deletion internal/operator/controller/sandbox_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,9 @@ func (r *SandboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
}

if sandboxPod == nil {
isTerminal := meta.FindStatusCondition(sandbox.Status.Conditions, sandboxv1alpha1.SandboxSucceededCondition) != nil

if sandboxPod == nil && !isTerminal {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve terminal Ready status after pod GC

In the out-of-band GC case this condition now skips pod creation, but the terminal sandbox still falls through to reconcileSandboxStatus(..., nil) below. That helper derives Ready/PodReady from a nil pod as PodPending, so after a succeeded or failed workload loses its pod, the controller overwrites the terminal PodSucceeded/PodFailed Ready reason (the CRD's Reason column is sourced from Ready) even though no pod is pending. Please return before the nil-pod status reconciliation or preserve terminal Ready/PodReady conditions.

Useful? React with 👍 / 👎.

if err := r.CreateSandboxPod(ctx, sandbox, baseSandbox); err != nil {
return ctrl.Result{}, err
}
Expand Down
39 changes: 39 additions & 0 deletions internal/operator/controller/sandbox_controller_pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,45 @@ var _ = Describe("Sandbox Controller", func() {
sandbox := getSandbox(ctx, sandboxName)
Expect(meta.FindStatusCondition(sandbox.Status.Conditions, sandboxv1alpha1.SandboxSucceededCondition)).To(BeNil())
})

It("should not re-create pod for a terminal sandbox whose pod was deleted out-of-band", func() {
sandboxName := "sandbox-pod-terminal-gc"

createSandbox(ctx, sandboxName)
defer deleteSandbox(ctx, sandboxName)

podName := sandboxName + "-pod"
defer deletePod(ctx, podName)

_, err := doReconcile(ctx, reconciler, sandboxName)
Expect(err).NotTo(HaveOccurred())

pod := getPod(ctx, podName)
Expect(pod).NotTo(BeNil())

pod.Status.Phase = corev1.PodSucceeded
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
{Name: "sandbox", State: corev1.ContainerState{Terminated: &corev1.ContainerStateTerminated{ExitCode: 0, Reason: "Completed"}}},
}
Expect(k8sClient.Status().Update(ctx, pod)).To(Succeed())

_, err = doReconcile(ctx, reconciler, sandboxName)
Expect(err).NotTo(HaveOccurred())

sandbox := getSandbox(ctx, sandboxName)
Expect(hasConditionWithReason(sandbox, sandboxv1alpha1.SandboxSucceededCondition, metav1.ConditionTrue, CondReasonPodSucceeded)).To(BeTrue())

Expect(k8sClient.Delete(ctx, pod)).To(Succeed())
Expect(getPod(ctx, podName)).To(BeNil())

_, err = doReconcile(ctx, reconciler, sandboxName)
Expect(err).NotTo(HaveOccurred())

Expect(getPod(ctx, podName)).To(BeNil(), "terminal sandbox pod must not be re-created after out-of-band deletion")

sandbox = getSandbox(ctx, sandboxName)
Expect(hasConditionWithReason(sandbox, sandboxv1alpha1.SandboxSucceededCondition, metav1.ConditionTrue, CondReasonPodSucceeded)).To(BeTrue())
})
})

// ============================================
Expand Down