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  }