diff --git a/internal/operator/controller/sandbox_controller.go b/internal/operator/controller/sandbox_controller.go index 7c6a2573..3b87a3ba 100644 --- a/internal/operator/controller/sandbox_controller.go +++ b/internal/operator/controller/sandbox_controller.go @@ -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 { if err := r.CreateSandboxPod(ctx, sandbox, baseSandbox); err != nil { return ctrl.Result{}, err } diff --git a/internal/operator/controller/sandbox_controller_pod_test.go b/internal/operator/controller/sandbox_controller_pod_test.go index 7bd84eb1..4be1df7d 100644 --- a/internal/operator/controller/sandbox_controller_pod_test.go +++ b/internal/operator/controller/sandbox_controller_pod_test.go @@ -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()) + }) }) // ============================================