Change:
This issue is to discuss an e2e testing plan for Ephemeral Containers. /sig node
As a first step towards this issue, I am planning to create node-e2e tests (under ) to verify the following: With a running pod client adds a valid EphemeralContainer resource, and the ephemeral container starts from the ephemeral container, list processes of itself and other containers, and then signal other processes (if is ) from the ephemeral container, ping & curl other containers try to read/write to stdin/stdout of the ephemeral container verify resource request/limit values against calculated expectations check oom scores of processes in the ephemeral container verify volume mounts restarts according to policy, when process exits client tries to add EphemeralContainer resource with ports (should be rejected) client tries to add EphemeralContainer resource with probes (should be rejected) client tries to add EphemeralContainer resource with resources (should be rejected) client tries to add EphemeralContainer resource with securityContext (should be rejected) removes the ephemeral container, and the ephemeral container is stopped and deleted adds multiple ephemeral containers, should all startWith pod of non-running status (TBD)
Awesome, thanks! This is a great start, here are some things to look out for: Visibility should work, but I expect signaling to fail without which is not currently allowed. iirc was interested in how this would be tested for ephemeral containers. Can you point us towards any examples that would help us get started? Ephemeral containers will never restart (and the policy should always be never) These will be denied by the api server without ever making it to the kubelet, so they are better tested in the apiserver integration tests (and I think they are already). Removing ephemeral containers is not currently allowed. /assign
Validation of ephemeralContainers is well tested in Testing of removing ephemeral containers is in ,
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
Stale issues rot after 30d of inactivity. Mark the issue as fresh with . Rotten issues close after an additional 30d of inactivity. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle rotten
Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . Send feedback to sig-testing, kubernetes/test-infra and/or . /close
Closing this issue. <detailsIn response to : Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . /close Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen /assign /priority important-soon
Reopened this issue. <detailsIn response to : /reopen /assign /priority important-soon Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . Send feedback to sig-contributor-experience at . /close
Closing this issue. <detailsIn response to : Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . /close Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen
Reopened this issue. <detailsIn response to : /reopen Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . Send feedback to sig-contributor-experience at . /close
Closing this issue. <detailsIn response to : Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . /close Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen /remove-lifecycle rotten
Reopened this issue. <detailsIn response to : /reopen /remove-lifecycle rotten Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details

Discussion:
} } <ins> func isContainerRunning(c clientset.Interface, namespace, podName, containerName string) wait.ConditionFunc { return func() (bool, error) { pod, err := c.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) if err != nil { return false, err } for _, statuses := range [][]v1.ContainerStatus{pod.Status.ContainerStatuses, pod.Status.InitContainerStatuses, pod.Status.EphemeralContainerStatuses} { for _, cs := range statuses { if cs.Name == containerName { return cs.State.Running != nil, nil } } } return false, nil } } </ins> // LogPodStates logs basic info of provided pods for debugging. func LogPodStates(pods []v1.Pod) { // Find maximum widths for pod, node, and phase strings for column printing.

} return nil } <ins> // WaitForContainerRunning waits for the given Pod container to have a state of running func WaitForContainerRunning(c clientset.Interface, namespace, podName, containerName string, timeout time.Duration) error { return wait.PollImmediate(poll, timeout, isContainerRunning(c, namespace, podName, containerName)) } </ins>

import ( "context" <ins> "encoding/json" </ins> "fmt" "regexp" "sync"

apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" <ins> "k8s.io/apimachinery/pkg/types" </ins> "k8s.io/apimachinery/pkg/util/sets" <ins> "k8s.io/apimachinery/pkg/util/strategicpatch" </ins> "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/scheme" v1core "k8s.io/client-go/kubernetes/typed/core/v1"

"github.com/onsi/gomega" // TODO: Remove the following imports (ref: https://github.com/kubernetes/kubernetes/issues/81245) <ins> "k8s.io/kubernetes/pkg/kubelet/util/format" </ins> e2epod "k8s.io/kubernetes/test/e2e/framework/pod" )

})) } <ins> // AddEphemeralContainerSync adds an EphemeralContainer to a pod and waits for it to be running. func (c *PodClient) AddEphemeralContainerSync(pod *v1.Pod, ec *v1.EphemeralContainer, timeout time.Duration) { namespace := c.f.Namespace.Name podJS, err := json.Marshal(pod) ExpectNoError(err, "error creating JSON for pod %q", format.Pod(pod)) ecPod := pod.DeepCopy() ecPod.Spec.EphemeralContainers = append(ecPod.Spec.EphemeralContainers, *ec) ecJS, err := json.Marshal(ecPod) ExpectNoError(err, "error creating JSON for pod with ephemeral container %q", format.Pod(pod)) patch, err := strategicpatch.CreateTwoWayMergePatch(podJS, ecJS, pod) ExpectNoError(err, "error creating patch to add ephemeral container %q", format.Pod(pod)) _, err = c.Patch(context.TODO(), pod.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{}, "ephemeralcontainers") ExpectNoError(err, "Failed to patch ephemeral containers in pod %q", format.Pod(pod)) ExpectNoError(e2epod.WaitForContainerRunning(c.f.ClientSet, namespace, pod.Name, ec.Name, timeout)) } </ins> // DeleteSync deletes the pod and wait for the pod to disappear for `timeout`. If the pod doesn't // disappear before the timeout, it will fail the test. func (c *PodClient) DeleteSync(name string, options metav1.DeleteOptions, timeout time.Duration) {

<ins> /* Copyright 2021 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package e2enode import ( "time" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/test/e2e/framework" imageutils "k8s.io/kubernetes/test/utils/image" "github.com/onsi/ginkgo" ) var _ = SIGDescribe("Ephemeral Containers [Feature:EphemeralContainers][NodeAlphaFeature:EphemeralContainers]", func() { f := framework.NewDefaultFramework("ephemeral-containers-test") var podClient *framework.PodClient ginkgo.BeforeEach(func() { podClient = f.PodClient() }) ginkgo.It("will start an ephemeral container in an existing pod", func() { ginkgo.By("creating a target pod") pod := podClient.CreateSync(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "ephemeral-containers-target-pod"}, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name:    "test-container-1", Image:   imageutils.GetE2EImage(imageutils.BusyBox), Command: []string{"/bin/sleep"}, Args:    []string{"10000"}, }, }, }, }) ginkgo.By("adding an ephemeral container") ec := &v1.EphemeralContainer{ EphemeralContainerCommon: v1.EphemeralContainerCommon{ Name:    "debugger", Image:   imageutils.GetE2EImage(imageutils.BusyBox), Command: []string{"/bin/sh"}, Stdin:   true, TTY:     true, }, } podClient.AddEphemeralContainerSync(pod, ec, time.Minute) ginkgo.By("confirm that the container is really running") marco := f.ExecCommandInContainer(pod.Name, "debugger", "/bin/echo", "polo") framework.ExpectEqual(marco, "polo") }) }) </ins>