k8s.io/kubernetes@v1.29.3/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 "time" 24 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/uuid" 28 "k8s.io/kubernetes/test/e2e/framework" 29 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 30 imageutils "k8s.io/kubernetes/test/utils/image" 31 admissionapi "k8s.io/pod-security-admission/api" 32 33 "github.com/onsi/ginkgo/v2" 34 "github.com/onsi/gomega" 35 ) 36 37 const ( 38 testFinalizer = "example.com/test-finalizer" 39 ) 40 41 var _ = SIGDescribe("Deleted pods handling", framework.WithNodeConformance(), func() { 42 f := framework.NewDefaultFramework("deleted-pods-test") 43 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 44 45 ginkgo.It("Should transition to Failed phase a pod which is deleted while pending", func(ctx context.Context) { 46 podName := "deleted-pending-" + string(uuid.NewUUID()) 47 podSpec := &v1.Pod{ 48 ObjectMeta: metav1.ObjectMeta{ 49 Name: podName, 50 Finalizers: []string{testFinalizer}, 51 }, 52 Spec: v1.PodSpec{ 53 RestartPolicy: v1.RestartPolicyAlways, 54 Containers: []v1.Container{ 55 { 56 Name: podName, 57 Image: "non-existing-repo/non-existing-image:v1.0", 58 ImagePullPolicy: "Always", 59 Command: []string{"bash"}, 60 Args: []string{"-c", `echo "Hello world"`}, 61 }, 62 }, 63 }, 64 } 65 ginkgo.By("creating the pod with invalid image reference and finalizer") 66 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 67 68 ginkgo.By("set up cleanup of the finalizer") 69 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 70 71 ginkgo.By("Waiting for the pod to be scheduled so that kubelet owns it") 72 err := e2epod.WaitForPodScheduled(ctx, f.ClientSet, pod.Namespace, pod.Name) 73 framework.ExpectNoError(err, "Failed to await for the pod to be scheduled: %q", pod.Name) 74 75 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 76 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, metav1.DeleteOptions{}) 77 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 78 79 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned into the Failed phase", pod.Namespace, pod.Name)) 80 err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name) 81 framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name) 82 83 ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 84 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 85 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 86 }) 87 88 ginkgo.DescribeTable("Should transition to Failed phase a deleted pod if non-zero exit codes", 89 func(ctx context.Context, policy v1.RestartPolicy) { 90 podName := "deleted-running-" + strings.ToLower(string(policy)) + "-" + string(uuid.NewUUID()) 91 podSpec := e2epod.MustMixinRestrictedPodSecurity(&v1.Pod{ 92 ObjectMeta: metav1.ObjectMeta{ 93 Name: podName, 94 Finalizers: []string{testFinalizer}, 95 }, 96 Spec: v1.PodSpec{ 97 RestartPolicy: policy, 98 Containers: []v1.Container{ 99 { 100 Name: podName, 101 Image: imageutils.GetE2EImage(imageutils.BusyBox), 102 Command: []string{"sleep", "1800"}, 103 }, 104 }, 105 }, 106 }) 107 ginkgo.By(fmt.Sprintf("Creating a pod (%v/%v) with restart policy: %v", f.Namespace.Name, podSpec.Name, podSpec.Spec.RestartPolicy)) 108 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 109 110 ginkgo.By("set up cleanup of the finalizer") 111 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 112 113 ginkgo.By("Waiting for the pod to be running") 114 err := e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name) 115 framework.ExpectNoError(err, "Failed to await for the pod to be running: %q", pod.Name) 116 117 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 118 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, *metav1.NewDeleteOptions(1)) 119 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 120 121 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned to the failed phase", pod.Namespace, pod.Name)) 122 err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name) 123 framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name) 124 125 ginkgo.By(fmt.Sprintf("Fetching the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 126 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 127 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 128 129 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container is in the terminated state", pod.Namespace, pod.Name)) 130 gomega.Expect(pod.Status.ContainerStatuses).Should(gomega.HaveLen(1)) 131 containerStatus := pod.Status.ContainerStatuses[0] 132 gomega.Expect(containerStatus.State.Terminated).ToNot(gomega.BeNil(), "The pod container is in not in the Terminated state") 133 134 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container exit code is 137", pod.Namespace, pod.Name)) 135 gomega.Expect(containerStatus.State.Terminated.ExitCode).Should(gomega.Equal(int32(137))) 136 }, 137 ginkgo.Entry("Restart policy Always", v1.RestartPolicyAlways), 138 ginkgo.Entry("Restart policy OnFailure", v1.RestartPolicyOnFailure), 139 ginkgo.Entry("Restart policy Never", v1.RestartPolicyNever), 140 ) 141 142 ginkgo.DescribeTable("Should transition to Succeeded phase a deleted pod when containers complete with 0 exit code", 143 func(ctx context.Context, policy v1.RestartPolicy) { 144 podName := "deleted-running-" + strings.ToLower(string(policy)) + "-" + string(uuid.NewUUID()) 145 podSpec := e2epod.MustMixinRestrictedPodSecurity(&v1.Pod{ 146 ObjectMeta: metav1.ObjectMeta{ 147 Name: podName, 148 Finalizers: []string{testFinalizer}, 149 }, 150 Spec: v1.PodSpec{ 151 RestartPolicy: policy, 152 Containers: []v1.Container{ 153 { 154 Name: podName, 155 Image: imageutils.GetE2EImage(imageutils.BusyBox), 156 Command: []string{"sh", "-c"}, 157 Args: []string{` 158 sleep 9999999 & 159 PID=$! 160 _term() { 161 kill $PID 162 echo "Caught SIGTERM signal!" 163 } 164 165 trap _term SIGTERM 166 wait $PID 167 168 exit 0 169 `, 170 }, 171 }, 172 }, 173 }, 174 }) 175 ginkgo.By(fmt.Sprintf("Creating a pod (%v/%v) with restart policy: %v", f.Namespace.Name, podSpec.Name, podSpec.Spec.RestartPolicy)) 176 pod := e2epod.NewPodClient(f).Create(ctx, podSpec) 177 178 ginkgo.By("set up cleanup of the finalizer") 179 ginkgo.DeferCleanup(e2epod.NewPodClient(f).RemoveFinalizer, pod.Name, testFinalizer) 180 181 ginkgo.By("Waiting for the pod to be running") 182 err := e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name) 183 framework.ExpectNoError(err, "Failed to await for the pod to be running: %q", pod.Name) 184 185 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", pod.Namespace, pod.Name)) 186 // wait a little bit to make sure the we are inside the while and that the trap is registered 187 time.Sleep(time.Second) 188 err = e2epod.NewPodClient(f).Delete(ctx, pod.Name, metav1.DeleteOptions{}) 189 framework.ExpectNoError(err, "Failed to delete the pod: %q", pod.Name) 190 191 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned to the succeeded phase", pod.Namespace, pod.Name)) 192 err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name) 193 framework.ExpectNoError(err, "Failed to await for the pod to be succeeded: %q", pod.Name) 194 195 ginkgo.By(fmt.Sprintf("Fetching the end state of the pod (%v/%v)", pod.Namespace, pod.Name)) 196 pod, err = e2epod.NewPodClient(f).Get(ctx, pod.Name, metav1.GetOptions{}) 197 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name) 198 199 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container is in the terminated state", pod.Namespace, pod.Name)) 200 gomega.Expect(pod.Status.ContainerStatuses).Should(gomega.HaveLen(1)) 201 containerStatus := pod.Status.ContainerStatuses[0] 202 gomega.Expect(containerStatus.State.Terminated).ShouldNot(gomega.BeNil(), "The pod container is in not in the Terminated state") 203 204 ginkgo.By(fmt.Sprintf("Verifying the exit code for the terminated container is 0 for pod (%v/%v)", pod.Namespace, pod.Name)) 205 gomega.Expect(containerStatus.State.Terminated.ExitCode).Should(gomega.Equal(int32(0))) 206 }, 207 ginkgo.Entry("Restart policy Always", v1.RestartPolicyAlways), 208 ginkgo.Entry("Restart policy OnFailure", v1.RestartPolicyOnFailure), 209 ginkgo.Entry("Restart policy Never", v1.RestartPolicyNever), 210 ) 211 212 })