Merge pull request #101034 from verb/1.22-ec-api

Switch alpha Pod ephemeralcontainers API to use Pod kind
This commit is contained in:
Kubernetes Prow Robot 2021-04-22 06:21:41 -07:00 committed by GitHub
commit 972ee2d425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1194 additions and 1495 deletions

View File

@ -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

View File

@ -6590,42 +6590,6 @@
],
"type": "object"
},
"io.k8s.api.core.v1.EphemeralContainers": {
"description": "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"ephemeralContainers": {
"description": "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.",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainer"
},
"type": "array",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
}
},
"required": [
"ephemeralContainers"
],
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "",
"kind": "EphemeralContainers",
"version": "v1"
}
]
},
"io.k8s.api.core.v1.EphemeralVolumeSource": {
"description": "Represents an ephemeral volume that is handled by a normal storage driver.",
"properties": {
@ -24896,7 +24860,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
"401": {
@ -24912,13 +24876,13 @@
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "",
"kind": "EphemeralContainers",
"kind": "Pod",
"version": "v1"
}
},
"parameters": [
{
"description": "name of the EphemeralContainers",
"description": "name of the Pod",
"in": "path",
"name": "name",
"required": true,
@ -24990,13 +24954,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
"401": {
@ -25012,7 +24976,7 @@
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "",
"kind": "EphemeralContainers",
"kind": "Pod",
"version": "v1"
}
},
@ -25028,7 +24992,7 @@
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
{
@ -25055,13 +25019,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainers"
"$ref": "#/definitions/io.k8s.api.core.v1.Pod"
}
},
"401": {
@ -25077,7 +25041,7 @@
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "",
"kind": "EphemeralContainers",
"kind": "Pod",
"version": "v1"
}
}

View File

@ -96,7 +96,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
&EphemeralContainers{},
)
return nil

View File

@ -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.

View File

