Merge pull request #111402 from verb/111030-ec-ga

Promote EphemeralContainers feature to GA
This commit is contained in:
Kubernetes Prow Robot
2022-07-29 19:29:20 -07:00
committed by GitHub
37 changed files with 69 additions and 419 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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