diff --git a/api/api-rules/violation_exceptions.list b/api/api-rules/violation_exceptions.list index 834c277e282..28d1f223698 100644 --- a/api/api-rules/violation_exceptions.list +++ b/api/api-rules/violation_exceptions.list @@ -93,7 +93,6 @@ API rule violation: list_type_missing,k8s.io/api/core/v1,EphemeralContainerCommo API rule violation: list_type_missing,k8s.io/api/core/v1,EphemeralContainerCommon,Ports API rule violation: list_type_missing,k8s.io/api/core/v1,EphemeralContainerCommon,VolumeDevices API rule violation: list_type_missing,k8s.io/api/core/v1,EphemeralContainerCommon,VolumeMounts -API rule violation: list_type_missing,k8s.io/api/core/v1,EphemeralContainers,EphemeralContainers API rule violation: list_type_missing,k8s.io/api/core/v1,ExecAction,Command API rule violation: list_type_missing,k8s.io/api/core/v1,FCVolumeSource,TargetWWNs API rule violation: list_type_missing,k8s.io/api/core/v1,FCVolumeSource,WWIDs diff --git a/pkg/apis/core/register.go b/pkg/apis/core/register.go index 36856ef6cc7..882f31795a2 100644 --- a/pkg/apis/core/register.go +++ b/pkg/apis/core/register.go @@ -96,7 +96,6 @@ func addKnownTypes(scheme *runtime.Scheme) error { &RangeAllocation{}, &ConfigMap{}, &ConfigMapList{}, - &EphemeralContainers{}, ) return nil diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index a5ec5093617..cce61bd8538 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -4439,20 +4439,6 @@ type Binding struct { Target ObjectReference } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// EphemeralContainers is a list of ephemeral containers used with the Pod ephemeralcontainers subresource. -type EphemeralContainers struct { - metav1.TypeMeta - // +optional - metav1.ObjectMeta - - // A list of ephemeral containers associated with this pod. New ephemeral containers - // may be appended to this list, but existing ephemeral containers may not be removed - // or modified. - EphemeralContainers []EphemeralContainer -} - // Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out. type Preconditions struct { // Specifies the target UID. diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 786560a8110..2cf88faaf65 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -293,23 +293,18 @@ type EphemeralContainersREST struct { var _ = rest.Patcher(&EphemeralContainersREST{}) -// Get of this endpoint will return the list of ephemeral containers in this pod +// Get retrieves the object from the storage. It is required to support Patch. func (r *EphemeralContainersREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) { return nil, errors.NewBadRequest("feature EphemeralContainers disabled") } - obj, err := r.store.Get(ctx, name, options) - if err != nil { - return nil, err - } - - return ephemeralContainersInPod(obj.(*api.Pod)), nil + return r.store.Get(ctx, name, options) } -// New creates a new EphemeralContainers resource +// New creates a new pod resource func (r *EphemeralContainersREST) New() runtime.Object { - return &api.EphemeralContainers{} + return &api.Pod{} } // Update alters the EphemeralContainers field in PodSpec @@ -318,76 +313,7 @@ func (r *EphemeralContainersREST) Update(ctx context.Context, name string, objIn return nil, false, errors.NewBadRequest("feature EphemeralContainers disabled") } - obj, err := r.store.Get(ctx, name, &metav1.GetOptions{}) - if err != nil { - return nil, false, err - } - pod := obj.(*api.Pod) - - // Build an UpdatedObjectInfo to pass to the pod store. - // It is given the currently stored v1.Pod and transforms it to the new pod that should be stored. - updatedPodInfo := rest.DefaultUpdatedObjectInfo(pod, func(ctx context.Context, oldObject, _ runtime.Object) (newObject runtime.Object, err error) { - oldPod, ok := oldObject.(*api.Pod) - if !ok { - return nil, fmt.Errorf("unexpected type for Pod %T", oldObject) - } - - newEphemeralContainersObj, err := objInfo.UpdatedObject(ctx, ephemeralContainersInPod(oldPod)) - if err != nil { - return nil, err - } - newEphemeralContainers, ok := newEphemeralContainersObj.(*api.EphemeralContainers) - if !ok { - return nil, fmt.Errorf("unexpected type for EphemeralContainers %T", newEphemeralContainersObj) - } - - // avoid mutating - newPod := oldPod.DeepCopy() - // identity, version (make sure we're working with the right object, instance, and version) - newPod.Name = newEphemeralContainers.Name - newPod.Namespace = newEphemeralContainers.Namespace - newPod.UID = newEphemeralContainers.UID - newPod.ResourceVersion = newEphemeralContainers.ResourceVersion - // ephemeral containers - newPod.Spec.EphemeralContainers = newEphemeralContainers.EphemeralContainers - - return newPod, nil - }) - - // Validation should be passed the API kind (EphemeralContainers) rather than the storage kind. - obj, _, err = r.store.Update(ctx, name, updatedPodInfo, toEphemeralContainersCreateValidation(createValidation), toEphemeralContainersUpdateValidation(updateValidation), false, options) - if err != nil { - return nil, false, err - } - return ephemeralContainersInPod(obj.(*api.Pod)), false, err -} - -func toEphemeralContainersCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc { - return func(ctx context.Context, obj runtime.Object) error { - return f(ctx, ephemeralContainersInPod(obj.(*api.Pod))) - } -} - -func toEphemeralContainersUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc { - return func(ctx context.Context, obj, old runtime.Object) error { - return f(ctx, ephemeralContainersInPod(obj.(*api.Pod)), ephemeralContainersInPod(old.(*api.Pod))) - } -} - -// Extract the list of Ephemeral Containers from a Pod -func ephemeralContainersInPod(pod *api.Pod) *api.EphemeralContainers { - ephemeralContainers := pod.Spec.EphemeralContainers - if ephemeralContainers == nil { - ephemeralContainers = []api.EphemeralContainer{} - } - return &api.EphemeralContainers{ - ObjectMeta: metav1.ObjectMeta{ - Name: pod.Name, - Namespace: pod.Namespace, - UID: pod.UID, - ResourceVersion: pod.ResourceVersion, - CreationTimestamp: pod.CreationTimestamp, - }, - EphemeralContainers: ephemeralContainers, - } + // We are explicitly setting forceAllowCreate to false in the call to the underlying storage because + // subresources should never allow create on update. + return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) } diff --git a/pkg/registry/core/pod/strategy.go b/pkg/registry/core/pod/strategy.go index 15aeb7b5d38..bee96024122 100644 --- a/pkg/registry/core/pod/strategy.go +++ b/pkg/registry/core/pod/strategy.go @@ -205,6 +205,18 @@ type podEphemeralContainersStrategy struct { // EphemeralContainersStrategy wraps and exports the used podStrategy for the storage package. var EphemeralContainersStrategy = podEphemeralContainersStrategy{Strategy} +func (podEphemeralContainersStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { + newPod := obj.(*api.Pod) + oldPod := old.(*api.Pod) + + // Drop all changes except for pod.Spec.EphemeralContainers + ec := newPod.Spec.EphemeralContainers + *newPod = *oldPod + newPod.Spec.EphemeralContainers = ec + + podutil.DropDisabledPodFields(newPod, oldPod) +} + func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { newPod := obj.(*api.Pod) oldPod := old.(*api.Pod) diff --git a/staging/src/k8s.io/api/core/v1/register.go b/staging/src/k8s.io/api/core/v1/register.go index 8da1ddeade1..1aac0cb41e0 100644 --- a/staging/src/k8s.io/api/core/v1/register.go +++ b/staging/src/k8s.io/api/core/v1/register.go @@ -88,7 +88,6 @@ func addKnownTypes(scheme *runtime.Scheme) error { &RangeAllocation{}, &ConfigMap{}, &ConfigMapList{}, - &EphemeralContainers{}, ) // Add common types diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index be3a9ae73f9..c0068c47445 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -3689,8 +3689,7 @@ type PodStatusResult struct { } // +genclient -// +genclient:method=GetEphemeralContainers,verb=get,subresource=ephemeralcontainers,result=EphemeralContainers -// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers,input=EphemeralContainers,result=EphemeralContainers +// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Pod is a collection of containers that can run on a host. This resource is created @@ -5150,22 +5149,6 @@ type Binding struct { Target ObjectReference `json:"target" protobuf:"bytes,2,opt,name=target"` } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// A list of ephemeral containers used with the Pod ephemeralcontainers subresource. -type EphemeralContainers struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // A list of ephemeral containers associated with this pod. New ephemeral containers - // may be appended to this list, but existing ephemeral containers may not be removed - // or modified. - // +patchMergeKey=name - // +patchStrategy=merge - EphemeralContainers []EphemeralContainer `json:"ephemeralContainers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=ephemeralContainers"` -} - // Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out. // +k8s:openapi-gen=false type Preconditions struct { diff --git a/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod.go b/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod.go index 14b7bd0f04a..6692e454394 100644 --- a/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod.go +++ b/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod.go @@ -258,31 +258,72 @@ func (c *pods) ApplyStatus(ctx context.Context, pod *corev1.PodApplyConfiguratio return } +func ephemeralContainersInPod(pod *v1.Pod) *v1.EphemeralContainers { + ephemeralContainers := pod.Spec.EphemeralContainers + if ephemeralContainers == nil { + ephemeralContainers = []v1.EphemeralContainer{} + } + return &v1.EphemeralContainers{ + ObjectMeta: metav1.ObjectMeta{ + Name: pod.Name, + Namespace: pod.Namespace, + UID: pod.UID, + ResourceVersion: pod.ResourceVersion, + CreationTimestamp: pod.CreationTimestamp, + }, + EphemeralContainers: ephemeralContainers, + } + +} + // GetEphemeralContainers takes name of the pod, and returns the corresponding v1.EphemeralContainers object, and an error if there is any. -func (c *pods) GetEphemeralContainers(ctx context.Context, podName string, options metav1.GetOptions) (result *v1.EphemeralContainers, err error) { - result = &v1.EphemeralContainers{} - err = c.client.Get(). +func (c *pods) GetEphemeralContainers(ctx context.Context, podName string, options metav1.GetOptions) (*v1.EphemeralContainers, error) { + pod := &v1.Pod{} + err := c.client.Get(). Namespace(c.ns). Resource("pods"). Name(podName). SubResource("ephemeralcontainers"). VersionedParams(&options, scheme.ParameterCodec). Do(ctx). - Into(result) - return + Into(pod) + + if err != nil { + return nil, err + } + + return ephemeralContainersInPod(pod), nil } // UpdateEphemeralContainers takes the top resource name and the representation of a ephemeralContainers and updates it. Returns the server's representation of the ephemeralContainers, and an error, if there is any. -func (c *pods) UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *v1.EphemeralContainers, opts metav1.UpdateOptions) (result *v1.EphemeralContainers, err error) { - result = &v1.EphemeralContainers{} +func (c *pods) UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *v1.EphemeralContainers, opts metav1.UpdateOptions) (*v1.EphemeralContainers, error) { + pod := &v1.Pod{} + err := c.client.Get(). + Namespace(c.ns). + Resource("pods"). + Name(podName). + SubResource("ephemeralcontainers"). + VersionedParams(&metav1.GetOptions{}, scheme.ParameterCodec). + Do(ctx). + Into(pod) + if err != nil { + return nil, err + } + + result := &v1.Pod{} + pod.Spec.EphemeralContainers = ephemeralContainers.EphemeralContainers err = c.client.Put(). Namespace(c.ns). Resource("pods"). Name(podName). SubResource("ephemeralcontainers"). VersionedParams(&opts, scheme.ParameterCodec). - Body(ephemeralContainers). + Body(pod). Do(ctx). Into(result) - return + if err != nil { + return nil, err + } + + return ephemeralContainersInPod(pod), nil } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go index abcb2530468..28d34237c03 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go @@ -382,25 +382,22 @@ func (o *DebugOptions) visitPod(ctx context.Context, pod *corev1.Pod) (*corev1.P // debugByEphemeralContainer runs an EphemeralContainer in the target Pod for use as a debug container func (o *DebugOptions) debugByEphemeralContainer(ctx context.Context, pod *corev1.Pod) (*corev1.Pod, string, error) { pods := o.podClient.Pods(pod.Namespace) - ec, err := pods.GetEphemeralContainers(ctx, pod.Name, metav1.GetOptions{}) + klog.V(2).Infof("existing ephemeral containers: %v", pod.Spec.EphemeralContainers) + + debugContainer := o.generateDebugContainer(pod) + klog.V(2).Infof("new ephemeral container: %#v", debugContainer) + + pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, *debugContainer) + result, err := pods.UpdateEphemeralContainers(ctx, pod.Name, pod, metav1.UpdateOptions{}) if err != nil { // The pod has already been fetched at this point, so a NotFound error indicates the ephemeralcontainers subresource wasn't found. if serr, ok := err.(*errors.StatusError); ok && serr.Status().Reason == metav1.StatusReasonNotFound { return nil, "", fmt.Errorf("ephemeral containers are disabled for this cluster (error from server: %q).", err) } - return nil, "", err - } - klog.V(2).Infof("existing ephemeral containers: %v", ec.EphemeralContainers) - - debugContainer := o.generateDebugContainer(pod) - klog.V(2).Infof("new ephemeral container: %#v", debugContainer) - ec.EphemeralContainers = append(ec.EphemeralContainers, *debugContainer) - _, err = pods.UpdateEphemeralContainers(ctx, pod.Name, ec, metav1.UpdateOptions{}) - if err != nil { return nil, "", fmt.Errorf("error updating ephemeral containers: %v", err) } - return pod, debugContainer.Name, nil + return result, debugContainer.Name, nil } // debugByCopy runs a copy of the target Pod with a debug container added or an original container modified diff --git a/test/integration/pods/pods_test.go b/test/integration/pods/pods_test.go index 252f7014c14..1ed8a0384c4 100644 --- a/test/integration/pods/pods_test.go +++ b/test/integration/pods/pods_test.go @@ -235,31 +235,27 @@ func TestPodCreateEphemeralContainers(t *testing.T) { // setUpEphemeralContainers creates a pod that has Ephemeral Containers. This is a two step // process because Ephemeral Containers are not allowed during pod creation. -func setUpEphemeralContainers(podsClient typedv1.PodInterface, pod *v1.Pod, containers []v1.EphemeralContainer) error { - if _, err := podsClient.Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("failed to create pod: %v", err) +func setUpEphemeralContainers(podsClient typedv1.PodInterface, pod *v1.Pod, containers []v1.EphemeralContainer) (*v1.Pod, error) { + result, err := podsClient.Create(context.TODO(), pod, metav1.CreateOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to create pod: %v", err) } if len(containers) == 0 { - return nil + return result, nil } pod.Spec.EphemeralContainers = containers if _, err := podsClient.Update(context.TODO(), pod, metav1.UpdateOptions{}); err == nil { - return fmt.Errorf("unexpected allowed direct update of ephemeral containers during set up: %v", err) + return nil, fmt.Errorf("unexpected allowed direct update of ephemeral containers during set up: %v", err) } - ec, err := podsClient.GetEphemeralContainers(context.TODO(), pod.Name, metav1.GetOptions{}) + result, err = podsClient.UpdateEphemeralContainers(context.TODO(), pod.Name, pod, metav1.UpdateOptions{}) if err != nil { - return fmt.Errorf("unable to get ephemeral containers for test case set up: %v", err) + return nil, fmt.Errorf("failed to update ephemeral containers for test case set up: %v", err) } - ec.EphemeralContainers = containers - if _, err = podsClient.UpdateEphemeralContainers(context.TODO(), pod.Name, ec, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("failed to update ephemeral containers for test case set up: %v", err) - } - - return nil + return result, nil } func TestPodPatchEphemeralContainers(t *testing.T) { @@ -303,12 +299,14 @@ func TestPodPatchEphemeralContainers(t *testing.T) { original: nil, patchType: types.StrategicMergePatchType, patchBody: []byte(`{ - "ephemeralContainers": [{ - "name": "debugger1", - "image": "debugimage", - "imagePullPolicy": "Always", - "terminationMessagePolicy": "File" - }] + "spec": { + "ephemeralContainers": [{ + "name": "debugger1", + "image": "debugimage", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File" + }] + } }`), valid: true, }, @@ -317,12 +315,14 @@ func TestPodPatchEphemeralContainers(t *testing.T) { original: nil, patchType: types.MergePatchType, patchBody: []byte(`{ - "ephemeralContainers":[{ - "name": "debugger1", - "image": "debugimage", - "imagePullPolicy": "Always", - "terminationMessagePolicy": "File" - }] + "spec": { + "ephemeralContainers":[{ + "name": "debugger1", + "image": "debugimage", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File" + }] + } }`), valid: true, }, @@ -330,15 +330,17 @@ func TestPodPatchEphemeralContainers(t *testing.T) { name: "create single container (JSON)", original: nil, patchType: types.JSONPatchType, + // Because ephemeralContainers is optional, a JSON patch of an empty ephemeralContainers must add the + // list rather than simply appending to it. patchBody: []byte(`[{ "op":"add", - "path":"/ephemeralContainers/-", - "value":{ + "path":"/spec/ephemeralContainers", + "value":[{ "name":"debugger1", "image":"debugimage", "imagePullPolicy": "Always", "terminationMessagePolicy": "File" - } + }] }]`), valid: true, }, @@ -356,12 +358,14 @@ func TestPodPatchEphemeralContainers(t *testing.T) { }, patchType: types.StrategicMergePatchType, patchBody: []byte(`{ - "ephemeralContainers":[{ - "name": "debugger2", - "image": "debugimage", - "imagePullPolicy": "Always", - "terminationMessagePolicy": "File" - }] + "spec": { + "ephemeralContainers":[{ + "name": "debugger2", + "image": "debugimage", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File" + }] + } }`), valid: true, }, @@ -379,17 +383,19 @@ func TestPodPatchEphemeralContainers(t *testing.T) { }, patchType: types.MergePatchType, patchBody: []byte(`{ - "ephemeralContainers":[{ - "name": "debugger1", - "image": "debugimage", - "imagePullPolicy": "Always", - "terminationMessagePolicy": "File" - },{ - "name": "debugger2", - "image": "debugimage", - "imagePullPolicy": "Always", - "terminationMessagePolicy": "File" - }] + "spec": { + "ephemeralContainers":[{ + "name": "debugger1", + "image": "debugimage", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File" + },{ + "name": "debugger2", + "image": "debugimage", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File" + }] + } }`), valid: true, }, @@ -408,7 +414,7 @@ func TestPodPatchEphemeralContainers(t *testing.T) { patchType: types.JSONPatchType, patchBody: []byte(`[{ "op":"add", - "path":"/ephemeralContainers/-", + "path":"/spec/ephemeralContainers/-", "value":{ "name":"debugger2", "image":"debugimage", @@ -431,9 +437,25 @@ func TestPodPatchEphemeralContainers(t *testing.T) { }, }, patchType: types.MergePatchType, - patchBody: []byte(`{"ephemeralContainers":[]}`), + patchBody: []byte(`{"spec": {"ephemeralContainers":[]}}`), valid: false, }, + { + name: "remove the single container (JSON)", + original: []v1.EphemeralContainer{ + { + EphemeralContainerCommon: v1.EphemeralContainerCommon{ + Name: "debugger1", + Image: "debugimage", + ImagePullPolicy: "Always", + TerminationMessagePolicy: "File", + }, + }, + }, + patchType: types.JSONPatchType, + patchBody: []byte(`[{"op":"remove","path":"/ephemeralContainers/0"}]`), + valid: false, // disallowed by policy rather than patch semantics + }, { name: "remove all containers (JSON)", original: []v1.EphemeralContainer{ @@ -447,14 +469,14 @@ func TestPodPatchEphemeralContainers(t *testing.T) { }, }, patchType: types.JSONPatchType, - patchBody: []byte(`[{"op":"remove","path":"/ephemeralContainers/0"}]`), - valid: false, + patchBody: []byte(`[{"op":"remove","path":"/ephemeralContainers"}]`), + valid: false, // disallowed by policy rather than patch semantics }, } for i, tc := range cases { pod := testPod(fmt.Sprintf("ephemeral-container-test-%v", i)) - if err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, tc.original); err != nil { + if _, err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, tc.original); err != nil { t.Errorf("%v: %v", tc.name, err) } @@ -643,18 +665,13 @@ func TestPodUpdateEphemeralContainers(t *testing.T) { } for i, tc := range cases { - pod := testPod(fmt.Sprintf("ephemeral-container-test-%v", i)) - if err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, tc.original); err != nil { + pod, err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), testPod(fmt.Sprintf("ephemeral-container-test-%v", i)), tc.original) + if err != nil { t.Errorf("%v: %v", tc.name, err) } - ec, err := client.CoreV1().Pods(ns.Name).GetEphemeralContainers(context.TODO(), pod.Name, metav1.GetOptions{}) - if err != nil { - t.Errorf("%v: unable to get ephemeral containers: %v", tc.name, err) - } - - ec.EphemeralContainers = tc.update - if _, err := client.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.TODO(), pod.Name, ec, metav1.UpdateOptions{}); tc.valid && err != nil { + pod.Spec.EphemeralContainers = tc.update + if _, err := client.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.TODO(), pod.Name, pod, metav1.UpdateOptions{}); tc.valid && err != nil { t.Errorf("%v: failed to update ephemeral containers: %v", tc.name, err) } else if !tc.valid && err == nil { t.Errorf("%v: unexpected allowed update to ephemeral containers", tc.name) @@ -690,29 +707,21 @@ func TestPodEphemeralContainersDisabled(t *testing.T) { }, }, } - if err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, nil); err != nil { + pod, err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, nil) + if err != nil { t.Error(err) } - ec, err := client.CoreV1().Pods(ns.Name).GetEphemeralContainers(context.TODO(), pod.Name, metav1.GetOptions{}) - if err == nil { - t.Errorf("got nil error when getting ephemeral containers with feature disabled, wanted %q", metav1.StatusReasonNotFound) - } else if se := err.(*errors.StatusError); se.ErrStatus.Reason != metav1.StatusReasonNotFound { - t.Errorf("got error reason %q when getting ephemeral containers with feature disabled, want %q: %#v", se.ErrStatus.Reason, metav1.StatusReasonNotFound, se) - } - - ec.EphemeralContainers = []v1.EphemeralContainer{ - { - EphemeralContainerCommon: v1.EphemeralContainerCommon{ - Name: "debugger", - Image: "debugimage", - ImagePullPolicy: "Always", - TerminationMessagePolicy: "File", - }, + pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, v1.EphemeralContainer{ + EphemeralContainerCommon: v1.EphemeralContainerCommon{ + Name: "debugger", + Image: "debugimage", + ImagePullPolicy: "Always", + TerminationMessagePolicy: "File", }, - } + }) - if _, err := client.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.TODO(), pod.Name, ec, metav1.UpdateOptions{}); err == nil { + if _, err := client.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.TODO(), pod.Name, pod, metav1.UpdateOptions{}); err == nil { t.Errorf("got nil error when updating ephemeral containers with feature disabled, wanted %q", metav1.StatusReasonNotFound) } else if se := err.(*errors.StatusError); se.ErrStatus.Reason != metav1.StatusReasonNotFound { t.Errorf("got error reason %q when updating ephemeral containers with feature disabled, want %q: %#v", se.ErrStatus.Reason, metav1.StatusReasonNotFound, se)