1
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
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 })
213
View as plain text