mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #111402 from verb/111030-ec-ga
Promote EphemeralContainers feature to GA
This commit is contained in:
commit
cf2800b812
6
api/openapi-spec/swagger.json
generated
6
api/openapi-spec/swagger.json
generated
@ -5402,7 +5402,7 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"io.k8s.api.core.v1.EphemeralContainer": {
|
"io.k8s.api.core.v1.EphemeralContainer": {
|
||||||
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
||||||
@ -7795,7 +7795,7 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"ephemeralContainers": {
|
"ephemeralContainers": {
|
||||||
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainer"
|
"$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainer"
|
||||||
},
|
},
|
||||||
@ -7986,7 +7986,7 @@
|
|||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"ephemeralContainerStatuses": {
|
"ephemeralContainerStatuses": {
|
||||||
"description": "Status for any ephemeral containers that have run in this pod. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "Status for any ephemeral containers that have run in this pod.",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus"
|
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus"
|
||||||
},
|
},
|
||||||
|
@ -1840,7 +1840,7 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"io.k8s.api.core.v1.EphemeralContainer": {
|
"io.k8s.api.core.v1.EphemeralContainer": {
|
||||||
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
||||||
@ -4993,7 +4993,7 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"ephemeralContainers": {
|
"ephemeralContainers": {
|
||||||
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
"items": {
|
"items": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@ -5248,7 +5248,7 @@
|
|||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"ephemeralContainerStatuses": {
|
"ephemeralContainerStatuses": {
|
||||||
"description": "Status for any ephemeral containers that have run in this pod. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "Status for any ephemeral containers that have run in this pod.",
|
||||||
"items": {
|
"items": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
@ -2047,7 +2047,7 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"io.k8s.api.core.v1.EphemeralContainer": {
|
"io.k8s.api.core.v1.EphemeralContainer": {
|
||||||
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
||||||
@ -3416,7 +3416,7 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"ephemeralContainers": {
|
"ephemeralContainers": {
|
||||||
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
"items": {
|
"items": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
@ -1243,7 +1243,7 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"io.k8s.api.core.v1.EphemeralContainer": {
|
"io.k8s.api.core.v1.EphemeralContainer": {
|
||||||
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
|
||||||
@ -2495,7 +2495,7 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"ephemeralContainers": {
|
"ephemeralContainers": {
|
||||||
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
"items": {
|
"items": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
@ -46,11 +46,7 @@ const AllContainers ContainerType = (InitContainers | Containers | EphemeralCont
|
|||||||
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
||||||
// types except for the ones guarded by feature gate.
|
// types except for the ones guarded by feature gate.
|
||||||
func AllFeatureEnabledContainers() ContainerType {
|
func AllFeatureEnabledContainers() ContainerType {
|
||||||
containerType := AllContainers
|
return AllContainers
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
containerType &= ^EphemeralContainers
|
|
||||||
}
|
|
||||||
return containerType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerVisitor is called with each container spec, and returns true
|
// ContainerVisitor is called with each container spec, and returns true
|
||||||
@ -529,10 +525,6 @@ func dropDisabledFields(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) && !ephemeralContainersInUse(oldPodSpec) {
|
|
||||||
podSpec.EphemeralContainers = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ProbeTerminationGracePeriod) && !probeGracePeriodInUse(oldPodSpec) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.ProbeTerminationGracePeriod) && !probeGracePeriodInUse(oldPodSpec) {
|
||||||
// Set pod-level terminationGracePeriodSeconds to nil if the feature is disabled and it is not used
|
// Set pod-level terminationGracePeriodSeconds to nil if the feature is disabled and it is not used
|
||||||
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
||||||
@ -654,13 +646,6 @@ func nodeTaintsPolicyInUse(podSpec *api.PodSpec) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ephemeralContainersInUse(podSpec *api.PodSpec) bool {
|
|
||||||
if podSpec == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(podSpec.EphemeralContainers) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value
|
// procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value
|
||||||
func procMountInUse(podSpec *api.PodSpec) bool {
|
func procMountInUse(podSpec *api.PodSpec) bool {
|
||||||
if podSpec == nil {
|
if podSpec == nil {
|
||||||
|
@ -43,7 +43,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
spec *api.PodSpec
|
spec *api.PodSpec
|
||||||
wantContainers []string
|
wantContainers []string
|
||||||
mask ContainerType
|
mask ContainerType
|
||||||
ephemeralContainersEnabled bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "empty podspec",
|
desc: "empty podspec",
|
||||||
@ -127,25 +126,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||||
mask: AllContainers,
|
mask: AllContainers,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "all feature enabled container types with ephemeral containers disabled",
|
|
||||||
spec: &api.PodSpec{
|
|
||||||
Containers: []api.Container{
|
|
||||||
{Name: "c1"},
|
|
||||||
{Name: "c2"},
|
|
||||||
},
|
|
||||||
InitContainers: []api.Container{
|
|
||||||
{Name: "i1"},
|
|
||||||
{Name: "i2"},
|
|
||||||
},
|
|
||||||
EphemeralContainers: []api.EphemeralContainer{
|
|
||||||
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
|
|
||||||
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantContainers: []string{"i1", "i2", "c1", "c2"},
|
|
||||||
mask: setAllFeatureEnabledContainersDuringTest,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "all feature enabled container types with ephemeral containers enabled",
|
desc: "all feature enabled container types with ephemeral containers enabled",
|
||||||
spec: &api.PodSpec{
|
spec: &api.PodSpec{
|
||||||
@ -164,7 +144,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||||
mask: setAllFeatureEnabledContainersDuringTest,
|
mask: setAllFeatureEnabledContainersDuringTest,
|
||||||
ephemeralContainersEnabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "dropping fields",
|
desc: "dropping fields",
|
||||||
@ -189,8 +168,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, tc.ephemeralContainersEnabled)()
|
|
||||||
|
|
||||||
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
||||||
tc.mask = AllFeatureEnabledContainers()
|
tc.mask = AllFeatureEnabledContainers()
|
||||||
}
|
}
|
||||||
@ -226,8 +203,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodSecrets(t *testing.T) {
|
func TestPodSecrets(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Stub containing all possible secret references in a pod.
|
// Stub containing all possible secret references in a pod.
|
||||||
// The names of the referenced secrets match struct paths detected by reflection.
|
// The names of the referenced secrets match struct paths detected by reflection.
|
||||||
pod := &api.Pod{
|
pod := &api.Pod{
|
||||||
@ -425,8 +400,6 @@ func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodConfigmaps(t *testing.T) {
|
func TestPodConfigmaps(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Stub containing all possible ConfigMap references in a pod.
|
// Stub containing all possible ConfigMap references in a pod.
|
||||||
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
||||||
pod := &api.Pod{
|
pod := &api.Pod{
|
||||||
@ -1023,95 +996,6 @@ func TestDropProbeGracePeriod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropEphemeralContainers(t *testing.T) {
|
|
||||||
podWithEphemeralContainers := func() *api.Pod {
|
|
||||||
return &api.Pod{
|
|
||||||
Spec: api.PodSpec{
|
|
||||||
RestartPolicy: api.RestartPolicyNever,
|
|
||||||
EphemeralContainers: []api.EphemeralContainer{{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "container1", Image: "testimage"}}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
podWithoutEphemeralContainers := func() *api.Pod {
|
|
||||||
return &api.Pod{
|
|
||||||
Spec: api.PodSpec{
|
|
||||||
RestartPolicy: api.RestartPolicyNever,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
podInfo := []struct {
|
|
||||||
description string
|
|
||||||
hasEphemeralContainers bool
|
|
||||||
pod func() *api.Pod
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "has ephemeral containers",
|
|
||||||
hasEphemeralContainers: true,
|
|
||||||
pod: podWithEphemeralContainers,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "does not have ephemeral containers",
|
|
||||||
hasEphemeralContainers: false,
|
|
||||||
pod: podWithoutEphemeralContainers,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "is nil",
|
|
||||||
hasEphemeralContainers: false,
|
|
||||||
pod: func() *api.Pod { return nil },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
|
||||||
for _, oldPodInfo := range podInfo {
|
|
||||||
for _, newPodInfo := range podInfo {
|
|
||||||
oldPodHasEphemeralContainers, oldPod := oldPodInfo.hasEphemeralContainers, oldPodInfo.pod()
|
|
||||||
newPodHasEphemeralContainers, newPod := newPodInfo.hasEphemeralContainers, newPodInfo.pod()
|
|
||||||
if newPod == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, enabled)()
|
|
||||||
|
|
||||||
var oldPodSpec *api.PodSpec
|
|
||||||
if oldPod != nil {
|
|
||||||
oldPodSpec = &oldPod.Spec
|
|
||||||
}
|
|
||||||
dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
|
|
||||||
|
|
||||||
// old pod should never be changed
|
|
||||||
if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
|
|
||||||
t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case enabled || oldPodHasEphemeralContainers:
|
|
||||||
// new pod should not be changed if the feature is enabled, or if the old pod had subpaths
|
|
||||||
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
|
|
||||||
}
|
|
||||||
case newPodHasEphemeralContainers:
|
|
||||||
// new pod should be changed
|
|
||||||
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod was not changed")
|
|
||||||
}
|
|
||||||
// new pod should not have subpaths
|
|
||||||
if !reflect.DeepEqual(newPod, podWithoutEphemeralContainers()) {
|
|
||||||
t.Errorf("new pod had subpaths: %v", cmp.Diff(newPod, podWithoutEphemeralContainers()))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// new pod should not need to be changed
|
|
||||||
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
|
|
||||||
t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidatePodDeletionCostOption(t *testing.T) {
|
func TestValidatePodDeletionCostOption(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -23,8 +23,6 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindPort locates the container port for the given pod and portName. If the
|
// FindPort locates the container port for the given pod and portName. If the
|
||||||
@ -68,11 +66,7 @@ const AllContainers ContainerType = (InitContainers | Containers | EphemeralCont
|
|||||||
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
||||||
// types except for the ones guarded by feature gate.
|
// types except for the ones guarded by feature gate.
|
||||||
func AllFeatureEnabledContainers() ContainerType {
|
func AllFeatureEnabledContainers() ContainerType {
|
||||||
containerType := AllContainers
|
return AllContainers
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
containerType &= ^EphemeralContainers
|
|
||||||
}
|
|
||||||
return containerType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerVisitor is called with each container spec, and returns true
|
// ContainerVisitor is called with each container spec, and returns true
|
||||||
|
@ -29,9 +29,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindPort(t *testing.T) {
|
func TestFindPort(t *testing.T) {
|
||||||
@ -209,7 +206,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
spec *v1.PodSpec
|
spec *v1.PodSpec
|
||||||
wantContainers []string
|
wantContainers []string
|
||||||
mask ContainerType
|
mask ContainerType
|
||||||
ephemeralContainersEnabled bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "empty podspec",
|
desc: "empty podspec",
|
||||||
@ -294,26 +290,7 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
mask: AllContainers,
|
mask: AllContainers,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "all feature enabled container types with ephemeral containers disabled",
|
desc: "all feature enabled container types",
|
||||||
spec: &v1.PodSpec{
|
|
||||||
Containers: []v1.Container{
|
|
||||||
{Name: "c1"},
|
|
||||||
{Name: "c2"},
|
|
||||||
},
|
|
||||||
InitContainers: []v1.Container{
|
|
||||||
{Name: "i1"},
|
|
||||||
{Name: "i2"},
|
|
||||||
},
|
|
||||||
EphemeralContainers: []v1.EphemeralContainer{
|
|
||||||
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e1"}},
|
|
||||||
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantContainers: []string{"i1", "i2", "c1", "c2"},
|
|
||||||
mask: setAllFeatureEnabledContainersDuringTest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "all feature enabled container types with ephemeral containers enabled",
|
|
||||||
spec: &v1.PodSpec{
|
spec: &v1.PodSpec{
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
{Name: "c1"},
|
{Name: "c1"},
|
||||||
@ -330,7 +307,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||||
mask: setAllFeatureEnabledContainersDuringTest,
|
mask: setAllFeatureEnabledContainersDuringTest,
|
||||||
ephemeralContainersEnabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "dropping fields",
|
desc: "dropping fields",
|
||||||
@ -355,8 +331,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, tc.ephemeralContainersEnabled)()
|
|
||||||
|
|
||||||
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
||||||
tc.mask = AllFeatureEnabledContainers()
|
tc.mask = AllFeatureEnabledContainers()
|
||||||
}
|
}
|
||||||
@ -392,8 +366,6 @@ func TestVisitContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodSecrets(t *testing.T) {
|
func TestPodSecrets(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Stub containing all possible secret references in a pod.
|
// Stub containing all possible secret references in a pod.
|
||||||
// The names of the referenced secrets match struct paths detected by reflection.
|
// The names of the referenced secrets match struct paths detected by reflection.
|
||||||
pod := &v1.Pod{
|
pod := &v1.Pod{
|
||||||
@ -591,8 +563,6 @@ func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodConfigmaps(t *testing.T) {
|
func TestPodConfigmaps(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Stub containing all possible ConfigMap references in a pod.
|
// Stub containing all possible ConfigMap references in a pod.
|
||||||
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
||||||
pod := &v1.Pod{
|
pod := &v1.Pod{
|
||||||
|
@ -2396,8 +2396,6 @@ func TestValidateDaemonSetUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateDaemonSet(t *testing.T) {
|
func TestValidateDaemonSet(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
validSelector := map[string]string{"a": "b"}
|
validSelector := map[string]string{"a": "b"}
|
||||||
validPodTemplate := api.PodTemplate{
|
validPodTemplate := api.PodTemplate{
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
@ -2660,8 +2658,6 @@ func validDeployment() *apps.Deployment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateDeployment(t *testing.T) {
|
func TestValidateDeployment(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
successCases := []*apps.Deployment{
|
successCases := []*apps.Deployment{
|
||||||
validDeployment(),
|
validDeployment(),
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/fieldpath"
|
"k8s.io/kubernetes/pkg/fieldpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,14 +45,12 @@ func VisitContainersWithPath(podSpec *api.PodSpec, specPath *field.Path, visitor
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
fldPath = specPath.Child("ephemeralContainers")
|
fldPath = specPath.Child("ephemeralContainers")
|
||||||
for i := range podSpec.EphemeralContainers {
|
for i := range podSpec.EphemeralContainers {
|
||||||
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i)) {
|
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,15 +21,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVisitContainersWithPath(t *testing.T) {
|
func TestVisitContainersWithPath(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
path *field.Path
|
path *field.Path
|
||||||
|
@ -2829,7 +2829,6 @@ type PodSpec struct {
|
|||||||
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
||||||
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
||||||
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
EphemeralContainers []EphemeralContainer
|
EphemeralContainers []EphemeralContainer
|
||||||
// +optional
|
// +optional
|
||||||
@ -3326,8 +3325,6 @@ var _ = Container(EphemeralContainerCommon{})
|
|||||||
//
|
//
|
||||||
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
||||||
// Pod. Ephemeral containers may not be removed or restarted.
|
// Pod. Ephemeral containers may not be removed or restarted.
|
||||||
//
|
|
||||||
// This is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
type EphemeralContainer struct {
|
type EphemeralContainer struct {
|
||||||
// Ephemeral containers have all of the fields of Container, plus additional fields
|
// Ephemeral containers have all of the fields of Container, plus additional fields
|
||||||
// specific to ephemeral containers. Fields in common with Container are in the
|
// specific to ephemeral containers. Fields in common with Container are in the
|
||||||
@ -3390,7 +3387,6 @@ type PodStatus struct {
|
|||||||
ContainerStatuses []ContainerStatus
|
ContainerStatuses []ContainerStatus
|
||||||
|
|
||||||
// Status for any ephemeral containers that have run in this pod.
|
// Status for any ephemeral containers that have run in this pod.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
EphemeralContainerStatuses []ContainerStatus
|
EphemeralContainerStatuses []ContainerStatus
|
||||||
}
|
}
|
||||||
|
@ -12171,8 +12171,6 @@ func makeValidService() core.Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
makePod := func(ephemeralContainers []core.EphemeralContainer) *core.Pod {
|
makePod := func(ephemeralContainers []core.EphemeralContainer) *core.Pod {
|
||||||
return &core.Pod{
|
return &core.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -20998,7 +20996,6 @@ func TestValidateWindowsHostProcessPod(t *testing.T) {
|
|||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostProcessContainers, testCase.featureEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostProcessContainers, testCase.featureEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
opts := PodValidationOptions{AllowWindowsHostProcessField: testCase.featureEnabled}
|
opts := PodValidationOptions{AllowWindowsHostProcessField: testCase.featureEnabled}
|
||||||
|
|
||||||
|
@ -285,6 +285,7 @@ const (
|
|||||||
// owner: @verb
|
// owner: @verb
|
||||||
// alpha: v1.16
|
// alpha: v1.16
|
||||||
// beta: v1.23
|
// beta: v1.23
|
||||||
|
// GA: v1.25
|
||||||
//
|
//
|
||||||
// Allows running an ephemeral container in pod namespaces to troubleshoot a running pod.
|
// Allows running an ephemeral container in pod namespaces to troubleshoot a running pod.
|
||||||
EphemeralContainers featuregate.Feature = "EphemeralContainers"
|
EphemeralContainers featuregate.Feature = "EphemeralContainers"
|
||||||
@ -884,7 +885,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta},
|
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
EphemeralContainers: {Default: true, PreRelease: featuregate.Beta},
|
EphemeralContainers: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27
|
||||||
|
|
||||||
ExecProbeTimeout: {Default: true, PreRelease: featuregate.GA}, // lock to default and remove after v1.22 based on KEP #1972 update
|
ExecProbeTimeout: {Default: true, PreRelease: featuregate.GA}, // lock to default and remove after v1.22 based on KEP #1972 update
|
||||||
|
|
||||||
|
6
pkg/generated/openapi/zz_generated.openapi.go
generated
6
pkg/generated/openapi/zz_generated.openapi.go
generated
@ -16778,7 +16778,7 @@ func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) c
|
|||||||
return common.OpenAPIDefinition{
|
return common.OpenAPIDefinition{
|
||||||
Schema: spec.Schema{
|
Schema: spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
Description: "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
Type: []string{"object"},
|
Type: []string{"object"},
|
||||||
Properties: map[string]spec.Schema{
|
Properties: map[string]spec.Schema{
|
||||||
"name": {
|
"name": {
|
||||||
@ -21888,7 +21888,7 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
Type: []string{"array"},
|
Type: []string{"array"},
|
||||||
Items: &spec.SchemaOrArray{
|
Items: &spec.SchemaOrArray{
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
@ -22341,7 +22341,7 @@ func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.Ope
|
|||||||
},
|
},
|
||||||
"ephemeralContainerStatuses": {
|
"ephemeralContainerStatuses": {
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: "Status for any ephemeral containers that have run in this pod. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
Description: "Status for any ephemeral containers that have run in this pod.",
|
||||||
Type: []string{"array"},
|
Type: []string{"array"},
|
||||||
Items: &spec.SchemaOrArray{
|
Items: &spec.SchemaOrArray{
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
|
@ -26,9 +26,6 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnvVarsToMap(t *testing.T) {
|
func TestEnvVarsToMap(t *testing.T) {
|
||||||
@ -326,7 +323,6 @@ func TestExpandVolumeMountsWithSubpath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContainerSpec(t *testing.T) {
|
func TestGetContainerSpec(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
havePod *v1.Pod
|
havePod *v1.Pod
|
||||||
|
@ -20,10 +20,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
ref "k8s.io/client-go/tools/reference"
|
ref "k8s.io/client-go/tools/reference"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImplicitContainerPrefix is a container name prefix that will indicate that container was started implicitly (like the pod infra container).
|
// ImplicitContainerPrefix is a container name prefix that will indicate that container was started implicitly (like the pod infra container).
|
||||||
@ -67,7 +65,6 @@ func fieldPath(pod *v1.Pod, container *v1.Container) (string, error) {
|
|||||||
return fmt.Sprintf("spec.initContainers{%s}", here.Name), nil
|
return fmt.Sprintf("spec.initContainers{%s}", here.Name), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
for i := range pod.Spec.EphemeralContainers {
|
for i := range pod.Spec.EphemeralContainers {
|
||||||
here := &pod.Spec.EphemeralContainers[i]
|
here := &pod.Spec.EphemeralContainers[i]
|
||||||
if here.Name == container.Name {
|
if here.Name == container.Name {
|
||||||
@ -77,6 +74,5 @@ func fieldPath(pod *v1.Pod, container *v1.Container) (string, error) {
|
|||||||
return fmt.Sprintf("spec.ephemeralContainers{%s}", here.Name), nil
|
return fmt.Sprintf("spec.ephemeralContainers{%s}", here.Name), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return "", fmt.Errorf("container %q not found in pod %s/%s", container.Name, pod.Namespace, pod.Name)
|
return "", fmt.Errorf("container %q not found in pod %s/%s", container.Name, pod.Namespace, pod.Name)
|
||||||
}
|
}
|
||||||
|
@ -1221,7 +1221,7 @@ func (kl *Kubelet) validateContainerLogStatus(podName string, podStatus *v1.PodS
|
|||||||
if !found {
|
if !found {
|
||||||
cStatus, found = podutil.GetContainerStatus(podStatus.InitContainerStatuses, containerName)
|
cStatus, found = podutil.GetContainerStatus(podStatus.InitContainerStatuses, containerName)
|
||||||
}
|
}
|
||||||
if !found && utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
if !found {
|
||||||
cStatus, found = podutil.GetContainerStatus(podStatus.EphemeralContainerStatuses, containerName)
|
cStatus, found = podutil.GetContainerStatus(podStatus.EphemeralContainerStatuses, containerName)
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
@ -1602,7 +1602,6 @@ func (kl *Kubelet) convertStatusToAPIStatus(pod *v1.Pod, podStatus *kubecontaine
|
|||||||
len(pod.Spec.InitContainers) > 0,
|
len(pod.Spec.InitContainers) > 0,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
var ecSpecs []v1.Container
|
var ecSpecs []v1.Container
|
||||||
for i := range pod.Spec.EphemeralContainers {
|
for i := range pod.Spec.EphemeralContainers {
|
||||||
ecSpecs = append(ecSpecs, v1.Container(pod.Spec.EphemeralContainers[i].EphemeralContainerCommon))
|
ecSpecs = append(ecSpecs, v1.Container(pod.Spec.EphemeralContainers[i].EphemeralContainerCommon))
|
||||||
@ -1617,7 +1616,6 @@ func (kl *Kubelet) convertStatusToAPIStatus(pod *v1.Pod, podStatus *kubecontaine
|
|||||||
len(pod.Spec.InitContainers) > 0,
|
len(pod.Spec.InitContainers) > 0,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return &apiPodStatus
|
return &apiPodStatus
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,7 @@ import (
|
|||||||
kubetypes "k8s.io/apimachinery/pkg/types"
|
kubetypes "k8s.io/apimachinery/pkg/types"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cri/remote"
|
"k8s.io/kubernetes/pkg/kubelet/cri/remote"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/events"
|
"k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
@ -120,7 +118,7 @@ func ephemeralContainerStartSpec(ec *v1.EphemeralContainer) *startSpec {
|
|||||||
// usually isn't a problem since ephemeral containers aren't allowed at pod creation time.
|
// usually isn't a problem since ephemeral containers aren't allowed at pod creation time.
|
||||||
// This always returns nil when the EphemeralContainers feature is disabled.
|
// This always returns nil when the EphemeralContainers feature is disabled.
|
||||||
func (s *startSpec) getTargetID(podStatus *kubecontainer.PodStatus) (*kubecontainer.ContainerID, error) {
|
func (s *startSpec) getTargetID(podStatus *kubecontainer.PodStatus) (*kubecontainer.ContainerID, error) {
|
||||||
if s.ephemeralContainer == nil || s.ephemeralContainer.TargetContainerName == "" || !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
if s.ephemeralContainer == nil || s.ephemeralContainer.TargetContainerName == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +520,6 @@ func TestGetHugepageLimitsFromResources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) {
|
func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
_, _, m, err := createTestRuntimeManager()
|
_, _, m, err := createTestRuntimeManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating test RuntimeManager: %v", err)
|
t.Fatalf("error creating test RuntimeManager: %v", err)
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package kuberuntime
|
package kuberuntime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -32,10 +31,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||||
@ -405,23 +401,12 @@ func TestStartSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
if got, err := tc.spec.getTargetID(podStatus); err != nil {
|
if got, err := tc.spec.getTargetID(podStatus); err != nil {
|
||||||
t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err)
|
t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err)
|
||||||
} else if diff := cmp.Diff(tc.want, got); diff != "" {
|
} else if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||||
t.Errorf("%v: getTargetID got unexpected result. diff:\n%v", t.Name(), diff)
|
t.Errorf("%v: getTargetID got unexpected result. diff:\n%v", t.Name(), diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test with feature disabled in self-contained section which can be removed when feature flag is removed.
|
|
||||||
t.Run(fmt.Sprintf("%s (disabled)", tc.name), func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, false)()
|
|
||||||
if got, err := tc.spec.getTargetID(podStatus); err != nil {
|
|
||||||
t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err)
|
|
||||||
} else if got != nil {
|
|
||||||
t.Errorf("%v: getTargetID got: %v, wanted nil", t.Name(), got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,7 +578,6 @@ func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *ku
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ephemeral containers may be started even if initialization is not yet complete.
|
// Ephemeral containers may be started even if initialization is not yet complete.
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
for i := range pod.Spec.EphemeralContainers {
|
for i := range pod.Spec.EphemeralContainers {
|
||||||
c := (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon)
|
c := (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon)
|
||||||
|
|
||||||
@ -587,7 +586,6 @@ func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *ku
|
|||||||
changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i)
|
changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check initialization progress.
|
// Check initialization progress.
|
||||||
initLastStatus, next, done := findNextInitContainerToRun(pod, podStatus)
|
initLastStatus, next, done := findNextInitContainerToRun(pod, podStatus)
|
||||||
@ -914,11 +912,9 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine
|
|||||||
// These are started "prior" to init containers to allow running ephemeral containers even when there
|
// These are started "prior" to init containers to allow running ephemeral containers even when there
|
||||||
// are errors starting an init container. In practice init containers will start first since ephemeral
|
// are errors starting an init container. In practice init containers will start first since ephemeral
|
||||||
// containers cannot be specified on pod creation.
|
// containers cannot be specified on pod creation.
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
for _, idx := range podContainerChanges.EphemeralContainersToStart {
|
for _, idx := range podContainerChanges.EphemeralContainersToStart {
|
||||||
start("ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
start("ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Step 6: start the init container.
|
// Step 6: start the init container.
|
||||||
if container := podContainerChanges.NextInitContainerToStart; container != nil {
|
if container := podContainerChanges.NextInitContainerToStart; container != nil {
|
||||||
|
@ -35,14 +35,11 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
apitest "k8s.io/cri-api/pkg/apis/testing"
|
apitest "k8s.io/cri-api/pkg/apis/testing"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/credentialprovider"
|
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
|
proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
|
||||||
@ -490,9 +487,6 @@ func TestGetPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKillPod(t *testing.T) {
|
func TestKillPod(t *testing.T) {
|
||||||
// Tests that KillPod also kills Ephemeral Containers
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
fakeRuntime, _, m, err := createTestRuntimeManager()
|
fakeRuntime, _, m, err := createTestRuntimeManager()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@ -1372,7 +1366,6 @@ func makeBasePodAndStatusWithInitContainers() (*v1.Pod, *kubecontainer.PodStatus
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
// Make sure existing test cases pass with feature enabled
|
// Make sure existing test cases pass with feature enabled
|
||||||
TestComputePodActions(t)
|
TestComputePodActions(t)
|
||||||
TestComputePodActionsWithInitContainers(t)
|
TestComputePodActionsWithInitContainers(t)
|
||||||
|
@ -22,8 +22,6 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
@ -162,10 +160,6 @@ func (pm *basicManager) UpdatePod(pod *v1.Pod) {
|
|||||||
// updateMetrics updates the metrics surfaced by the pod manager.
|
// updateMetrics updates the metrics surfaced by the pod manager.
|
||||||
// oldPod or newPod may be nil to signify creation or deletion.
|
// oldPod or newPod may be nil to signify creation or deletion.
|
||||||
func updateMetrics(oldPod, newPod *v1.Pod) {
|
func updateMetrics(oldPod, newPod *v1.Pod) {
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var numEC int
|
var numEC int
|
||||||
if oldPod != nil {
|
if oldPod != nil {
|
||||||
numEC -= len(oldPod.Spec.EphemeralContainers)
|
numEC -= len(oldPod.Spec.EphemeralContainers)
|
||||||
|
@ -917,7 +917,7 @@ func (s *Server) checkpoint(request *restful.Request, response *restful.Response
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found && utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
if !found {
|
||||||
for _, container := range pod.Spec.EphemeralContainers {
|
for _, container := range pod.Spec.EphemeralContainers {
|
||||||
if container.Name == containerName {
|
if container.Name == containerName {
|
||||||
found = true
|
found = true
|
||||||
|
@ -33,12 +33,10 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||||
"k8s.io/apiserver/pkg/util/dryrun"
|
"k8s.io/apiserver/pkg/util/dryrun"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||||
@ -330,10 +328,6 @@ var _ = rest.Patcher(&EphemeralContainersREST{})
|
|||||||
|
|
||||||
// Get retrieves the object from the storage. It is required to support Patch.
|
// 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) {
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.store.Get(ctx, name, options)
|
return r.store.Get(ctx, name, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,10 +344,6 @@ func (r *EphemeralContainersREST) Destroy() {
|
|||||||
|
|
||||||
// Update alters the EphemeralContainers field in PodSpec
|
// Update alters the EphemeralContainers field in PodSpec
|
||||||
func (r *EphemeralContainersREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
func (r *EphemeralContainersREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
return nil, false, errors.NewBadRequest("feature EphemeralContainers disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
|
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
|
||||||
// subresources should never allow create on update.
|
// subresources should never allow create on update.
|
||||||
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
|
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
|
||||||
|
@ -35,12 +35,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
|
|
||||||
@ -355,7 +352,6 @@ func (g mockPodGetter) Get(context.Context, string, *metav1.GetOptions) (runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckLogLocation(t *testing.T) {
|
func TestCheckLogLocation(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
ctx := genericapirequest.NewDefaultContext()
|
ctx := genericapirequest.NewDefaultContext()
|
||||||
fakePodName := "test"
|
fakePodName := "test"
|
||||||
tcs := []struct {
|
tcs := []struct {
|
||||||
|
@ -36,13 +36,11 @@ import (
|
|||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/componentstatus"
|
"k8s.io/kubernetes/pkg/registry/core/componentstatus"
|
||||||
configmapstore "k8s.io/kubernetes/pkg/registry/core/configmap/storage"
|
configmapstore "k8s.io/kubernetes/pkg/registry/core/configmap/storage"
|
||||||
@ -291,9 +289,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(apiResourceConfigSource
|
|||||||
if podStorage.Eviction != nil {
|
if podStorage.Eviction != nil {
|
||||||
storage[resource+"/eviction"] = podStorage.Eviction
|
storage[resource+"/eviction"] = podStorage.Eviction
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
|
||||||
storage[resource+"/ephemeralcontainers"] = podStorage.EphemeralContainers
|
storage[resource+"/ephemeralcontainers"] = podStorage.EphemeralContainers
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if resource := "bindings"; apiResourceConfigSource.ResourceEnabled(corev1.SchemeGroupVersion.WithResource(resource)) {
|
if resource := "bindings"; apiResourceConfigSource.ResourceEnabled(corev1.SchemeGroupVersion.WithResource(resource)) {
|
||||||
|
@ -28,10 +28,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/util/slice"
|
"k8s.io/kubernetes/pkg/util/slice"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
utilptr "k8s.io/utils/pointer"
|
utilptr "k8s.io/utils/pointer"
|
||||||
@ -581,7 +578,6 @@ func TestMakeAbsolutePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPodVolumeNames(t *testing.T) {
|
func TestGetPodVolumeNames(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
|
@ -1205,8 +1205,6 @@ message EnvVarSource {
|
|||||||
//
|
//
|
||||||
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
||||||
// Pod. Ephemeral containers may not be removed or restarted.
|
// Pod. Ephemeral containers may not be removed or restarted.
|
||||||
//
|
|
||||||
// This is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
message EphemeralContainer {
|
message EphemeralContainer {
|
||||||
// Ephemeral containers have all of the fields of Container, plus additional fields
|
// Ephemeral containers have all of the fields of Container, plus additional fields
|
||||||
// specific to ephemeral containers. Fields in common with Container are in the
|
// specific to ephemeral containers. Fields in common with Container are in the
|
||||||
@ -3489,7 +3487,6 @@ message PodSpec {
|
|||||||
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
||||||
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
||||||
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
// +patchMergeKey=name
|
// +patchMergeKey=name
|
||||||
// +patchStrategy=merge
|
// +patchStrategy=merge
|
||||||
@ -3828,7 +3825,6 @@ message PodStatus {
|
|||||||
optional string qosClass = 9;
|
optional string qosClass = 9;
|
||||||
|
|
||||||
// Status for any ephemeral containers that have run in this pod.
|
// Status for any ephemeral containers that have run in this pod.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
repeated ContainerStatus ephemeralContainerStatuses = 13;
|
repeated ContainerStatus ephemeralContainerStatuses = 13;
|
||||||
}
|
}
|
||||||
|
@ -3090,7 +3090,6 @@ type PodSpec struct {
|
|||||||
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
|
||||||
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
|
||||||
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
// +patchMergeKey=name
|
// +patchMergeKey=name
|
||||||
// +patchStrategy=merge
|
// +patchStrategy=merge
|
||||||
@ -3804,8 +3803,6 @@ var _ = Container(EphemeralContainerCommon{})
|
|||||||
//
|
//
|
||||||
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
||||||
// Pod. Ephemeral containers may not be removed or restarted.
|
// Pod. Ephemeral containers may not be removed or restarted.
|
||||||
//
|
|
||||||
// This is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
type EphemeralContainer struct {
|
type EphemeralContainer struct {
|
||||||
// Ephemeral containers have all of the fields of Container, plus additional fields
|
// Ephemeral containers have all of the fields of Container, plus additional fields
|
||||||
// specific to ephemeral containers. Fields in common with Container are in the
|
// specific to ephemeral containers. Fields in common with Container are in the
|
||||||
@ -3907,7 +3904,6 @@ type PodStatus struct {
|
|||||||
// +optional
|
// +optional
|
||||||
QOSClass PodQOSClass `json:"qosClass,omitempty" protobuf:"bytes,9,rep,name=qosClass"`
|
QOSClass PodQOSClass `json:"qosClass,omitempty" protobuf:"bytes,9,rep,name=qosClass"`
|
||||||
// Status for any ephemeral containers that have run in this pod.
|
// Status for any ephemeral containers that have run in this pod.
|
||||||
// This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.
|
|
||||||
// +optional
|
// +optional
|
||||||
EphemeralContainerStatuses []ContainerStatus `json:"ephemeralContainerStatuses,omitempty" protobuf:"bytes,13,rep,name=ephemeralContainerStatuses"`
|
EphemeralContainerStatuses []ContainerStatus `json:"ephemeralContainerStatuses,omitempty" protobuf:"bytes,13,rep,name=ephemeralContainerStatuses"`
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,7 @@ func (EnvVarSource) SwaggerDoc() map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var map_EphemeralContainer = map[string]string{
|
var map_EphemeralContainer = map[string]string{
|
||||||
"": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.\n\nThis is a beta feature available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
|
||||||
"targetContainerName": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec.\n\nThe container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined.",
|
"targetContainerName": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec.\n\nThe container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined.",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1638,7 +1638,7 @@ var map_PodSpec = map[string]string{
|
|||||||
"volumes": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes",
|
"volumes": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes",
|
||||||
"initContainers": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
|
"initContainers": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
|
||||||
"containers": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
|
"containers": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
|
||||||
"ephemeralContainers": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"ephemeralContainers": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
|
||||||
"restartPolicy": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy",
|
"restartPolicy": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy",
|
||||||
"terminationGracePeriodSeconds": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.",
|
"terminationGracePeriodSeconds": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.",
|
||||||
"activeDeadlineSeconds": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.",
|
"activeDeadlineSeconds": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.",
|
||||||
@ -1691,7 +1691,7 @@ var map_PodStatus = map[string]string{
|
|||||||
"initContainerStatuses": "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status",
|
"initContainerStatuses": "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status",
|
||||||
"containerStatuses": "The list has one entry per container in the manifest. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status",
|
"containerStatuses": "The list has one entry per container in the manifest. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status",
|
||||||
"qosClass": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md",
|
"qosClass": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md",
|
||||||
"ephemeralContainerStatuses": "Status for any ephemeral containers that have run in this pod. This field is beta-level and available on clusters that haven't disabled the EphemeralContainers feature gate.",
|
"ephemeralContainerStatuses": "Status for any ephemeral containers that have run in this pod.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (PodStatus) SwaggerDoc() map[string]string {
|
func (PodStatus) SwaggerDoc() map[string]string {
|
||||||
|
@ -20,12 +20,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||||
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
|
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
admissionapi "k8s.io/pod-security-admission/api"
|
admissionapi "k8s.io/pod-security-admission/api"
|
||||||
|
|
||||||
@ -69,11 +67,6 @@ var _ = SIGDescribe("Ephemeral Containers [NodeConformance]", func() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := podClient.AddEphemeralContainerSync(pod, ec, time.Minute)
|
err := podClient.AddEphemeralContainerSync(pod, ec, time.Minute)
|
||||||
// BEGIN TODO: Remove when EphemeralContainers feature gate is retired.
|
|
||||||
if apierrors.IsNotFound(err) {
|
|
||||||
e2eskipper.Skipf("Skipping test because EphemeralContainers feature disabled (error: %q)", err)
|
|
||||||
}
|
|
||||||
// END TODO: Remove when EphemeralContainers feature gate is retired.
|
|
||||||
framework.ExpectNoError(err, "Failed to patch ephemeral containers in pod %q", format.Pod(pod))
|
framework.ExpectNoError(err, "Failed to patch ephemeral containers in pod %q", format.Pod(pod))
|
||||||
|
|
||||||
ginkgo.By("checking pod container endpoints")
|
ginkgo.By("checking pod container endpoints")
|
||||||
|
@ -50,7 +50,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
@ -58,7 +57,6 @@ import (
|
|||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
watchtools "k8s.io/client-go/tools/watch"
|
watchtools "k8s.io/client-go/tools/watch"
|
||||||
"k8s.io/component-base/featuregate"
|
|
||||||
testutils "k8s.io/kubernetes/test/utils"
|
testutils "k8s.io/kubernetes/test/utils"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
uexec "k8s.io/utils/exec"
|
uexec "k8s.io/utils/exec"
|
||||||
@ -776,8 +774,6 @@ func (f *Framework) testContainerOutputMatcher(scenarioName string,
|
|||||||
type ContainerType int
|
type ContainerType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// FeatureEphemeralContainers allows running an ephemeral container in pod namespaces to troubleshoot a running pod
|
|
||||||
FeatureEphemeralContainers featuregate.Feature = "EphemeralContainers"
|
|
||||||
// Containers is for normal containers
|
// Containers is for normal containers
|
||||||
Containers ContainerType = 1 << iota
|
Containers ContainerType = 1 << iota
|
||||||
// InitContainers is for init containers
|
// InitContainers is for init containers
|
||||||
@ -790,11 +786,7 @@ const (
|
|||||||
// types except for the ones guarded by feature gate.
|
// types except for the ones guarded by feature gate.
|
||||||
// Copied from pkg/api/v1/pod to avoid pulling extra dependencies
|
// Copied from pkg/api/v1/pod to avoid pulling extra dependencies
|
||||||
func allFeatureEnabledContainers() ContainerType {
|
func allFeatureEnabledContainers() ContainerType {
|
||||||
containerType := AllContainers
|
return AllContainers
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(FeatureEphemeralContainers) {
|
|
||||||
containerType &= ^EphemeralContainers
|
|
||||||
}
|
|
||||||
return containerType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerVisitor is called with each container spec, and returns true
|
// ContainerVisitor is called with each container spec, and returns true
|
||||||
|
@ -609,12 +609,6 @@ func testVolumeClient(f *framework.Framework, config TestConfig, fsGroup *int64,
|
|||||||
ec.Name = "volume-ephemeral-container"
|
ec.Name = "volume-ephemeral-container"
|
||||||
err = f.PodClient().AddEphemeralContainerSync(clientPod, ec, timeouts.PodStart)
|
err = f.PodClient().AddEphemeralContainerSync(clientPod, ec, timeouts.PodStart)
|
||||||
// The API server will return NotFound for the subresource when the feature is disabled
|
// The API server will return NotFound for the subresource when the feature is disabled
|
||||||
// BEGIN TODO: remove after EphemeralContainers feature gate is retired
|
|
||||||
if apierrors.IsNotFound(err) {
|
|
||||||
framework.Logf("Skipping ephemeral container re-test because feature is disabled (error: %q)", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// END TODO: remove after EphemeralContainers feature gate is retired
|
|
||||||
framework.ExpectNoError(err, "failed to add ephemeral container for re-test")
|
framework.ExpectNoError(err, "failed to add ephemeral container for re-test")
|
||||||
testVolumeContent(f, clientPod, ec.Name, fsGroup, fsType, tests)
|
testVolumeContent(f, clientPod, ec.Name, fsGroup, fsType, tests)
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ func testWebhookAdmission(t *testing.T, watchCache bool) {
|
|||||||
// force enable all resources so we can check storage.
|
// force enable all resources so we can check storage.
|
||||||
"--runtime-config=api/all=true",
|
"--runtime-config=api/all=true",
|
||||||
// enable feature-gates that protect resources to check their storage, too.
|
// enable feature-gates that protect resources to check their storage, too.
|
||||||
"--feature-gates=EphemeralContainers=true",
|
// e.g. "--feature-gates=EphemeralContainers=true",
|
||||||
}, etcdConfig)
|
}, etcdConfig)
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
|
|
||||||
|
@ -23,15 +23,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/test/integration"
|
"k8s.io/kubernetes/test/integration"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
)
|
)
|
||||||
@ -190,8 +186,6 @@ func TestPodReadOnlyFilesystem(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodCreateEphemeralContainers(t *testing.T) {
|
func TestPodCreateEphemeralContainers(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
@ -261,8 +255,6 @@ func setUpEphemeralContainers(podsClient typedv1.PodInterface, pod *v1.Pod, cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodPatchEphemeralContainers(t *testing.T) {
|
func TestPodPatchEphemeralContainers(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
@ -494,8 +486,6 @@ func TestPodPatchEphemeralContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodUpdateEphemeralContainers(t *testing.T) {
|
func TestPodUpdateEphemeralContainers(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
|
||||||
|
|
||||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
@ -684,61 +674,3 @@ func TestPodUpdateEphemeralContainers(t *testing.T) {
|
|||||||
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPodEphemeralContainersDisabled tests that the API server returns a 404 when the feature is disabled (because the subresource won't exist).
|
|
||||||
// This validates that the feature gate is working, but kubectl also uses the 404 to guess that the feature is disabled on the server.
|
|
||||||
func TestPodEphemeralContainersDisabled(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, false)()
|
|
||||||
|
|
||||||
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
|
|
||||||
defer server.TearDownFn()
|
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(server.ClientConfig)
|
|
||||||
|
|
||||||
ns := framework.CreateNamespaceOrDie(client, "pod-ephemeral-containers-disabled", t)
|
|
||||||
defer framework.DeleteNamespaceOrDie(client, ns, t)
|
|
||||||
|
|
||||||
pod := &v1.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "ephemeral-container-pod",
|
|
||||||
},
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
Containers: []v1.Container{
|
|
||||||
{
|
|
||||||
Name: "fake-name",
|
|
||||||
Image: "fakeimage",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
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