1
16
17 package node
18
19 import (
20 "context"
21 "fmt"
22 "time"
23
24 v1 "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 clientscheme "k8s.io/client-go/kubernetes/scheme"
30 "k8s.io/client-go/util/retry"
31 "k8s.io/kubernetes/test/e2e/framework"
32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
33 imageutils "k8s.io/kubernetes/test/utils/image"
34 admissionapi "k8s.io/pod-security-admission/api"
35
36 "github.com/onsi/ginkgo/v2"
37 "github.com/onsi/gomega"
38 )
39
40 var _ = SIGDescribe("Ephemeral Containers", framework.WithNodeConformance(), func() {
41 f := framework.NewDefaultFramework("ephemeral-containers-test")
42 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
43 var podClient *e2epod.PodClient
44 ginkgo.BeforeEach(func() {
45 podClient = e2epod.NewPodClient(f)
46 })
47
48
49
50
51 framework.ConformanceIt("will start an ephemeral container in an existing pod", func(ctx context.Context) {
52 ginkgo.By("creating a target pod")
53 pod := podClient.CreateSync(ctx, &v1.Pod{
54 ObjectMeta: metav1.ObjectMeta{Name: "ephemeral-containers-target-pod"},
55 Spec: v1.PodSpec{
56 Containers: []v1.Container{
57 {
58 Name: "test-container-1",
59 Image: imageutils.GetE2EImage(imageutils.BusyBox),
60 Command: []string{"/bin/sleep"},
61 Args: []string{"10000"},
62 },
63 },
64 },
65 })
66
67 ginkgo.By("adding an ephemeral container")
68 ecName := "debugger"
69 ec := &v1.EphemeralContainer{
70 EphemeralContainerCommon: v1.EphemeralContainerCommon{
71 Name: ecName,
72 Image: imageutils.GetE2EImage(imageutils.BusyBox),
73 Command: e2epod.GenerateScriptCmd("while true; do echo polo; sleep 2; done"),
74 Stdin: true,
75 TTY: true,
76 },
77 }
78 err := podClient.AddEphemeralContainerSync(ctx, pod, ec, time.Minute)
79 framework.ExpectNoError(err, "Failed to patch ephemeral containers in pod %q", e2epod.FormatPod(pod))
80
81 ginkgo.By("checking pod container endpoints")
82
83 output := e2epod.ExecCommandInContainer(f, pod.Name, ecName, "/bin/echo", "marco")
84 gomega.Expect(output).To(gomega.ContainSubstring("marco"))
85 log, err := e2epod.GetPodLogs(ctx, f.ClientSet, pod.Namespace, pod.Name, ecName)
86 framework.ExpectNoError(err, "Failed to get logs for pod %q ephemeral container %q", e2epod.FormatPod(pod), ecName)
87 gomega.Expect(log).To(gomega.ContainSubstring("polo"))
88 })
89
90
98 framework.ConformanceIt("should update the ephemeral containers in an existing pod", func(ctx context.Context) {
99 ginkgo.By("creating a target pod")
100 pod := podClient.CreateSync(ctx, &v1.Pod{
101 ObjectMeta: metav1.ObjectMeta{Name: "ephemeral-containers-target-pod"},
102 Spec: v1.PodSpec{
103 Containers: []v1.Container{
104 {
105 Name: "test-container-1",
106 Image: imageutils.GetE2EImage(imageutils.BusyBox),
107 Command: []string{"/bin/sleep"},
108 Args: []string{"10000"},
109 },
110 },
111 },
112 })
113
114 ginkgo.By("adding an ephemeral container")
115 ecName := "debugger"
116 ec := &v1.EphemeralContainer{
117 EphemeralContainerCommon: v1.EphemeralContainerCommon{
118 Name: ecName,
119 Image: imageutils.GetE2EImage(imageutils.BusyBox),
120 Command: e2epod.GenerateScriptCmd("while true; do echo polo; sleep 2; done"),
121 Stdin: true,
122 TTY: true,
123 },
124 }
125 err := podClient.AddEphemeralContainerSync(ctx, pod, ec, time.Minute)
126 framework.ExpectNoError(err, "Failed to patch ephemeral containers in pod %q", e2epod.FormatPod(pod))
127
128 ginkgo.By("checking pod container endpoints")
129
130 output := e2epod.ExecCommandInContainer(f, pod.Name, ecName, "/bin/echo", "marco")
131 gomega.Expect(output).To(gomega.ContainSubstring("marco"))
132 log, err := e2epod.GetPodLogs(ctx, f.ClientSet, pod.Namespace, pod.Name, ecName)
133 framework.ExpectNoError(err, "Failed to get logs for pod %q ephemeral container %q", e2epod.FormatPod(pod), ecName)
134 gomega.Expect(log).To(gomega.ContainSubstring("polo"))
135
136 ginkgo.By(fmt.Sprintf("checking pod %q has only one ephemeralcontainer", pod.Name))
137 podResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
138 unstruct, err := f.DynamicClient.Resource(podResource).Namespace(f.Namespace.Name).Get(ctx, "ephemeral-containers-target-pod", metav1.GetOptions{}, "ephemeralcontainers")
139 framework.ExpectNoError(err, "can't get ephermalcontainers")
140 verifyPod, err := unstructuredToPod(unstruct)
141 framework.ExpectNoError(err, "Getting the %q pod's ephemeralcontainers", verifyPod.Name)
142 gomega.Expect(verifyPod.Spec.EphemeralContainers).To(gomega.HaveLen(1), "checking ephemeralContainer count")
143
144 ginkgo.By(fmt.Sprintf("adding another ephemeralcontainer to pod %q", pod.Name))
145 var podToUpdate *v1.Pod
146 err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
147 podToUpdate, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{})
148 framework.ExpectNoError(err, "Unable to retrieve pod %s", pod.Name)
149
150 podToUpdate.Spec.EphemeralContainers = append(podToUpdate.Spec.EphemeralContainers, v1.EphemeralContainer{
151 EphemeralContainerCommon: v1.EphemeralContainerCommon{
152 Name: "debugger2",
153 Image: imageutils.GetE2EImage(imageutils.Agnhost),
154 ImagePullPolicy: "IfNotPresent",
155 TerminationMessagePolicy: "File",
156 },
157 })
158 _, err = podClient.UpdateEphemeralContainers(context.TODO(), pod.Name, podToUpdate, metav1.UpdateOptions{})
159 return err
160 })
161 framework.ExpectNoError(err, "Failed to update ephemeral container.")
162
163 ginkgo.By(fmt.Sprintf("checking pod %q has only two ephemeralcontainers", pod.Name))
164 unstruct, err = f.DynamicClient.Resource(podResource).Namespace(f.Namespace.Name).Get(ctx, "ephemeral-containers-target-pod", metav1.GetOptions{}, "ephemeralcontainers")
165 framework.ExpectNoError(err, "can't get ephermalcontainers")
166 verifyPod, err = unstructuredToPod(unstruct)
167 framework.ExpectNoError(err, "Getting the %q pod's ephemeralcontainers", verifyPod.Name)
168 gomega.Expect(verifyPod.Spec.EphemeralContainers).To(gomega.HaveLen(2), "checking ephemeralContainer count")
169 })
170 })
171
172 func unstructuredToPod(obj *unstructured.Unstructured) (*v1.Pod, error) {
173 json, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
174 if err != nil {
175 return nil, err
176 }
177 p := &v1.Pod{}
178 err = runtime.DecodeInto(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), json, p)
179
180 return p, err
181 }
182
View as plain text