Add Ephemeral Containers to the Kubernetes core API

This commit is contained in:
Lee Verberne
2018-08-09 15:24:23 +02:00
parent c7ffc1cd8c
commit 013f049ce0
20 changed files with 1492 additions and 64 deletions

View File

@@ -45,6 +45,13 @@ func VisitContainers(podSpec *api.PodSpec, visitor ContainerVisitor) bool {
return false
}
}
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
for i := range podSpec.EphemeralContainers {
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon)) {
return false
}
}
}
return true
}
@@ -362,6 +369,9 @@ func dropDisabledFields(
return true
})
}
if !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
podSpec.EphemeralContainers = nil
}
if (!utilfeature.DefaultFeatureGate.Enabled(features.VolumeSubpath) || !utilfeature.DefaultFeatureGate.Enabled(features.VolumeSubpathEnvExpansion)) && !subpathExprInUse(oldPodSpec) {
// drop subpath env expansion from the pod if either of the subpath features is disabled and the old spec did not specify subpath env expansion

View File

@@ -35,6 +35,8 @@ import (
)
func TestVisitContainers(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
testCases := []struct {
description string
haveSpec *api.PodSpec
@@ -79,6 +81,37 @@ func TestVisitContainers(t *testing.T) {
},
[]string{"i1", "i2", "c1", "c2"},
},
{
"ephemeral containers",
&api.PodSpec{
Containers: []api.Container{
{Name: "c1"},
{Name: "c2"},
},
EphemeralContainers: []api.EphemeralContainer{
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
},
},
[]string{"c1", "c2", "e1"},
},
{
"all container types",
&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"}},
},
},
[]string{"i1", "i2", "c1", "c2", "e1", "e2"},
},
{
"dropping fields",
&api.PodSpec{
@@ -90,8 +123,12 @@ func TestVisitContainers(t *testing.T) {
{Name: "i1"},
{Name: "i2", SecurityContext: &api.SecurityContext{}},
},
EphemeralContainers: []api.EphemeralContainer{
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2", SecurityContext: &api.SecurityContext{}}},
},
},
[]string{"i1", "i2", "c1", "c2"},
[]string{"i1", "i2", "c1", "c2", "e1", "e2"},
},
}
@@ -117,10 +154,17 @@ func TestVisitContainers(t *testing.T) {
t.Errorf("VisitContainers() for test case %q: got SecurityContext %#v for init container %v, wanted nil", tc.description, c.SecurityContext, c.Name)
}
}
for _, c := range tc.haveSpec.EphemeralContainers {
if c.SecurityContext != nil {
t.Errorf("VisitContainers() for test case %q: got SecurityContext %#v for ephemeral container %v, wanted nil", tc.description, c.SecurityContext, c.Name)
}
}
}
}
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{
@@ -195,6 +239,17 @@ func TestPodSecrets(t *testing.T) {
CSI: &api.CSIVolumeSource{
NodePublishSecretRef: &api.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
EphemeralContainers: []api.EphemeralContainer{{
EphemeralContainerCommon: api.EphemeralContainerCommon{
EnvFrom: []api.EnvFromSource{{
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef"}}}},
Env: []api.EnvVar{{
ValueFrom: &api.EnvVarSource{
SecretKeyRef: &api.SecretKeySelector{
LocalObjectReference: api.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef"}}}}}}}},
},
}
extractedNames := sets.NewString()
@@ -212,6 +267,8 @@ func TestPodSecrets(t *testing.T) {
expectedSecretPaths := sets.NewString(
"Spec.Containers[*].EnvFrom[*].SecretRef",
"Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef",
"Spec.ImagePullSecrets",
"Spec.InitContainers[*].EnvFrom[*].SecretRef",
"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
@@ -290,6 +347,8 @@ 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{
@@ -304,6 +363,17 @@ func TestPodConfigmaps(t *testing.T) {
ConfigMapKeyRef: &api.ConfigMapKeySelector{
LocalObjectReference: api.LocalObjectReference{
Name: "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
EphemeralContainers: []api.EphemeralContainer{{
EphemeralContainerCommon: api.EphemeralContainerCommon{
EnvFrom: []api.EnvFromSource{{
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef"}}}},
Env: []api.EnvVar{{
ValueFrom: &api.EnvVarSource{
ConfigMapKeyRef: &api.ConfigMapKeySelector{
LocalObjectReference: api.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}}},
InitContainers: []api.Container{{
EnvFrom: []api.EnvFromSource{{
ConfigMapRef: &api.ConfigMapEnvSource{
@@ -338,6 +408,8 @@ func TestPodConfigmaps(t *testing.T) {
expectedPaths := sets.NewString(
"Spec.Containers[*].EnvFrom[*].ConfigMapRef",
"Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.InitContainers[*].EnvFrom[*].ConfigMapRef",
"Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap",

View File

@@ -50,6 +50,7 @@ func TestDefaulting(t *testing.T) {
{Group: "", Version: "v1", Kind: "ConfigMapList"}: {},
{Group: "", Version: "v1", Kind: "Endpoints"}: {},
{Group: "", Version: "v1", Kind: "EndpointsList"}: {},
{Group: "", Version: "v1", Kind: "EphemeralContainers"}: {},
{Group: "", Version: "v1", Kind: "Namespace"}: {},
{Group: "", Version: "v1", Kind: "NamespaceList"}: {},
{Group: "", Version: "v1", Kind: "Node"}: {},

View File

@@ -23,6 +23,8 @@ import (
"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
@@ -67,6 +69,13 @@ func VisitContainers(podSpec *v1.PodSpec, visitor ContainerVisitor) bool {
return false
}
}
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
for i := range podSpec.EphemeralContainers {
if !visitor((*v1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon)) {
return false
}
}
}
return true
}

View File

@@ -28,6 +28,9 @@ 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) {
@@ -199,6 +202,8 @@ func TestFindPort(t *testing.T) {
}
func TestVisitContainers(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
testCases := []struct {
description string
haveSpec *v1.PodSpec
@@ -243,6 +248,37 @@ func TestVisitContainers(t *testing.T) {
},
[]string{"i1", "i2", "c1", "c2"},
},
{
"ephemeral containers",
&v1.PodSpec{
Containers: []v1.Container{
{Name: "c1"},
{Name: "c2"},
},
EphemeralContainers: []v1.EphemeralContainer{
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e1"}},
},
},
[]string{"c1", "c2", "e1"},
},
{
"all container types",
&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"}},
},
},
[]string{"i1", "i2", "c1", "c2", "e1", "e2"},
},
{
"dropping fields",
&v1.PodSpec{
@@ -254,8 +290,12 @@ func TestVisitContainers(t *testing.T) {
{Name: "i1"},
{Name: "i2", SecurityContext: &v1.SecurityContext{}},
},
EphemeralContainers: []v1.EphemeralContainer{
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e1"}},
{EphemeralContainerCommon: v1.EphemeralContainerCommon{Name: "e2", SecurityContext: &v1.SecurityContext{}}},
},
},
[]string{"i1", "i2", "c1", "c2"},
[]string{"i1", "i2", "c1", "c2", "e1", "e2"},
},
}
@@ -281,10 +321,17 @@ func TestVisitContainers(t *testing.T) {
t.Errorf("VisitContainers() for test case %q: got SecurityContext %#v for init container %v, wanted nil", tc.description, c.SecurityContext, c.Name)
}
}
for _, c := range tc.haveSpec.EphemeralContainers {
if c.SecurityContext != nil {
t.Errorf("VisitContainers() for test case %q: got SecurityContext %#v for ephemeral container %v, wanted nil", tc.description, c.SecurityContext, c.Name)
}
}
}
}
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{
@@ -359,6 +406,17 @@ func TestPodSecrets(t *testing.T) {
CSI: &v1.CSIVolumeSource{
NodePublishSecretRef: &v1.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
EphemeralContainers: []v1.EphemeralContainer{{
EphemeralContainerCommon: v1.EphemeralContainerCommon{
EnvFrom: []v1.EnvFromSource{{
SecretRef: &v1.SecretEnvSource{
LocalObjectReference: v1.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef"}}}},
Env: []v1.EnvVar{{
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef"}}}}}}}},
},
}
extractedNames := sets.NewString()
@@ -376,6 +434,8 @@ func TestPodSecrets(t *testing.T) {
expectedSecretPaths := sets.NewString(
"Spec.Containers[*].EnvFrom[*].SecretRef",
"Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef",
"Spec.ImagePullSecrets",
"Spec.InitContainers[*].EnvFrom[*].SecretRef",
"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
@@ -454,6 +514,8 @@ 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{
@@ -468,6 +530,17 @@ func TestPodConfigmaps(t *testing.T) {
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
EphemeralContainers: []v1.EphemeralContainer{{
EphemeralContainerCommon: v1.EphemeralContainerCommon{
EnvFrom: []v1.EnvFromSource{{
ConfigMapRef: &v1.ConfigMapEnvSource{
LocalObjectReference: v1.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef"}}}},
Env: []v1.EnvVar{{
ValueFrom: &v1.EnvVarSource{
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}}},
InitContainers: []v1.Container{{
EnvFrom: []v1.EnvFromSource{{
ConfigMapRef: &v1.ConfigMapEnvSource{
@@ -502,6 +575,8 @@ func TestPodConfigmaps(t *testing.T) {
expectedPaths := sets.NewString(
"Spec.Containers[*].EnvFrom[*].ConfigMapRef",
"Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef",
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.InitContainers[*].EnvFrom[*].ConfigMapRef",
"Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef",
"Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap",