Remove EphemeralContainers feature-gate checks

This commit is contained in:
Lee Verberne 2022-07-24 16:36:42 +02:00
parent bc3c5ae269
commit d238e67ba6
27 changed files with 51 additions and 390 deletions

View File

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

View File

@ -39,11 +39,10 @@ import (
func TestVisitContainers(t *testing.T) { func TestVisitContainers(t *testing.T) {
setAllFeatureEnabledContainersDuringTest := ContainerType(0) setAllFeatureEnabledContainersDuringTest := ContainerType(0)
testCases := []struct { testCases := []struct {
desc string desc string
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{
@ -162,9 +142,8 @@ func TestVisitContainers(t *testing.T) {
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}}, {EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
}, },
}, },
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

View File

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

View File

@ -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) {
@ -205,11 +202,10 @@ func TestFindPort(t *testing.T) {
func TestVisitContainers(t *testing.T) { func TestVisitContainers(t *testing.T) {
setAllFeatureEnabledContainersDuringTest := ContainerType(0) setAllFeatureEnabledContainersDuringTest := ContainerType(0)
testCases := []struct { testCases := []struct {
desc string desc string
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"},
@ -328,9 +305,8 @@ func TestVisitContainers(t *testing.T) {
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e2"}}, {EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e2"}},
}, },
}, },
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{

View File

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

View File

@ -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,12 +45,10 @@ 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

View File

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

View File

@ -11485,8 +11485,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{
@ -20312,7 +20310,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}

View File

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

View File

@ -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,15 +65,13 @@ 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 { if here.Name == "" {
if here.Name == "" { return fmt.Sprintf("spec.ephemeralContainers[%d]", i), nil
return fmt.Sprintf("spec.ephemeralContainers[%d]", i), nil
}
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)

View File

@ -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,23 +1602,21 @@ 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))
}
// #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,
)
} }
// #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 return &apiPodStatus
} }

View File

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

View File

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

View File

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

View File

@ -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. // 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)
// Ephemeral Containers are never restarted // Ephemeral Containers are never restarted
if podStatus.FindContainerStatusByName(c.Name) == nil { if podStatus.FindContainerStatusByName(c.Name) == nil {
changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i) 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 // 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.

View File

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

View File

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

View File

@ -916,7 +916,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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