k8s.io/kubernetes@v1.29.3/test/e2e/common/node/ephemeral_containers.go (about) 1 /* 2 Copyright 2021 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 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 // Release: 1.25 49 // Testname: Ephemeral Container Creation 50 // Description: Adding an ephemeral container to pod.spec MUST result in the container running. 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 // Can't use anything depending on kubectl here because it's not available in the node test environment 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 /* 91 Release: v1.28 92 Testname: Ephemeral Container, update ephemeral containers 93 Description: Adding an ephemeral container to pod.spec MUST result in the container 94 running. There MUST now be only one ephermal container found. Updating the pod with 95 another ephemeral container MUST succeed. There MUST now be two ephermal containers 96 found. 97 */ 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 // Can't use anything depending on kubectl here because it's not available in the node test environment 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 }