diff --git a/test/e2e/common/node/configmap.go b/test/e2e/common/node/configmap.go index e7fd1713d3a..309441c1634 100644 --- a/test/e2e/common/node/configmap.go +++ b/test/e2e/common/node/configmap.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" imageutils "k8s.io/kubernetes/test/utils/image" @@ -242,6 +243,54 @@ var _ = SIGDescribe("ConfigMap", func() { framework.ExpectNoError(err, "failed to list ConfigMap by LabelSelector") gomega.Expect(configMapList.Items).To(gomega.BeEmpty(), "ConfigMap is still present after being deleted by collection") }) + + /* + Release: v1.30 + Testname: ConfigMap, from environment field + Description: Create a Pod with an environment variable value set using a value from ConfigMap. + Allows users to use envFrom to set prefix starting with a digit as environment variable names. + */ + framework.It("should be consumable as environment variable names when configmap keys start with a digit", + feature.RelaxedEnvironmentVariableValidation, func(ctx context.Context) { + name := "configmap-test-" + string(uuid.NewUUID()) + configMap := newConfigMap(f, name) + ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name)) + var err error + if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-configmaps-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "env-test", + Image: imageutils.GetE2EImage(imageutils.BusyBox), + Command: []string{"sh", "-c", "env"}, + EnvFrom: []v1.EnvFromSource{ + { + ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, + }, + { + // prefix start with a digit can be consumed as environment variables. + Prefix: "1-", + ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + e2epodoutput.TestContainerOutput(ctx, f, "consume configMaps", pod, 0, []string{ + "data-1=value-1", "data-2=value-2", "data-3=value-3", + "1-data-1=value-1", "1-data-2=value-2", "1-data-3=value-3", + }) + }) }) func newConfigMap(f *framework.Framework, name string) *v1.ConfigMap { diff --git a/test/e2e/common/node/expansion.go b/test/e2e/common/node/expansion.go index a24f48b7857..48f6f410e5e 100644 --- a/test/e2e/common/node/expansion.go +++ b/test/e2e/common/node/expansion.go @@ -22,6 +22,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" @@ -371,6 +372,42 @@ var _ = SIGDescribe("Variable Expansion", func() { err = e2epod.DeletePodWithWait(ctx, f.ClientSet, pod) framework.ExpectNoError(err, "failed to delete pod") }) + + /* + Release: v1.30 + Testname: Environment variables, expansion + Description: Create a Pod with environment variables. Environment variables defined using previously defined environment variables MUST expand to proper values. + Allow almost all printable ASCII characters in environment variables. + */ + framework.It("allow almost all printable ASCII characters as environment variable names", feature.RelaxedEnvironmentVariableValidation, func(ctx context.Context) { + envVars := []v1.EnvVar{ + { + Name: "!\"#$%&'()", + Value: "value-1", + }, + { + Name: "* +,-./0123456789", + Value: "value-2", + }, + { + Name: ":;<>?@", + Value: "value-3", + }, + { + Name: "[\\]^_`{}|~", + Value: "value-4", + }, + } + pod := newPod([]string{"sh", "-c", "env"}, envVars, nil, nil) + + e2epodoutput.TestContainerOutput(ctx, f, "env composition", pod, 0, []string{ + "!\"#$%&'()=value-1", + "* +,-./0123456789=value-2", + ":;<>?@=value-3", + "[\\]^_`{}|~=value-4", + }) + }) + }) func testPodFailSubpath(ctx context.Context, f *framework.Framework, pod *v1.Pod) { diff --git a/test/e2e/common/node/secrets.go b/test/e2e/common/node/secrets.go index 885c2c4b9a9..29be3811933 100644 --- a/test/e2e/common/node/secrets.go +++ b/test/e2e/common/node/secrets.go @@ -22,17 +22,18 @@ import ( "encoding/json" "fmt" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" imageutils "k8s.io/kubernetes/test/utils/image" admissionapi "k8s.io/pod-security-admission/api" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" ) var _ = SIGDescribe("Secrets", func() { @@ -238,6 +239,54 @@ var _ = SIGDescribe("Secrets", func() { framework.Failf("secret %s/%s was not deleted successfully", f.Namespace.Name, secretTestName) } }) + + /* + Release: v1.30 + Testname: Secrets, pod environment from source + Description: Create a secret. Create a Pod with Container that declares a environment variable using 'EnvFrom' which references the secret created to extract a key value from the secret. + Allows users to use envFrom to set prefix starting with a digit as environment variable names. + */ + framework.It("should be consumable as environment variable names when secret keys start with a digit", feature.RelaxedEnvironmentVariableValidation, func(ctx context.Context) { + name := "secret-test-" + string(uuid.NewUUID()) + secret := secretForTest(f.Namespace.Name, name) + + ginkgo.By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name)) + var err error + if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { + framework.Failf("unable to create test secret %s: %v", secret.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-configmaps-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "env-test", + Image: imageutils.GetE2EImage(imageutils.BusyBox), + Command: []string{"sh", "-c", "env"}, + EnvFrom: []v1.EnvFromSource{ + { + SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, + }, + { + // prefix start with a digit can be consumed as environment variables. + Prefix: "1-", + SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{ + "data-1=value-1", "data-2=value-2", "data-3=value-3", + "1-data-1=value-1", "1-data-2=value-2", "1-data-3=value-3", + }) + }) }) func secretForTest(namespace, name string) *v1.Secret { diff --git a/test/e2e/feature/feature.go b/test/e2e/feature/feature.go index f7cae943d15..d95cae98add 100644 --- a/test/e2e/feature/feature.go +++ b/test/e2e/feature/feature.go @@ -253,6 +253,10 @@ var ( // TODO: document the feature (owning SIG, when to use this feature for a test) RecoverVolumeExpansionFailure = framework.WithFeature(framework.ValidFeatures.Add("RecoverVolumeExpansionFailure")) + // RelaxedEnvironmentVariableValidation used when we verify whether the pod can consume all printable ASCII characters as environment variable names, + // and whether the pod can consume configmap/secret that key starts with a number. + RelaxedEnvironmentVariableValidation = framework.WithFeature(framework.ValidFeatures.Add("RelaxedEnvironmentVariableValidation")) + // TODO: document the feature (owning SIG, when to use this feature for a test) Recreate = framework.WithFeature(framework.ValidFeatures.Add("Recreate"))