k8s.io/kubernetes@v1.29.3/test/e2e_node/pods_lifecycle_termination_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 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/util/uuid" 26 "k8s.io/kubernetes/test/e2e/framework" 27 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 28 testutils "k8s.io/kubernetes/test/utils" 29 admissionapi "k8s.io/pod-security-admission/api" 30 31 "github.com/onsi/ginkgo/v2" 32 "github.com/onsi/gomega" 33 ) 34 35 // Pod sigkill test will cover pods with graceful termination period set but failed 36 // to terminate and forcefully killed by kubelet. This test examine pod's container's 37 // exit code is 137 and the exit reason is `Error` 38 var _ = SIGDescribe("Pod SIGKILL [LinuxOnly]", framework.WithNodeConformance(), func() { 39 f := framework.NewDefaultFramework("sigkill-test") 40 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 41 42 podName := "sigkill-pod-" + string(uuid.NewUUID()) 43 containerName := "sigkill-target-container" 44 podSpec := getSigkillTargetPod(podName, containerName) 45 ginkgo.Context("", func() { 46 ginkgo.BeforeEach(func() { 47 ginkgo.By("setting up the pod to be used in the test") 48 e2epod.NewPodClient(f).Create(context.TODO(), podSpec) 49 }) 50 51 ginkgo.It("The containers terminated forcefully by Sigkill should have the correct exit code(137) and reason (Error)", func() { 52 53 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be running", f.Namespace.Name, podSpec.Name)) 54 err := e2epod.WaitForPodNameRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, f.Namespace.Name) 55 framework.ExpectNoError(err, "Failed to await for the pod to be running: %q", podSpec.Name) 56 57 // Checking pod's readiness to confirm the signal handler has registered successfully. 58 err = e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, f.Namespace.Name, podSpec.Name, "Ready", f.Timeouts.PodStart, testutils.PodRunningReady) 59 framework.ExpectNoError(err, "Failed to await Pod (%v/%v) become ready after registering signal handler: %v", f.Namespace.Name, podSpec.Name, err) 60 61 ginkgo.By(fmt.Sprintf("Deleting the pod (%v/%v) to set a deletion timestamp", f.Namespace.Name, podSpec.Name)) 62 err = e2epod.NewPodClient(f).Delete(context.TODO(), podSpec.Name, metav1.DeleteOptions{}) 63 framework.ExpectNoError(err, "Failed to delete the pod: %q", podSpec.Name) 64 65 ginkgo.By(fmt.Sprintf("Waiting for the pod (%v/%v) to be transitioned to the terminated phase", f.Namespace.Name, podSpec.Name)) 66 err = e2epod.WaitForPodTerminatedInNamespace(context.TODO(), f.ClientSet, podSpec.Name, "", f.Namespace.Name) 67 framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", podSpec.Name) 68 69 ginkgo.By(fmt.Sprintf("Fetching the end state of the pod (%v/%v)", f.Namespace.Name, podSpec.Name)) 70 pod, err := e2epod.NewPodClient(f).Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 71 framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", podSpec.Name) 72 73 ginkgo.By(fmt.Sprintf("Verify the pod (%v/%v) container is in the terminated state", pod.Namespace, podSpec.Name)) 74 gomega.Expect(pod.Status.ContainerStatuses).Should(gomega.HaveLen(1), "The pod container has %v status", len(pod.Status.ContainerStatuses)) 75 containerStatus := pod.Status.ContainerStatuses[0] 76 gomega.Expect(containerStatus.State.Terminated).ShouldNot(gomega.BeNil(), "The pod container is in not in the Terminated state") 77 78 ginkgo.By(fmt.Sprintf("Verifying the exit code for the terminated container is 137 for pod (%v/%v)", pod.Namespace, podSpec.Name)) 79 gomega.Expect(containerStatus.State.Terminated.ExitCode).Should(gomega.Equal(int32(137))) 80 81 ginkgo.By(fmt.Sprintf("Verify exit reason of the pod (%v/%v) container", f.Namespace.Name, podSpec.Name)) 82 gomega.Expect(containerStatus.State.Terminated.Reason).Should(gomega.Equal("Error"), "Container terminated by sigkill expect Error but got %v", containerStatus.State.Terminated.Reason) 83 }) 84 85 ginkgo.AfterEach(func() { 86 ginkgo.By(fmt.Sprintf("Deleting pod by removing finalizers: %s", podSpec.Name)) 87 e2epod.NewPodClient(f).RemoveFinalizer(context.TODO(), podSpec.Name, testFinalizer) 88 89 ginkgo.By(fmt.Sprintf("Confirm the pod was successfully deleted: %s", podSpec.Name)) 90 e2epod.WaitForPodNotFoundInNamespace(context.TODO(), f.ClientSet, podSpec.Name, f.Namespace.Name, f.Timeouts.PodDelete) 91 }) 92 }) 93 }) 94 95 func getSigkillTargetPod(podName string, ctnName string) *v1.Pod { 96 gracePeriod := int64(5) 97 return &v1.Pod{ 98 ObjectMeta: metav1.ObjectMeta{ 99 Name: podName, 100 // Using default test finalizer to keep exit status and code can be 101 // preserved after deleting the pod 102 Finalizers: []string{testFinalizer}, 103 }, 104 Spec: v1.PodSpec{ 105 RestartPolicy: v1.RestartPolicyNever, 106 Containers: []v1.Container{ 107 { 108 Name: ctnName, 109 Image: busyboxImage, 110 // In the main container, SIGTERM was trapped and later /tmp/healthy 111 // will be created for readiness probe to verify if the trap was 112 // executed successfully 113 Command: []string{ 114 "sh", 115 "-c", 116 "trap \"echo SIGTERM caught\" SIGTERM SIGINT; touch /tmp/healthy; /bin/sleep 1000", 117 }, 118 // Using readiness probe to guarantee signal handler registering finished 119 ReadinessProbe: &v1.Probe{ 120 InitialDelaySeconds: 1, 121 TimeoutSeconds: 2, 122 ProbeHandler: v1.ProbeHandler{ 123 Exec: &v1.ExecAction{ 124 Command: []string{"/bin/sh", "-c", "cat /tmp/healthy"}, 125 }, 126 }, 127 }, 128 }, 129 }, 130 TerminationGracePeriodSeconds: &gracePeriod, 131 }, 132 } 133 }