Merge pull request #101034 from verb/1.22-ec-api
Switch alpha Pod ephemeralcontainers API to use Pod kind
This commit is contained in:
commit
972ee2d425
@ -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
|
||||
|
56
api/openapi-spec/swagger.json
generated
56
api/openapi-spec/swagger.json
generated
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&RangeAllocation{},
|
||||
&ConfigMap{},
|
||||
&ConfigMapList{},
|
||||
&EphemeralContainers{},
|
||||
)
|
||||
|
||||
return nil
|
||||
|
@ -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.
|
||||
|
32
pkg/apis/core/v1/zz_generated.conversion.go
generated
32
pkg/apis/core/v1/zz_generated.conversion.go
generated
@ -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
|
||||
|
54
pkg/apis/core/v1/zz_generated.defaults.go
generated
54
pkg/apis/core/v1/zz_generated.defaults.go
generated
@ -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]
|
||||
|
33
pkg/apis/core/zz_generated.deepcopy.go
generated
33
pkg/apis/core/zz_generated.deepcopy.go
generated
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
1830
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
1830
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
@ -88,7 +88,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&RangeAllocation{},
|
||||
&ConfigMap{},
|
||||
&ConfigMapList{},
|
||||
&EphemeralContainers{},
|
||||
)
|
||||
|
||||
// Add common types
|
||||
|
@ -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 {
|
||||
|
@ -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.",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user