k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e_node/deleted_pods_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package e2enode 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/util/uuid" 27 "k8s.io/kubernetes/test/e2e/framework" 28 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 29 imageutils "k8s.io/kubernetes/test/utils/image" 30 admissionapi "k8s.io/pod-security-admission/api" 31 32 "github.com/onsi/ginkgo/v2" 33 "github.com/onsi/gomega" 34 ) 35 36 const ( 37 testFinalizer = "example.com/test-finalizer" 38 ) 39 40 var _ = SIGDescribe("Deleted pods handling", framework.WithNodeConformance(), func() { 41 f := framework.NewDefaultFramework("deleted-pods-test") 42 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 43 44 ginkgo.It("Should transition to Failed phase a pod which is deleted while pending", func(ctx context.Context) { 45 podName := "deleted-pending-" + string(uuid.NewUUID()) 46 podSpec := &v1.Pod{ 47 ObjectMeta: metav1.ObjectMeta{ 48 Name: podName, 49 Finalizers: []string{testFinalizer}, 50 }, 51 Spec: v1.PodSpec{ 52 RestartPolicy: v1.RestartPolicyAlways, 53 Containers: []v1.Container{ 54 { 55 Name: podName, 56 Image: "non-existing-repo/non-existing-image:v1.0", 57 ImagePullPolicy: "Always", 58 Command: []string{"bash"}, 59 Args: []string{"-c", `echo "Hello world"`}, 60 }, 61 }, 62 }, 63 } 64 ginkgo.By("creating the pod with invalid image reference and finalizer") 65 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 66 67 ginkgo.By("set up cleanup of the finalizer") 68 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 69 70 ginkgo.By("Waiting for the pod to be scheduled so that kubelet owns it") 71 err := e2epod.WaitForPodScheduled(ctx, f.ClientSet, pod.Namespace, pod.Name) 72 framework.ExpectNoError(err, "Failed to await for the pod to be scheduled: %q", pod.Name) 73 74 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 75 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, metav1.DeleteOptions{}) 76 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 77 78 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned into the Failed phase", pod.Namespace, pod.Name)) 79 err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name) 80 framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name) 81 82 ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 83 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 84 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 85 }) 86 87 ginkgo.DescribeTable("Should transition to Failed phase a deleted pod if non-zero exit codes", 88 func(ctx context.Context, policy v1.RestartPolicy) { 89 podName := "deleted-running-" + strings.ToLower(string(policy)) + "-" + string(uuid.NewUUID()) 90 podSpec := e2epod.MustMixinRestrictedPodSecurity(&v1.Pod{ 91 ObjectMeta: metav1.ObjectMeta{ 92 Name: podName, 93 Finalizers: []string{testFinalizer}, 94 }, 95 Spec: v1.PodSpec{ 96 RestartPolicy: policy, 97 Containers: []v1.Container{ 98 { 99 Name: podName, 100 Image: imageutils.GetE2EImage(imageutils.BusyBox), 101 Command: []string{"sleep", "1800"}, 102 }, 103 }, 104 }, 105 }) 106 ginkgo.By(fmt.Sprintf("Creating a pod (%v/%v) with restart policy: %v", f.Namespace.Name, podSpec.Name, podSpec.Spec.RestartPolicy)) 107 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 108 109 ginkgo.By("set up cleanup of the finalizer") 110 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 111 112 ginkgo.By("Waiting for the pod to be running") 113 err := e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name) 114 framework.ExpectNoError(err, "Failed to await for the pod to be running: %q", pod.Name) 115 116 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 117 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, *metav1.NewDeleteOptions(1)) 118 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 119 120 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned to the failed phase", pod.Namespace, pod.Name)) 121 err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name) 122 framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name) 123 124 ginkgo.By(fmt.Sprintf("Fetching the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 125 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 126 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 127 128 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container is in the terminated state", pod.Namespace, pod.Name)) 129 gomega.Expect(pod.Status.ContainerStatuses).Should(gomega.HaveLen(1)) 130 containerStatus := pod.Status.ContainerStatuses[0] 131 gomega.Expect(containerStatus.State.Terminated).ToNot(gomega.BeNil(), "The pod container is in not in the Terminated state") 132 133 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container exit code is 137", pod.Namespace, pod.Name)) 134 gomega.Expect(containerStatus.State.Terminated.ExitCode).Should(gomega.Equal(int32(137))) 135 }, 136 ginkgo.Entry("Restart policy Always", v1.RestartPolicyAlways), 137 ginkgo.Entry("Restart policy OnFailure", v1.RestartPolicyOnFailure), 138 ginkgo.Entry("Restart policy Never", v1.RestartPolicyNever), 139 ) 140 141 ginkgo.DescribeTable("Should transition to Succeeded phase a deleted pod when containers complete with 0 exit code", 142 func(ctx context.Context, policy v1.RestartPolicy) { 143 podName := "deleted-running-" + strings.ToLower(string(policy)) + "-" + string(uuid.NewUUID()) 144 podSpec := e2epod.MustMixinRestrictedPodSecurity(&v1.Pod{ 145 ObjectMeta: metav1.ObjectMeta{ 146 Name: podName, 147 Finalizers: []string{testFinalizer}, 148 }, 149 Spec: v1.PodSpec{ 150 RestartPolicy: policy, 151 Containers: []v1.Container{ 152 { 153 Name: podName, 154 Image: imageutils.GetE2EImage(imageutils.BusyBox), 155 Command: []string{"sh", "-c"}, 156 Args: []string{` 157 sleep 9999999 & 158 PID=$! 159 _term() { 160 kill $PID 161 echo "Caught SIGTERM signal!" 162 } 163 164 trap _term SIGTERM 165 touch /tmp/trap-marker 166 wait $PID 167 168 exit 0 169 `, 170 }, 171 ReadinessProbe: &v1.Probe{ 172 PeriodSeconds: 1, 173 ProbeHandler: v1.ProbeHandler{ 174 Exec: &v1.ExecAction{ 175 Command: []string{"/bin/sh", "-c", "cat /tmp/trap-marker"}, 176 }, 177 }, 178 }, 179 }, 180 }, 181 }, 182 }) 183 ginkgo.By(fmt.Sprintf("Creating a pod (%v/%v) with restart policy: %v", f.Namespace.Name, podSpec.Name, podSpec.Spec.RestartPolicy)) 184 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 185 186 ginkgo.By("set up cleanup of the finalizer") 187 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 188 189 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be running and with the SIGTERM trap registered", pod.Namespace, pod.Name)) 190 err := e2epod.WaitTimeoutForPodReadyInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name, f.Timeouts.PodStart) 191 framework.ExpectNoError(err, "Failed to await for the pod to be running: %q", pod.Name) 192 193 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 194 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, metav1.DeleteOptions{}) 195 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 196 197 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned to the succeeded phase", pod.Namespace, pod.Name)) 198 err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name) 199 framework.ExpectNoError(err, "Failed to await for the pod to be succeeded: %q", pod.Name) 200 201 ginkgo.By(fmt.Sprintf("Fetching the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 202 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 203 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 204 205 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container is in the terminated state", pod.Namespace, pod.Name)) 206 gomega.Expect(pod.Status.ContainerStatuses).Should(gomega.HaveLen(1)) 207 containerStatus := pod.Status.ContainerStatuses[0] 208 gomega.Expect(containerStatus.State.Terminated).ShouldNot(gomega.BeNil(), "The pod container is in not in the Terminated state") 209 210 ginkgo.By(fmt.Sprintf("Verifying the exit code for the terminated container is 0 for pod (%v/%v)", pod.Namespace, pod.Name)) 211 gomega.Expect(containerStatus.State.Terminated.ExitCode).Should(gomega.Equal(int32(0))) 212 }, 213 ginkgo.Entry("Restart policy Always", v1.RestartPolicyAlways), 214 ginkgo.Entry("Restart policy OnFailure", v1.RestartPolicyOnFailure), 215 ginkgo.Entry("Restart policy Never", v1.RestartPolicyNever), 216 ) 217 218 })