@ -531,16 +531,6 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.EphemeralContainers)(nil), (*core.EphemeralContainers)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_EphemeralContainers_To_core_EphemeralContainers(a.(*v1.EphemeralContainers), b.(*core.EphemeralContainers), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*core.EphemeralContainers)(nil), (*v1.EphemeralContainers)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_core_EphemeralContainers_To_v1_EphemeralContainers(a.(*core.EphemeralContainers), b.(*v1.EphemeralContainers), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.EphemeralVolumeSource)(nil), (*core.EphemeralVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_EphemeralVolumeSource_To_core_EphemeralVolumeSource(a.(*v1.EphemeralVolumeSource), b.(*core.EphemeralVolumeSource), scope)
}); err != nil {
@ -3522,28 +3512,6 @@ func Convert_core_EphemeralContainerCommon_To_v1_EphemeralContainerCommon(in *co
return autoConvert_core_EphemeralContainerCommon_To_v1_EphemeralContainerCommon(in, out, s)
}
func autoConvert_v1_EphemeralContainers_To_core_EphemeralContainers(in *v1.EphemeralContainers, out *core.EphemeralContainers, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.EphemeralContainers = *(*[]core.EphemeralContainer)(unsafe.Pointer(&in.EphemeralContainers))
return nil
}
// Convert_v1_EphemeralContainers_To_core_EphemeralContainers is an autogenerated conversion function.
func Convert_v1_EphemeralContainers_To_core_EphemeralContainers(in *v1.EphemeralContainers, out *core.EphemeralContainers, s conversion.Scope) error {
return autoConvert_v1_EphemeralContainers_To_core_EphemeralContainers(in, out, s)
}
func autoConvert_core_EphemeralContainers_To_v1_EphemeralContainers(in *core.EphemeralContainers, out *v1.EphemeralContainers, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.EphemeralContainers = *(*[]v1.EphemeralContainer)(unsafe.Pointer(&in.EphemeralContainers))
return nil
}
// Convert_core_EphemeralContainers_To_v1_EphemeralContainers is an autogenerated conversion function.
func Convert_core_EphemeralContainers_To_v1_EphemeralContainers(in *core.EphemeralContainers, out *v1.EphemeralContainers, s conversion.Scope) error {
return autoConvert_core_EphemeralContainers_To_v1_EphemeralContainers(in, out, s)
}
func autoConvert_v1_EphemeralVolumeSource_To_core_EphemeralVolumeSource(in *v1.EphemeralVolumeSource, out *core.EphemeralVolumeSource, s conversion.Scope) error {
out.VolumeClaimTemplate = (*core.PersistentVolumeClaimTemplate)(unsafe.Pointer(in.VolumeClaimTemplate))
return nil

View File

@ -33,7 +33,6 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&v1.ConfigMapList{}, func(obj interface{}) { SetObjectDefaults_ConfigMapList(obj.(*v1.ConfigMapList)) })
scheme.AddTypeDefaultingFunc(&v1.Endpoints{}, func(obj interface{}) { SetObjectDefaults_Endpoints(obj.(*v1.Endpoints)) })
scheme.AddTypeDefaultingFunc(&v1.EndpointsList{}, func(obj interface{}) { SetObjectDefaults_EndpointsList(obj.(*v1.EndpointsList)) })
scheme.AddTypeDefaultingFunc(&v1.EphemeralContainers{}, func(obj interface{}) { SetObjectDefaults_EphemeralContainers(obj.(*v1.EphemeralContainers)) })
scheme.AddTypeDefaultingFunc(&v1.LimitRange{}, func(obj interface{}) { SetObjectDefaults_LimitRange(obj.(*v1.LimitRange)) })
scheme.AddTypeDefaultingFunc(&v1.LimitRangeList{}, func(obj interface{}) { SetObjectDefaults_LimitRangeList(obj.(*v1.LimitRangeList)) })
scheme.AddTypeDefaultingFunc(&v1.Namespace{}, func(obj interface{}) { SetObjectDefaults_Namespace(obj.(*v1.Namespace)) })
@ -85,59 +84,6 @@ func SetObjectDefaults_EndpointsList(in *v1.EndpointsList) {
}
}
func SetObjectDefaults_EphemeralContainers(in *v1.EphemeralContainers) {
for i := range in.EphemeralContainers {
a := &in.EphemeralContainers[i]
SetDefaults_EphemeralContainer(a)
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
if b.Protocol == "" {
b.Protocol = "TCP"
}
}
for j := range a.EphemeralContainerCommon.Env {
b := &a.EphemeralContainerCommon.Env[j]
if b.ValueFrom != nil {
if b.ValueFrom.FieldRef != nil {
SetDefaults_ObjectFieldSelector(b.ValueFrom.FieldRef)
}
}
}
SetDefaults_ResourceList(&a.EphemeralContainerCommon.Resources.Limits)
SetDefaults_ResourceList(&a.EphemeralContainerCommon.Resources.Requests)
if a.EphemeralContainerCommon.LivenessProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.LivenessProbe)
if a.EphemeralContainerCommon.LivenessProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.LivenessProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.ReadinessProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.ReadinessProbe)
if a.EphemeralContainerCommon.ReadinessProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.ReadinessProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.StartupProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.StartupProbe)
if a.EphemeralContainerCommon.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.StartupProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.Lifecycle != nil {
if a.EphemeralContainerCommon.Lifecycle.PostStart != nil {
if a.EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet)
}
}
if a.EphemeralContainerCommon.Lifecycle.PreStop != nil {
if a.EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet)
}
}
}
}
}
func SetObjectDefaults_LimitRange(in *v1.LimitRange) {
for i := range in.Spec.Limits {
a := &in.Spec.Limits[i]

View File

@ -1400,39 +1400,6 @@ func (in *EphemeralContainerCommon) DeepCopy() *EphemeralContainerCommon {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralContainers) DeepCopyInto(out *EphemeralContainers) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.EphemeralContainers != nil {
in, out := &in.EphemeralContainers, &out.EphemeralContainers
*out = make([]EphemeralContainer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainers.
func (in *EphemeralContainers) DeepCopy() *EphemeralContainers {
if in == nil {
return nil
}
out := new(EphemeralContainers)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EphemeralContainers) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralVolumeSource) DeepCopyInto(out *EphemeralVolumeSource) {
*out = *in

View File

@ -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)
}

View File

@ -205,6 +205,25 @@ type podEphemeralContainersStrategy struct {
// EphemeralContainersStrategy wraps and exports the used podStrategy for the storage package.
var EphemeralContainersStrategy = podEphemeralContainersStrategy{Strategy}
// dropNonEphemeralContainerUpdates discards all changes except for pod.Spec.EphemeralContainers and certain metadata
func dropNonEphemeralContainerUpdates(newPod, oldPod *api.Pod) *api.Pod {
pod := oldPod.DeepCopy()
pod.Name = newPod.Name
pod.Namespace = newPod.Namespace
pod.ResourceVersion = newPod.ResourceVersion
pod.UID = newPod.UID
pod.Spec.EphemeralContainers = newPod.Spec.EphemeralContainers
return pod
}
func (podEphemeralContainersStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPod := obj.(*api.Pod)
oldPod := old.(*api.Pod)
*newPod = *dropNonEphemeralContainerUpdates(newPod, oldPod)
podutil.DropDisabledPodFields(newPod, oldPod)
}
func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
newPod := obj.(*api.Pod)
oldPod := old.(*api.Pod)

View File

@ -24,6 +24,7 @@ import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
@ -1381,3 +1382,221 @@ func TestPodStrategyValidateUpdate(t *testing.T) {
})
}
}
func TestDropNonEphemeralContainerUpdates(t *testing.T) {
tests := []struct {
name string
oldPod, newPod, wantPod *api.Pod
}{
{
name: "simple ephemeral container append",
oldPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "image",
},
},
},
},
newPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "image",
},
},
EphemeralContainers: []api.EphemeralContainer{
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container",
Image: "image",
},
},
},
},
},
wantPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "image",
},
},
EphemeralContainers: []api.EphemeralContainer{
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container",
Image: "image",
},
},
},
},
},
},
{
name: "whoops wrong pod",
oldPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
UID: "blue",
},
},
newPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "new-pod",
Namespace: "new-ns",
ResourceVersion: "1",
UID: "green",
},
},
wantPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "new-pod",
Namespace: "new-ns",
ResourceVersion: "1",
UID: "green",
},
},
},
{
name: "resource conflict during update",
oldPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "2",
UID: "blue",
},
},
newPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
UID: "blue",
},
},
wantPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
UID: "blue",
},
},
},
{
name: "drop non-ephemeral container changes",
oldPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
Annotations: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "image",
},
},
},
},
newPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
Annotations: map[string]string{"foo": "bar", "whiz": "pop"},
ClusterName: "milo",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "newimage",
},
},
EphemeralContainers: []api.EphemeralContainer{
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container1",
Image: "image",
},
},
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container2",
Image: "image",
},
},
},
},
Status: api.PodStatus{
Message: "hi.",
},
},
wantPod: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "test-ns",
ResourceVersion: "1",
Annotations: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "container",
Image: "image",
},
},
EphemeralContainers: []api.EphemeralContainer{
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container1",
Image: "image",
},
},
{
EphemeralContainerCommon: api.EphemeralContainerCommon{
Name: "container2",
Image: "image",
},
},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
gotPod := dropNonEphemeralContainerUpdates(tc.newPod, tc.oldPod)
if diff := cmp.Diff(tc.wantPod, gotPod); diff != "" {
t.Errorf("unexpected diff when dropping fields (-want, +got):\n%s", diff)
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1362,19 +1362,6 @@ message EphemeralContainerCommon {
optional bool tty = 18;
}
// A list of ephemeral containers used with the Pod ephemeralcontainers subresource.
message EphemeralContainers {
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// 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
repeated EphemeralContainer ephemeralContainers = 2;
}
// Represents an ephemeral volume that is handled by a normal storage driver.
message EphemeralVolumeSource {
// Will be used to create a stand-alone PVC to provision the volume.

View File

@ -88,7 +88,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
&EphemeralContainers{},
)
// Add common types

View File

@ -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 {

View File

@ -617,15 +617,6 @@ func (EphemeralContainerCommon) SwaggerDoc() map[string]string {
return map_EphemeralContainerCommon
}
var map_EphemeralContainers = map[string]string{
"": "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.",
"ephemeralContainers": "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.",
}
func (EphemeralContainers) SwaggerDoc() map[string]string {
return map_EphemeralContainers
}
var map_EphemeralVolumeSource = map[string]string{
"": "Represents an ephemeral volume that is handled by a normal storage driver.",
"volumeClaimTemplate": "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `<pod name>-<volume name>` where `<volume name>` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long).\n\nAn existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster.\n\nThis field is read-only and no changes will be made by Kubernetes to the PVC after it has been created.\n\nRequired, must not be nil.",

View File

@ -1400,39 +1400,6 @@ func (in *EphemeralContainerCommon) DeepCopy() *EphemeralContainerCommon {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralContainers) DeepCopyInto(out *EphemeralContainers) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.EphemeralContainers != nil {
in, out := &in.EphemeralContainers, &out.EphemeralContainers
*out = make([]EphemeralContainer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainers.
func (in *EphemeralContainers) DeepCopy() *EphemeralContainers {
if in == nil {
return nil
}
out := new(EphemeralContainers)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EphemeralContainers) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralVolumeSource) DeepCopyInto(out *EphemeralVolumeSource) {
*out = *in

View File

@ -189,24 +189,13 @@ func (c *FakePods) ApplyStatus(ctx context.Context, pod *applyconfigurationscore
return obj.(*corev1.Pod), err
}
// GetEphemeralContainers takes name of the pod, and returns the corresponding ephemeralContainers object, and an error if there is any.
func (c *FakePods) GetEphemeralContainers(ctx context.Context, podName string, options v1.GetOptions) (result *corev1.EphemeralContainers, err error) {
// UpdateEphemeralContainers takes the representation of a pod and updates it. Returns the server's representation of the pod, and an error, if there is any.
func (c *FakePods) UpdateEphemeralContainers(ctx context.Context, podName string, pod *corev1.Pod, opts v1.UpdateOptions) (result *corev1.Pod, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetSubresourceAction(podsResource, c.ns, "ephemeralcontainers", podName), &corev1.EphemeralContainers{})
Invokes(testing.NewUpdateSubresourceAction(podsResource, "ephemeralcontainers", c.ns, pod), &corev1.Pod{})
if obj == nil {
return nil, err
}
return obj.(*corev1.EphemeralContainers), err
}
// UpdateEphemeralContainers takes 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 *FakePods) UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *corev1.EphemeralContainers, opts v1.UpdateOptions) (result *corev1.EphemeralContainers, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(podsResource, "ephemeralcontainers", c.ns, ephemeralContainers), &corev1.EphemeralContainers{})
if obj == nil {
return nil, err
}
return obj.(*corev1.EphemeralContainers), err
return obj.(*corev1.Pod), err
}

View File

@ -52,8 +52,7 @@ type PodInterface interface {
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Pod, err error)
Apply(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error)
ApplyStatus(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error)
GetEphemeralContainers(ctx context.Context, podName string, options metav1.GetOptions) (*v1.EphemeralContainers, error)
UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *v1.EphemeralContainers, opts metav1.UpdateOptions) (*v1.EphemeralContainers, error)
UpdateEphemeralContainers(ctx context.Context, podName string, pod *v1.Pod, opts metav1.UpdateOptions) (*v1.Pod, error)
PodExpansion
}
@ -258,30 +257,16 @@ func (c *pods) ApplyStatus(ctx context.Context, pod *corev1.PodApplyConfiguratio
return
}
// 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().
Namespace(c.ns).
Resource("pods").
Name(podName).
SubResource("ephemeralcontainers").
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// 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{}
// UpdateEphemeralContainers takes the top resource name and the representation of a pod and updates it. Returns the server's representation of the pod, and an error, if there is any.
func (c *pods) UpdateEphemeralContainers(ctx context.Context, podName string, pod *v1.Pod, opts metav1.UpdateOptions) (result *v1.Pod, err error) {
result = &v1.Pod{}
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

View File

@ -18,6 +18,7 @@ package debug
import (
"context"
"encoding/json"
"fmt"
"time"
@ -31,7 +32,9 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilrand "k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
@ -381,26 +384,39 @@ 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)
podJS, err := json.Marshal(pod)
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("error creating JSON for pod: %v", err)
}
debugContainer := o.generateDebugContainer(pod)
klog.V(2).Infof("new ephemeral container: %#v", debugContainer)
debugPod := pod.DeepCopy()
debugPod.Spec.EphemeralContainers = append(debugPod.Spec.EphemeralContainers, *debugContainer)
debugJS, err := json.Marshal(debugPod)
if err != nil {
return nil, "", fmt.Errorf("error creating JSON for debug container: %v", err)
}
patch, err := strategicpatch.CreateTwoWayMergePatch(podJS, debugJS, pod)
if err != nil {
return nil, "", fmt.Errorf("error creating patch to add debug container: %v", err)
}
klog.V(2).Infof("generated strategic merge patch for debug container: %s", patch)
pods := o.podClient.Pods(pod.Namespace)
result, err := pods.Patch(ctx, pod.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{}, "ephemeralcontainers")
if err != nil {
// The apiserver will return a 404 when the EphemeralContainers feature is disabled because the `/ephemeralcontainers` subresource
// is missing. Unlike the 404 returned by a missing pod, the status details will be empty.
if serr, ok := err.(*errors.StatusError); ok && serr.Status().Reason == metav1.StatusReasonNotFound && serr.ErrStatus.Details.Name == "" {
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

View File

@ -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,33 +707,33 @@ func TestPodEphemeralContainersDisabled(t *testing.T) {
},
},
}
if err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, nil); err != nil {
t.Error(err)
pod, err := setUpEphemeralContainers(client.CoreV1().Pods(ns.Name), pod, nil)
if err != nil {
t.Fatal(err)
}
defer integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
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, pod, metav1.UpdateOptions{}); err == nil {
t.Fatalf("got nil error when updating ephemeral containers with feature disabled, wanted %q", metav1.StatusReasonNotFound)
}
if _, err := client.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.TODO(), pod.Name, ec, 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 {
se, ok := err.(*errors.StatusError)
if !ok {
t.Fatalf("got error %#v, expected StatusError", err)
}
if 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)
}
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
if se.ErrStatus.Details.Name != "" {
t.Errorf("got error details with name %q, want %q: %#v", se.ErrStatus.Details.Name, "", se)
}
}