Merge pull request #111402 from verb/111030-ec-ga
Promote EphemeralContainers feature to GA
This commit is contained in:
@@ -46,11 +46,7 @@ const AllContainers ContainerType = (InitContainers | Containers | EphemeralCont
|
||||
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
||||
// types except for the ones guarded by feature gate.
|
||||
func AllFeatureEnabledContainers() ContainerType {
|
||||
containerType := AllContainers
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
containerType &= ^EphemeralContainers
|
||||
}
|
||||
return containerType
|
||||
return AllContainers
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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 {
|
||||
@@ -654,13 +646,6 @@ func nodeTaintsPolicyInUse(podSpec *api.PodSpec) bool {
|
||||
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
|
||||
func procMountInUse(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
|
@@ -39,11 +39,10 @@ import (
|
||||
func TestVisitContainers(t *testing.T) {
|
||||
setAllFeatureEnabledContainersDuringTest := ContainerType(0)
|
||||
testCases := []struct {
|
||||
desc string
|
||||
spec *api.PodSpec
|
||||
wantContainers []string
|
||||
mask ContainerType
|
||||
ephemeralContainersEnabled bool
|
||||
desc string
|
||||
spec *api.PodSpec
|
||||
wantContainers []string
|
||||
mask ContainerType
|
||||
}{
|
||||
{
|
||||
desc: "empty podspec",
|
||||
@@ -127,25 +126,6 @@ func TestVisitContainers(t *testing.T) {
|
||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||
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",
|
||||
spec: &api.PodSpec{
|
||||
@@ -162,9 +142,8 @@ func TestVisitContainers(t *testing.T) {
|
||||
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
|
||||
},
|
||||
},
|
||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||
mask: setAllFeatureEnabledContainersDuringTest,
|
||||
ephemeralContainersEnabled: true,
|
||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||
mask: setAllFeatureEnabledContainersDuringTest,
|
||||
},
|
||||
{
|
||||
desc: "dropping fields",
|
||||
@@ -189,8 +168,6 @@ func TestVisitContainers(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, tc.ephemeralContainersEnabled)()
|
||||
|
||||
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
||||
tc.mask = AllFeatureEnabledContainers()
|
||||
}
|
||||
@@ -226,8 +203,6 @@ func TestVisitContainers(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.
|
||||
// The names of the referenced secrets match struct paths detected by reflection.
|
||||
pod := &api.Pod{
|
||||
@@ -425,8 +400,6 @@ func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, n
|
||||
}
|
||||
|
||||
func TestPodConfigmaps(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
// Stub containing all possible ConfigMap references in a pod.
|
||||
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
||||
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) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@@ -23,8 +23,6 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"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
|
||||
@@ -68,11 +66,7 @@ const AllContainers ContainerType = (InitContainers | Containers | EphemeralCont
|
||||
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
|
||||
// types except for the ones guarded by feature gate.
|
||||
func AllFeatureEnabledContainers() ContainerType {
|
||||
containerType := AllContainers
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
containerType &= ^EphemeralContainers
|
||||
}
|
||||
return containerType
|
||||
return AllContainers
|
||||
}
|
||||
|
||||
// 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/sets"
|
||||
"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) {
|
||||
@@ -205,11 +202,10 @@ func TestFindPort(t *testing.T) {
|
||||
func TestVisitContainers(t *testing.T) {
|
||||
setAllFeatureEnabledContainersDuringTest := ContainerType(0)
|
||||
testCases := []struct {
|
||||
desc string
|
||||
spec *v1.PodSpec
|
||||
wantContainers []string
|
||||
mask ContainerType
|
||||
ephemeralContainersEnabled bool
|
||||
desc string
|
||||
spec *v1.PodSpec
|
||||
wantContainers []string
|
||||
mask ContainerType
|
||||
}{
|
||||
{
|
||||
desc: "empty podspec",
|
||||
@@ -294,26 +290,7 @@ func TestVisitContainers(t *testing.T) {
|
||||
mask: AllContainers,
|
||||
},
|
||||
{
|
||||
desc: "all feature enabled container types with ephemeral containers disabled",
|
||||
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",
|
||||
desc: "all feature enabled container types",
|
||||
spec: &v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "c1"},
|
||||
@@ -328,9 +305,8 @@ func TestVisitContainers(t *testing.T) {
|
||||
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e2"}},
|
||||
},
|
||||
},
|
||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||
mask: setAllFeatureEnabledContainersDuringTest,
|
||||
ephemeralContainersEnabled: true,
|
||||
wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
|
||||
mask: setAllFeatureEnabledContainersDuringTest,
|
||||
},
|
||||
{
|
||||
desc: "dropping fields",
|
||||
@@ -355,8 +331,6 @@ func TestVisitContainers(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, tc.ephemeralContainersEnabled)()
|
||||
|
||||
if tc.mask == setAllFeatureEnabledContainersDuringTest {
|
||||
tc.mask = AllFeatureEnabledContainers()
|
||||
}
|
||||
@@ -392,8 +366,6 @@ func TestVisitContainers(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.
|
||||
// The names of the referenced secrets match struct paths detected by reflection.
|
||||
pod := &v1.Pod{
|
||||
@@ -591,8 +563,6 @@ func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, n
|
||||
}
|
||||
|
||||
func TestPodConfigmaps(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
// Stub containing all possible ConfigMap references in a pod.
|
||||
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
||||
pod := &v1.Pod{
|
||||
|
@@ -2396,8 +2396,6 @@ func TestValidateDaemonSetUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateDaemonSet(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
@@ -2660,8 +2658,6 @@ func validDeployment() *apps.Deployment {
|
||||
}
|
||||
|
||||
func TestValidateDeployment(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
successCases := []*apps.Deployment{
|
||||
validDeployment(),
|
||||
}
|
||||
|
@@ -20,9 +20,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/fieldpath"
|
||||
)
|
||||
|
||||
@@ -47,12 +45,10 @@ func VisitContainersWithPath(podSpec *api.PodSpec, specPath *field.Path, visitor
|
||||
return false
|
||||
}
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
fldPath = specPath.Child("ephemeralContainers")
|
||||
for i := range podSpec.EphemeralContainers {
|
||||
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i)) {
|
||||
return false
|
||||
}
|
||||
fldPath = specPath.Child("ephemeralContainers")
|
||||
for i := range podSpec.EphemeralContainers {
|
||||
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@@ -21,15 +21,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
func TestVisitContainersWithPath(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
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
|
||||
// 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.
|
||||
// +optional
|
||||
EphemeralContainers []EphemeralContainer
|
||||
// +optional
|
||||
@@ -3326,8 +3325,6 @@ var _ = Container(EphemeralContainerCommon{})
|
||||
//
|
||||
// To add an ephemeral container, use the ephemeralcontainers subresource of an existing
|
||||
// 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 {
|
||||
// Ephemeral containers have all of the fields of Container, plus additional fields
|
||||
// specific to ephemeral containers. Fields in common with Container are in the
|
||||
@@ -3390,7 +3387,6 @@ type PodStatus struct {
|
||||
ContainerStatuses []ContainerStatus
|
||||
|
||||
// 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
|
||||
EphemeralContainerStatuses []ContainerStatus
|
||||
}
|
||||
|
@@ -12171,8 +12171,6 @@ func makeValidService() core.Service {
|
||||
}
|
||||
|
||||
func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
|
||||
makePod := func(ephemeralContainers []core.EphemeralContainer) *core.Pod {
|
||||
return &core.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -20998,7 +20996,6 @@ func TestValidateWindowsHostProcessPod(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
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.EphemeralContainers, true)()
|
||||
|
||||
opts := PodValidationOptions{AllowWindowsHostProcessField: testCase.featureEnabled}
|
||||
|
||||
|
@@ -285,6 +285,7 @@ const (
|
||||
// owner: @verb
|
||||
// alpha: v1.16
|
||||
// beta: v1.23
|
||||
// GA: v1.25
|
||||
//
|
||||
// Allows running an ephemeral container in pod namespaces to troubleshoot a running pod.
|
||||
EphemeralContainers featuregate.Feature = "EphemeralContainers"
|
||||
@@ -884,7 +885,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
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
|
||||
|
||||
|
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{
|
||||
Schema: spec.Schema{
|
||||
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"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"name": {
|
||||
@@ -21888,7 +21888,7 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA
|
||||
},
|
||||
},
|
||||
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"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
@@ -22341,7 +22341,7 @@ func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.Ope
|
||||
},
|
||||
"ephemeralContainerStatuses": {
|
||||
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"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
|
@@ -26,9 +26,6 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/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) {
|
||||
@@ -326,7 +323,6 @@ func TestExpandVolumeMountsWithSubpath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContainerSpec(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
havePod *v1.Pod
|
||||
|
@@ -20,10 +20,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
ref "k8s.io/client-go/tools/reference"
|
||||
"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).
|
||||
@@ -67,15 +65,13 @@ func fieldPath(pod *v1.Pod, container *v1.Container) (string, error) {
|
||||
return fmt.Sprintf("spec.initContainers{%s}", here.Name), nil
|
||||
}
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
here := &pod.Spec.EphemeralContainers[i]
|
||||
if here.Name == container.Name {
|
||||
if here.Name == "" {
|
||||
return fmt.Sprintf("spec.ephemeralContainers[%d]", i), nil
|
||||
}
|
||||
return fmt.Sprintf("spec.ephemeralContainers{%s}", here.Name), nil
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
here := &pod.Spec.EphemeralContainers[i]
|
||||
if here.Name == container.Name {
|
||||
if here.Name == "" {
|
||||
return fmt.Sprintf("spec.ephemeralContainers[%d]", i), 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)
|
||||
|
@@ -1221,7 +1221,7 @@ func (kl *Kubelet) validateContainerLogStatus(podName string, podStatus *v1.PodS
|
||||
if !found {
|
||||
cStatus, found = podutil.GetContainerStatus(podStatus.InitContainerStatuses, containerName)
|
||||
}
|
||||
if !found && utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
if !found {
|
||||
cStatus, found = podutil.GetContainerStatus(podStatus.EphemeralContainerStatuses, containerName)
|
||||
}
|
||||
if !found {
|
||||
@@ -1602,23 +1602,21 @@ func (kl *Kubelet) convertStatusToAPIStatus(pod *v1.Pod, podStatus *kubecontaine
|
||||
len(pod.Spec.InitContainers) > 0,
|
||||
true,
|
||||
)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
var ecSpecs []v1.Container
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
ecSpecs = append(ecSpecs, v1.Container(pod.Spec.EphemeralContainers[i].EphemeralContainerCommon))
|
||||
}
|
||||
|
||||
// #80875: By now we've iterated podStatus 3 times. We could refactor this to make a single
|
||||
// pass through podStatus.ContainerStatuses
|
||||
apiPodStatus.EphemeralContainerStatuses = kl.convertToAPIContainerStatuses(
|
||||
pod, podStatus,
|
||||
oldPodStatus.EphemeralContainerStatuses,
|
||||
ecSpecs,
|
||||
len(pod.Spec.InitContainers) > 0,
|
||||
false,
|
||||
)
|
||||
var ecSpecs []v1.Container
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
ecSpecs = append(ecSpecs, v1.Container(pod.Spec.EphemeralContainers[i].EphemeralContainerCommon))
|
||||
}
|
||||
|
||||
// #80875: By now we've iterated podStatus 3 times. We could refactor this to make a single
|
||||
// pass through podStatus.ContainerStatuses
|
||||
apiPodStatus.EphemeralContainerStatuses = kl.convertToAPIContainerStatuses(
|
||||
pod, podStatus,
|
||||
oldPodStatus.EphemeralContainerStatuses,
|
||||
ecSpecs,
|
||||
len(pod.Spec.InitContainers) > 0,
|
||||
false,
|
||||
)
|
||||
|
||||
return &apiPodStatus
|
||||
}
|
||||
|
||||
|
@@ -46,9 +46,7 @@ import (
|
||||
kubetypes "k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cri/remote"
|
||||
"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.
|
||||
// This always returns nil when the EphemeralContainers feature is disabled.
|
||||
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
|
||||
}
|
||||
|
||||
|
@@ -520,7 +520,6 @@ func TestGetHugepageLimitsFromResources(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
_, _, m, err := createTestRuntimeManager()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating test RuntimeManager: %v", err)
|
||||
|
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package kuberuntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -32,10 +31,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/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"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
@@ -405,23 +401,12 @@ func TestStartSpec(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 {
|
||||
t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err)
|
||||
} else if diff := cmp.Diff(tc.want, got); 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,14 +578,12 @@ func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *ku
|
||||
}
|
||||
|
||||
// Ephemeral containers may be started even if initialization is not yet complete.
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
c := (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon)
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
c := (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon)
|
||||
|
||||
// Ephemeral Containers are never restarted
|
||||
if podStatus.FindContainerStatusByName(c.Name) == nil {
|
||||
changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i)
|
||||
}
|
||||
// Ephemeral Containers are never restarted
|
||||
if podStatus.FindContainerStatusByName(c.Name) == nil {
|
||||
changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,10 +912,8 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine
|
||||
// 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
|
||||
// containers cannot be specified on pod creation.
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
for _, idx := range podContainerChanges.EphemeralContainersToStart {
|
||||
start("ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
||||
}
|
||||
for _, idx := range podContainerChanges.EphemeralContainersToStart {
|
||||
start("ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
||||
}
|
||||
|
||||
// Step 6: start the init container.
|
||||
|
@@ -35,14 +35,11 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
apitest "k8s.io/cri-api/pkg/apis/testing"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results"
|
||||
@@ -490,9 +487,6 @@ func TestGetPods(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()
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -1372,7 +1366,6 @@ func makeBasePodAndStatusWithInitContainers() (*v1.Pod, *kubecontainer.PodStatus
|
||||
}
|
||||
|
||||
func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
// Make sure existing test cases pass with feature enabled
|
||||
TestComputePodActions(t)
|
||||
TestComputePodActionsWithInitContainers(t)
|
||||
|
@@ -22,8 +22,6 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"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.
|
||||
// oldPod or newPod may be nil to signify creation or deletion.
|
||||
func updateMetrics(oldPod, newPod *v1.Pod) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
|
||||
return
|
||||
}
|
||||
|
||||
var numEC int
|
||||
if oldPod != nil {
|
||||
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 {
|
||||
if container.Name == containerName {
|
||||
found = true
|
||||
|
@@ -33,12 +33,10 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -350,10 +344,6 @@ func (r *EphemeralContainersREST) Destroy() {
|
||||
|
||||
// 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) {
|
||||
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
|
||||
// subresources should never allow create on update.
|
||||
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/types"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
|
||||
@@ -355,7 +352,6 @@ func (g mockPodGetter) Get(context.Context, string, *metav1.GetOptions) (runtime
|
||||
}
|
||||
|
||||
func TestCheckLogLocation(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
ctx := genericapirequest.NewDefaultContext()
|
||||
fakePodName := "test"
|
||||
tcs := []struct {
|
||||
|
@@ -36,13 +36,11 @@ import (
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/registry/core/componentstatus"
|
||||
configmapstore "k8s.io/kubernetes/pkg/registry/core/configmap/storage"
|
||||
@@ -291,9 +289,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(apiResourceConfigSource
|
||||
if podStorage.Eviction != nil {
|
||||
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)) {
|
||||
|
@@ -28,10 +28,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"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/features"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
utilptr "k8s.io/utils/pointer"
|
||||
@@ -581,7 +578,6 @@ func TestMakeAbsolutePath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPodVolumeNames(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
|
Reference in New Issue
Block a user