1
16
17 package node
18
19 import (
20 "context"
21 "encoding/json"
22 "fmt"
23 "net"
24 "time"
25
26 v1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/util/uuid"
29 "k8s.io/apimachinery/pkg/util/wait"
30 clientset "k8s.io/client-go/kubernetes"
31 "k8s.io/kubernetes/pkg/cluster/ports"
32 "k8s.io/kubernetes/test/e2e/framework"
33 e2ekubelet "k8s.io/kubernetes/test/e2e/framework/kubelet"
34 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
35 imageutils "k8s.io/kubernetes/test/utils/image"
36 admissionapi "k8s.io/pod-security-admission/api"
37
38 "github.com/onsi/ginkgo/v2"
39 )
40
41
42 type State struct {
43 Received map[string]int
44 }
45
46 func testPreStop(ctx context.Context, c clientset.Interface, ns string) {
47
48 podDescr := e2epod.NewAgnhostPod(ns, "server", nil, nil, []v1.ContainerPort{{ContainerPort: 8080}}, "nettest")
49 ginkgo.By(fmt.Sprintf("Creating server pod %s in namespace %s", podDescr.Name, ns))
50 podDescr, err := c.CoreV1().Pods(ns).Create(ctx, podDescr, metav1.CreateOptions{})
51 framework.ExpectNoError(err, fmt.Sprintf("creating pod %s", podDescr.Name))
52
53
54 ginkgo.DeferCleanup(func(ctx context.Context) error {
55 ginkgo.By("Deleting the server pod")
56 return c.CoreV1().Pods(ns).Delete(ctx, podDescr.Name, metav1.DeleteOptions{})
57 })
58
59 ginkgo.By("Waiting for pods to come up.")
60 err = e2epod.WaitForPodRunningInNamespace(ctx, c, podDescr)
61 framework.ExpectNoError(err, "waiting for server pod to start")
62
63 val := "{\"Source\": \"prestop\"}"
64
65 podOut, err := c.CoreV1().Pods(ns).Get(ctx, podDescr.Name, metav1.GetOptions{})
66 framework.ExpectNoError(err, "getting pod info")
67
68 podURL := net.JoinHostPort(podOut.Status.PodIP, "8080")
69
70 preStopDescr := &v1.Pod{
71 ObjectMeta: metav1.ObjectMeta{
72 Name: "tester",
73 },
74 Spec: v1.PodSpec{
75 Containers: []v1.Container{
76 {
77 Name: "tester",
78 Image: imageutils.GetE2EImage(imageutils.BusyBox),
79 Command: []string{"sleep", "600"},
80 Lifecycle: &v1.Lifecycle{
81 PreStop: &v1.LifecycleHandler{
82 Exec: &v1.ExecAction{
83 Command: []string{
84 "wget", "-O-", "--post-data=" + val, fmt.Sprintf("http://%s/write", podURL),
85 },
86 },
87 },
88 },
89 },
90 },
91 },
92 }
93
94 ginkgo.By(fmt.Sprintf("Creating tester pod %s in namespace %s", preStopDescr.Name, ns))
95 preStopDescr, err = c.CoreV1().Pods(ns).Create(ctx, preStopDescr, metav1.CreateOptions{})
96 framework.ExpectNoError(err, fmt.Sprintf("creating pod %s", preStopDescr.Name))
97 deletePreStop := true
98
99
100 ginkgo.DeferCleanup(func(ctx context.Context) error {
101 if deletePreStop {
102 ginkgo.By("Deleting the tester pod")
103 return c.CoreV1().Pods(ns).Delete(ctx, preStopDescr.Name, metav1.DeleteOptions{})
104 }
105 return nil
106 })
107
108 err = e2epod.WaitForPodRunningInNamespace(ctx, c, preStopDescr)
109 framework.ExpectNoError(err, "waiting for tester pod to start")
110
111
112 ginkgo.By("Deleting pre-stop pod")
113 if err := c.CoreV1().Pods(ns).Delete(ctx, preStopDescr.Name, metav1.DeleteOptions{}); err == nil {
114 deletePreStop = false
115 }
116 framework.ExpectNoError(err, fmt.Sprintf("deleting pod: %s", preStopDescr.Name))
117
118
119 err = wait.Poll(time.Second*5, time.Second*60, func() (bool, error) {
120
121 ctx, cancel := context.WithTimeout(ctx, framework.SingleCallTimeout)
122 defer cancel()
123
124 var body []byte
125 body, err = c.CoreV1().RESTClient().Get().
126 Namespace(ns).
127 Resource("pods").
128 SubResource("proxy").
129 Name(podDescr.Name).
130 Suffix("read").
131 DoRaw(ctx)
132
133 if err != nil {
134 if ctx.Err() != nil {
135 framework.Failf("Error validating prestop: %v", err)
136 return true, err
137 }
138 ginkgo.By(fmt.Sprintf("Error validating prestop: %v", err))
139 } else {
140 framework.Logf("Saw: %s", string(body))
141 state := State{}
142 err := json.Unmarshal(body, &state)
143 if err != nil {
144 framework.Logf("Error parsing: %v", err)
145 return false, nil
146 }
147 if state.Received["prestop"] != 0 {
148 return true, nil
149 }
150 }
151 return false, nil
152 })
153 framework.ExpectNoError(err, "validating pre-stop.")
154 }
155
156 var _ = SIGDescribe("PreStop", func() {
157 f := framework.NewDefaultFramework("prestop")
158 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
159 var podClient *e2epod.PodClient
160 ginkgo.BeforeEach(func() {
161 podClient = e2epod.NewPodClient(f)
162 })
163
164
169 framework.ConformanceIt("should call prestop when killing a pod", func(ctx context.Context) {
170 testPreStop(ctx, f.ClientSet, f.Namespace.Name)
171 })
172
173 ginkgo.It("graceful pod terminated should wait until preStop hook completes the process", func(ctx context.Context) {
174 gracefulTerminationPeriodSeconds := int64(30)
175 ginkgo.By("creating the pod")
176 name := "pod-prestop-hook-" + string(uuid.NewUUID())
177 pod := getPodWithpreStopLifeCycle(name)
178
179 ginkgo.By("submitting the pod to kubernetes")
180 podClient.Create(ctx, pod)
181
182 ginkgo.By("waiting for pod running")
183 framework.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name))
184
185 var err error
186 pod, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{})
187 framework.ExpectNoError(err, "failed to GET scheduled pod")
188
189 ginkgo.By("deleting the pod gracefully")
190 err = podClient.Delete(ctx, pod.Name, *metav1.NewDeleteOptions(gracefulTerminationPeriodSeconds))
191 framework.ExpectNoError(err, "failed to delete pod")
192
193
194
195 time.Sleep(15 * time.Second)
196
197 ginkgo.By("verifying the pod is running while in the graceful period termination")
198 result := &v1.PodList{}
199 err = wait.Poll(time.Second*5, time.Second*60, func() (bool, error) {
200 client, err := e2ekubelet.ProxyRequest(ctx, f.ClientSet, pod.Spec.NodeName, "pods", ports.KubeletPort)
201 framework.ExpectNoError(err, "failed to get the pods of the node")
202 err = client.Into(result)
203 framework.ExpectNoError(err, "failed to parse the pods of the node")
204
205 for _, kubeletPod := range result.Items {
206 if pod.Name != kubeletPod.Name {
207 continue
208 } else if kubeletPod.Status.Phase == v1.PodRunning {
209 framework.Logf("pod is running")
210 return true, err
211 }
212 }
213 return false, err
214 })
215
216 framework.ExpectNoError(err, "validate-pod-is-running")
217 })
218 })
219
220 func getPodWithpreStopLifeCycle(name string) *v1.Pod {
221 return &v1.Pod{
222 ObjectMeta: metav1.ObjectMeta{
223 Name: name,
224 },
225 Spec: v1.PodSpec{
226 Containers: []v1.Container{
227 {
228 Name: "nginx",
229 Image: imageutils.GetE2EImage(imageutils.Nginx),
230 Lifecycle: &v1.Lifecycle{
231 PreStop: &v1.LifecycleHandler{
232 Exec: &v1.ExecAction{
233 Command: []string{"sh", "-c", "while true; do echo preStop; sleep 1; done"},
234 },
235 },
236 },
237 },
238 },
239 },
240 }
241 }
242
View as plain text