From 6d2e988e3b8ec96bda31f64ec056c386d841a3ae Mon Sep 17 00:00:00 2001 From: David Zhu Date: Thu, 12 Oct 2017 16:39:14 -0700 Subject: [PATCH] split configmap, downwardapi, and secrets, into two files each volume/non-volume and moved sig labels to front of the description string --- test/e2e/common/BUILD | 2 + test/e2e/common/configmap.go | 555 +----------------------- test/e2e/common/configmap_volume.go | 584 ++++++++++++++++++++++++++ test/e2e/common/downward_api.go | 2 +- test/e2e/common/downwardapi_volume.go | 28 +- test/e2e/common/secrets.go | 452 +------------------- test/e2e/common/secrets_volume.go | 482 +++++++++++++++++++++ 7 files changed, 1085 insertions(+), 1020 deletions(-) create mode 100644 test/e2e/common/configmap_volume.go create mode 100644 test/e2e/common/secrets_volume.go diff --git a/test/e2e/common/BUILD b/test/e2e/common/BUILD index d86b81a6795..d229bb28111 100644 --- a/test/e2e/common/BUILD +++ b/test/e2e/common/BUILD @@ -11,6 +11,7 @@ go_library( "apparmor.go", "autoscaling_utils.go", "configmap.go", + "configmap_volume.go", "container_probe.go", "docker_containers.go", "downward_api.go", @@ -26,6 +27,7 @@ go_library( "privileged.go", "projected.go", "secrets.go", + "secrets_volume.go", "sysctl.go", "util.go", "volumes.go", diff --git a/test/e2e/common/configmap.go b/test/e2e/common/configmap.go index 098ef1fbc27..ba1d6713370 100644 --- a/test/e2e/common/configmap.go +++ b/test/e2e/common/configmap.go @@ -18,315 +18,17 @@ package common import ( "fmt" - "os" - "path" . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/test/e2e/framework" ) -var _ = framework.KubeDescribe("ConfigMap", func() { +var _ = Describe("[sig-api-machinery] ConfigMap", func() { f := framework.NewDefaultFramework("configmap") - It("should be consumable from pods in volume [Conformance] [sig-storage]", func() { - doConfigMapE2EWithoutMappings(f, 0, 0, nil) - }) - - It("should be consumable from pods in volume with defaultMode set [Conformance] [sig-storage]", func() { - defaultMode := int32(0400) - doConfigMapE2EWithoutMappings(f, 0, 0, &defaultMode) - }) - - It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Feature:FSGroup] [sig-storage]", func() { - defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */ - doConfigMapE2EWithoutMappings(f, 1000, 1001, &defaultMode) - }) - - It("should be consumable from pods in volume as non-root [Conformance] [sig-storage]", func() { - doConfigMapE2EWithoutMappings(f, 1000, 0, nil) - }) - - It("should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup] [sig-storage]", func() { - doConfigMapE2EWithoutMappings(f, 1000, 1001, nil) - }) - - It("should be consumable from pods in volume with mappings [Conformance] [sig-storage]", func() { - doConfigMapE2EWithMappings(f, 0, 0, nil) - }) - - It("should be consumable from pods in volume with mappings and Item mode set[Conformance] [sig-storage]", func() { - mode := int32(0400) - doConfigMapE2EWithMappings(f, 0, 0, &mode) - }) - - It("should be consumable from pods in volume with mappings as non-root [Conformance] [sig-storage]", func() { - doConfigMapE2EWithMappings(f, 1000, 0, nil) - }) - - It("should be consumable from pods in volume with mappings as non-root with FSGroup [Feature:FSGroup] [sig-storage]", func() { - doConfigMapE2EWithMappings(f, 1000, 1001, nil) - }) - - It("updates should be reflected in volume [Conformance] [sig-storage]", func() { - podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) - containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) - - name := "configmap-test-upd-" + string(uuid.NewUUID()) - volumeName := "configmap-volume" - volumeMountPath := "/etc/configmap-volume" - containerName := "configmap-volume-test" - - configMap := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: name, - }, - Data: map[string]string{ - "data-1": "value-1", - }, - } - - By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) - var err error - if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); 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{ - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: containerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volume/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - By("Creating the pod") - f.PodClient().CreateSync(pod) - - pollLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, containerName) - } - - Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) - - By(fmt.Sprintf("Updating configmap %v", configMap.Name)) - configMap.ResourceVersion = "" // to force update - configMap.Data["data-1"] = "value-2" - _, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Update(configMap) - Expect(err).NotTo(HaveOccurred(), "Failed to update configmap %q in namespace %q", configMap.Name, f.Namespace.Name) - - By("waiting to observe update in volume") - Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-2")) - }) - - It("optional updates should be reflected in volume [Conformance] [sig-storage]", func() { - podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) - containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) - trueVal := true - volumeMountPath := "/etc/configmap-volumes" - - deleteName := "cm-test-opt-del-" + string(uuid.NewUUID()) - deleteContainerName := "delcm-volume-test" - deleteVolumeName := "deletecm-volume" - deleteConfigMap := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: deleteName, - }, - Data: map[string]string{ - "data-1": "value-1", - }, - } - - updateName := "cm-test-opt-upd-" + string(uuid.NewUUID()) - updateContainerName := "updcm-volume-test" - updateVolumeName := "updatecm-volume" - updateConfigMap := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: updateName, - }, - Data: map[string]string{ - "data-1": "value-1", - }, - } - - createName := "cm-test-opt-create-" + string(uuid.NewUUID()) - createContainerName := "createcm-volume-test" - createVolumeName := "createcm-volume" - createConfigMap := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: createName, - }, - Data: map[string]string{ - "data-1": "value-1", - }, - } - - By(fmt.Sprintf("Creating configMap with name %s", deleteConfigMap.Name)) - var err error - if deleteConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(deleteConfigMap); err != nil { - framework.Failf("unable to create test configMap %s: %v", deleteConfigMap.Name, err) - } - - By(fmt.Sprintf("Creating configMap with name %s", updateConfigMap.Name)) - if updateConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(updateConfigMap); err != nil { - framework.Failf("unable to create test configMap %s: %v", updateConfigMap.Name, err) - } - - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-configmaps-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - Volumes: []v1.Volume{ - { - Name: deleteVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: deleteName, - }, - Optional: &trueVal, - }, - }, - }, - { - Name: updateVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: updateName, - }, - Optional: &trueVal, - }, - }, - }, - { - Name: createVolumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: createName, - }, - Optional: &trueVal, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: deleteContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/delete/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: deleteVolumeName, - MountPath: path.Join(volumeMountPath, "delete"), - ReadOnly: true, - }, - }, - }, - { - Name: updateContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/update/data-3"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: updateVolumeName, - MountPath: path.Join(volumeMountPath, "update"), - ReadOnly: true, - }, - }, - }, - { - Name: createContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/create/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: createVolumeName, - MountPath: path.Join(volumeMountPath, "create"), - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - By("Creating the pod") - f.PodClient().CreateSync(pod) - - pollCreateLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName) - } - Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/create/data-1")) - - pollUpdateLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName) - } - Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/update/data-3")) - - pollDeleteLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName) - } - Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) - - By(fmt.Sprintf("Deleting configmap %v", deleteConfigMap.Name)) - err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Delete(deleteConfigMap.Name, &metav1.DeleteOptions{}) - Expect(err).NotTo(HaveOccurred(), "Failed to delete configmap %q in namespace %q", deleteConfigMap.Name, f.Namespace.Name) - - By(fmt.Sprintf("Updating configmap %v", updateConfigMap.Name)) - updateConfigMap.ResourceVersion = "" // to force update - delete(updateConfigMap.Data, "data-1") - updateConfigMap.Data["data-3"] = "value-3" - _, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Update(updateConfigMap) - Expect(err).NotTo(HaveOccurred(), "Failed to update configmap %q in namespace %q", updateConfigMap.Name, f.Namespace.Name) - - By(fmt.Sprintf("Creating configMap with name %s", createConfigMap.Name)) - if createConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(createConfigMap); err != nil { - framework.Failf("unable to create test configMap %s: %v", createConfigMap.Name, err) - } - - By("waiting to observe update in volume") - - Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) - Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3")) - Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/delete/data-1")) - }) - It("should be consumable via environment variable [Conformance]", func() { name := "configmap-test-" + string(uuid.NewUUID()) configMap := newConfigMap(f, name) @@ -409,94 +111,8 @@ var _ = framework.KubeDescribe("ConfigMap", func() { "p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3", }) }) - - It("should be consumable in multiple volumes in the same pod [Conformance] [sig-storage]", func() { - var ( - name = "configmap-test-volume-" + string(uuid.NewUUID()) - volumeName = "configmap-volume" - volumeMountPath = "/etc/configmap-volume" - volumeName2 = "configmap-volume-2" - volumeMountPath2 = "/etc/configmap-volume-2" - configMap = newConfigMap(f, name) - ) - - By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) - var err error - if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); 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{ - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - { - Name: volumeName2, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "configmap-volume-test", - Image: mountImage, - Args: []string{"--file_content=/etc/configmap-volume/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - ReadOnly: true, - }, - { - Name: volumeName2, - MountPath: volumeMountPath2, - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - f.TestContainerOutput("consume configMaps", pod, 0, []string{ - "content of file \"/etc/configmap-volume/data-1\": value-1", - }) - - }) }) -func newConfigMap(f *framework.Framework, name string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: name, - }, - Data: map[string]string{ - "data-1": "value-1", - "data-2": "value-2", - "data-3": "value-3", - }, - } -} - func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap { return &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -510,172 +126,3 @@ func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap { }, } } - -func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64, defaultMode *int32) { - userID := int64(uid) - groupID := int64(fsGroup) - - var ( - name = "configmap-test-volume-" + string(uuid.NewUUID()) - volumeName = "configmap-volume" - volumeMountPath = "/etc/configmap-volume" - configMap = newConfigMap(f, name) - ) - - By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) - var err error - if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { - framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) - } - - one := int64(1) - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-configmaps-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - SecurityContext: &v1.PodSecurityContext{}, - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "configmap-volume-test", - Image: mountImage, - Args: []string{ - "--file_content=/etc/configmap-volume/data-1", - "--file_mode=/etc/configmap-volume/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - TerminationGracePeriodSeconds: &one, - }, - } - - if userID != 0 { - pod.Spec.SecurityContext.RunAsUser = &userID - } - - if groupID != 0 { - pod.Spec.SecurityContext.FSGroup = &groupID - } - - if defaultMode != nil { - pod.Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode = defaultMode - } else { - mode := int32(0644) - defaultMode = &mode - } - - modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode)) - output := []string{ - "content of file \"/etc/configmap-volume/data-1\": value-1", - "mode of file \"/etc/configmap-volume/data-1\": " + modeString, - } - f.TestContainerOutput("consume configMaps", pod, 0, output) -} - -func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64, itemMode *int32) { - userID := int64(uid) - groupID := int64(fsGroup) - - var ( - name = "configmap-test-volume-map-" + string(uuid.NewUUID()) - volumeName = "configmap-volume" - volumeMountPath = "/etc/configmap-volume" - configMap = newConfigMap(f, name) - ) - - By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) - - var err error - if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { - framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) - } - - one := int64(1) - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-configmaps-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - SecurityContext: &v1.PodSecurityContext{}, - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - Items: []v1.KeyToPath{ - { - Key: "data-2", - Path: "path/to/data-2", - }, - }, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "configmap-volume-test", - Image: mountImage, - Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2", - "--file_mode=/etc/configmap-volume/path/to/data-2"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - TerminationGracePeriodSeconds: &one, - }, - } - - if userID != 0 { - pod.Spec.SecurityContext.RunAsUser = &userID - } - - if groupID != 0 { - pod.Spec.SecurityContext.FSGroup = &groupID - } - - if itemMode != nil { - pod.Spec.Volumes[0].VolumeSource.ConfigMap.Items[0].Mode = itemMode - } else { - mode := int32(0644) - itemMode = &mode - } - - // Just check file mode if fsGroup is not set. If fsGroup is set, the - // final mode is adjusted and we are not testing that case. - output := []string{ - "content of file \"/etc/configmap-volume/path/to/data-2\": value-2", - } - if fsGroup == 0 { - modeString := fmt.Sprintf("%v", os.FileMode(*itemMode)) - output = append(output, "mode of file \"/etc/configmap-volume/path/to/data-2\": "+modeString) - } - f.TestContainerOutput("consume configMaps", pod, 0, output) -} diff --git a/test/e2e/common/configmap_volume.go b/test/e2e/common/configmap_volume.go new file mode 100644 index 00000000000..8cca5e5ba7e --- /dev/null +++ b/test/e2e/common/configmap_volume.go @@ -0,0 +1,584 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "fmt" + "os" + "path" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/framework" +) + +var _ = Describe("[sig-storage] ConfigMap", func() { + f := framework.NewDefaultFramework("configmap") + + It("should be consumable from pods in volume [Conformance]", func() { + doConfigMapE2EWithoutMappings(f, 0, 0, nil) + }) + + It("should be consumable from pods in volume with defaultMode set [Conformance]", func() { + defaultMode := int32(0400) + doConfigMapE2EWithoutMappings(f, 0, 0, &defaultMode) + }) + + It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Feature:FSGroup]", func() { + defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */ + doConfigMapE2EWithoutMappings(f, 1000, 1001, &defaultMode) + }) + + It("should be consumable from pods in volume as non-root [Conformance]", func() { + doConfigMapE2EWithoutMappings(f, 1000, 0, nil) + }) + + It("should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]", func() { + doConfigMapE2EWithoutMappings(f, 1000, 1001, nil) + }) + + It("should be consumable from pods in volume with mappings [Conformance]", func() { + doConfigMapE2EWithMappings(f, 0, 0, nil) + }) + + It("should be consumable from pods in volume with mappings and Item mode set[Conformance]", func() { + mode := int32(0400) + doConfigMapE2EWithMappings(f, 0, 0, &mode) + }) + + It("should be consumable from pods in volume with mappings as non-root [Conformance]", func() { + doConfigMapE2EWithMappings(f, 1000, 0, nil) + }) + + It("should be consumable from pods in volume with mappings as non-root with FSGroup [Feature:FSGroup]", func() { + doConfigMapE2EWithMappings(f, 1000, 1001, nil) + }) + + It("updates should be reflected in volume [Conformance]", func() { + podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) + containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) + + name := "configmap-test-upd-" + string(uuid.NewUUID()) + volumeName := "configmap-volume" + volumeMountPath := "/etc/configmap-volume" + containerName := "configmap-volume-test" + + configMap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: name, + }, + Data: map[string]string{ + "data-1": "value-1", + }, + } + + By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) + var err error + if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); 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{ + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: containerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volume/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + By("Creating the pod") + f.PodClient().CreateSync(pod) + + pollLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, containerName) + } + + Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) + + By(fmt.Sprintf("Updating configmap %v", configMap.Name)) + configMap.ResourceVersion = "" // to force update + configMap.Data["data-1"] = "value-2" + _, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Update(configMap) + Expect(err).NotTo(HaveOccurred(), "Failed to update configmap %q in namespace %q", configMap.Name, f.Namespace.Name) + + By("waiting to observe update in volume") + Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-2")) + }) + + It("optional updates should be reflected in volume [Conformance]", func() { + podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) + containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) + trueVal := true + volumeMountPath := "/etc/configmap-volumes" + + deleteName := "cm-test-opt-del-" + string(uuid.NewUUID()) + deleteContainerName := "delcm-volume-test" + deleteVolumeName := "deletecm-volume" + deleteConfigMap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: deleteName, + }, + Data: map[string]string{ + "data-1": "value-1", + }, + } + + updateName := "cm-test-opt-upd-" + string(uuid.NewUUID()) + updateContainerName := "updcm-volume-test" + updateVolumeName := "updatecm-volume" + updateConfigMap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: updateName, + }, + Data: map[string]string{ + "data-1": "value-1", + }, + } + + createName := "cm-test-opt-create-" + string(uuid.NewUUID()) + createContainerName := "createcm-volume-test" + createVolumeName := "createcm-volume" + createConfigMap := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: createName, + }, + Data: map[string]string{ + "data-1": "value-1", + }, + } + + By(fmt.Sprintf("Creating configMap with name %s", deleteConfigMap.Name)) + var err error + if deleteConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(deleteConfigMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", deleteConfigMap.Name, err) + } + + By(fmt.Sprintf("Creating configMap with name %s", updateConfigMap.Name)) + if updateConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(updateConfigMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", updateConfigMap.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-configmaps-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + Name: deleteVolumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: deleteName, + }, + Optional: &trueVal, + }, + }, + }, + { + Name: updateVolumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: updateName, + }, + Optional: &trueVal, + }, + }, + }, + { + Name: createVolumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: createName, + }, + Optional: &trueVal, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: deleteContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/delete/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: deleteVolumeName, + MountPath: path.Join(volumeMountPath, "delete"), + ReadOnly: true, + }, + }, + }, + { + Name: updateContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/update/data-3"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: updateVolumeName, + MountPath: path.Join(volumeMountPath, "update"), + ReadOnly: true, + }, + }, + }, + { + Name: createContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/configmap-volumes/create/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: createVolumeName, + MountPath: path.Join(volumeMountPath, "create"), + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + By("Creating the pod") + f.PodClient().CreateSync(pod) + + pollCreateLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName) + } + Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/create/data-1")) + + pollUpdateLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName) + } + Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/update/data-3")) + + pollDeleteLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName) + } + Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) + + By(fmt.Sprintf("Deleting configmap %v", deleteConfigMap.Name)) + err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Delete(deleteConfigMap.Name, &metav1.DeleteOptions{}) + Expect(err).NotTo(HaveOccurred(), "Failed to delete configmap %q in namespace %q", deleteConfigMap.Name, f.Namespace.Name) + + By(fmt.Sprintf("Updating configmap %v", updateConfigMap.Name)) + updateConfigMap.ResourceVersion = "" // to force update + delete(updateConfigMap.Data, "data-1") + updateConfigMap.Data["data-3"] = "value-3" + _, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Update(updateConfigMap) + Expect(err).NotTo(HaveOccurred(), "Failed to update configmap %q in namespace %q", updateConfigMap.Name, f.Namespace.Name) + + By(fmt.Sprintf("Creating configMap with name %s", createConfigMap.Name)) + if createConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(createConfigMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", createConfigMap.Name, err) + } + + By("waiting to observe update in volume") + + Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) + Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3")) + Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/delete/data-1")) + }) + + It("should be consumable in multiple volumes in the same pod [Conformance]", func() { + var ( + name = "configmap-test-volume-" + string(uuid.NewUUID()) + volumeName = "configmap-volume" + volumeMountPath = "/etc/configmap-volume" + volumeName2 = "configmap-volume-2" + volumeMountPath2 = "/etc/configmap-volume-2" + configMap = newConfigMap(f, name) + ) + + By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) + var err error + if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); 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{ + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + { + Name: volumeName2, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "configmap-volume-test", + Image: mountImage, + Args: []string{"--file_content=/etc/configmap-volume/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + { + Name: volumeName2, + MountPath: volumeMountPath2, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + f.TestContainerOutput("consume configMaps", pod, 0, []string{ + "content of file \"/etc/configmap-volume/data-1\": value-1", + }) + + }) +}) + +func newConfigMap(f *framework.Framework, name string) *v1.ConfigMap { + return &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: name, + }, + Data: map[string]string{ + "data-1": "value-1", + "data-2": "value-2", + "data-3": "value-3", + }, + } +} + +func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64, defaultMode *int32) { + userID := int64(uid) + groupID := int64(fsGroup) + + var ( + name = "configmap-test-volume-" + string(uuid.NewUUID()) + volumeName = "configmap-volume" + volumeMountPath = "/etc/configmap-volume" + configMap = newConfigMap(f, name) + ) + + By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) + var err error + if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + one := int64(1) + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-configmaps-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + SecurityContext: &v1.PodSecurityContext{}, + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "configmap-volume-test", + Image: mountImage, + Args: []string{ + "--file_content=/etc/configmap-volume/data-1", + "--file_mode=/etc/configmap-volume/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + TerminationGracePeriodSeconds: &one, + }, + } + + if userID != 0 { + pod.Spec.SecurityContext.RunAsUser = &userID + } + + if groupID != 0 { + pod.Spec.SecurityContext.FSGroup = &groupID + } + + if defaultMode != nil { + pod.Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode = defaultMode + } else { + mode := int32(0644) + defaultMode = &mode + } + + modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode)) + output := []string{ + "content of file \"/etc/configmap-volume/data-1\": value-1", + "mode of file \"/etc/configmap-volume/data-1\": " + modeString, + } + f.TestContainerOutput("consume configMaps", pod, 0, output) +} + +func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64, itemMode *int32) { + userID := int64(uid) + groupID := int64(fsGroup) + + var ( + name = "configmap-test-volume-map-" + string(uuid.NewUUID()) + volumeName = "configmap-volume" + volumeMountPath = "/etc/configmap-volume" + configMap = newConfigMap(f, name) + ) + + By(fmt.Sprintf("Creating configMap with name %s", configMap.Name)) + + var err error + if configMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + one := int64(1) + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-configmaps-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + SecurityContext: &v1.PodSecurityContext{}, + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: name, + }, + Items: []v1.KeyToPath{ + { + Key: "data-2", + Path: "path/to/data-2", + }, + }, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "configmap-volume-test", + Image: mountImage, + Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2", + "--file_mode=/etc/configmap-volume/path/to/data-2"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + TerminationGracePeriodSeconds: &one, + }, + } + + if userID != 0 { + pod.Spec.SecurityContext.RunAsUser = &userID + } + + if groupID != 0 { + pod.Spec.SecurityContext.FSGroup = &groupID + } + + if itemMode != nil { + pod.Spec.Volumes[0].VolumeSource.ConfigMap.Items[0].Mode = itemMode + } else { + mode := int32(0644) + itemMode = &mode + } + + // Just check file mode if fsGroup is not set. If fsGroup is set, the + // final mode is adjusted and we are not testing that case. + output := []string{ + "content of file \"/etc/configmap-volume/path/to/data-2\": value-2", + } + if fsGroup == 0 { + modeString := fmt.Sprintf("%v", os.FileMode(*itemMode)) + output = append(output, "mode of file \"/etc/configmap-volume/path/to/data-2\": "+modeString) + } + f.TestContainerOutput("consume configMaps", pod, 0, output) +} diff --git a/test/e2e/common/downward_api.go b/test/e2e/common/downward_api.go index 74ada1aa6bd..c3c3bda19b2 100644 --- a/test/e2e/common/downward_api.go +++ b/test/e2e/common/downward_api.go @@ -31,7 +31,7 @@ import ( var hostIPVersion = utilversion.MustParseSemantic("v1.8.0") -var _ = framework.KubeDescribe("Downward API", func() { +var _ = Describe("[sig-api-machinery] Downward API", func() { f := framework.NewDefaultFramework("downward-api") It("should provide pod name and namespace as env vars [Conformance]", func() { diff --git a/test/e2e/common/downwardapi_volume.go b/test/e2e/common/downwardapi_volume.go index 4e0ce16e85a..34bb566eed0 100644 --- a/test/e2e/common/downwardapi_volume.go +++ b/test/e2e/common/downwardapi_volume.go @@ -30,7 +30,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = framework.KubeDescribe("Downward API volume", func() { +var _ = Describe("[sig-storage] Downward API volume", func() { // How long to wait for a log pod to be displayed const podLogTimeout = 2 * time.Minute f := framework.NewDefaultFramework("downward-api") @@ -39,7 +39,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { podClient = f.PodClient() }) - It("should provide podname only [Conformance] [sig-storage]", func() { + It("should provide podname only [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podname") @@ -48,7 +48,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should set DefaultMode on files [Conformance] [sig-storage]", func() { + It("should set DefaultMode on files [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) defaultMode := int32(0400) pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", nil, &defaultMode) @@ -58,7 +58,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should set mode on item file [Conformance] [sig-storage]", func() { + It("should set mode on item file [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) mode := int32(0400) pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil) @@ -68,7 +68,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide podname as non-root with fsgroup [Feature:FSGroup] [sig-storage]", func() { + It("should provide podname as non-root with fsgroup [Feature:FSGroup]", func() { podName := "metadata-volume-" + string(uuid.NewUUID()) uid := int64(1001) gid := int64(1234) @@ -82,7 +82,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide podname as non-root with fsgroup and defaultMode [Feature:FSGroup] [sig-storage]", func() { + It("should provide podname as non-root with fsgroup and defaultMode [Feature:FSGroup]", func() { podName := "metadata-volume-" + string(uuid.NewUUID()) uid := int64(1001) gid := int64(1234) @@ -97,7 +97,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should update labels on modification [Conformance] [sig-storage]", func() { + It("should update labels on modification [Conformance]", func() { labels := map[string]string{} labels["key1"] = "value1" labels["key2"] = "value2" @@ -124,7 +124,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { podLogTimeout, framework.Poll).Should(ContainSubstring("key3=\"value3\"\n")) }) - It("should update annotations on modification [Conformance] [sig-storage]", func() { + It("should update annotations on modification [Conformance]", func() { annotations := map[string]string{} annotations["builder"] = "bar" podName := "annotationupdate" + string(uuid.NewUUID()) @@ -153,7 +153,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { podLogTimeout, framework.Poll).Should(ContainSubstring("builder=\"foo\"\n")) }) - It("should provide container's cpu limit [Conformance] [sig-storage]", func() { + It("should provide container's cpu limit [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_limit") @@ -162,7 +162,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide container's memory limit [Conformance] [sig-storage]", func() { + It("should provide container's memory limit [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_limit") @@ -171,7 +171,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide container's cpu request [Conformance] [sig-storage]", func() { + It("should provide container's cpu request [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_request") @@ -180,7 +180,7 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide container's memory request [Conformance] [sig-storage]", func() { + It("should provide container's memory request [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_request") @@ -189,14 +189,14 @@ var _ = framework.KubeDescribe("Downward API volume", func() { }) }) - It("should provide node allocatable (cpu) as default cpu limit if the limit is not set [Conformance] [sig-storage]", func() { + It("should provide node allocatable (cpu) as default cpu limit if the limit is not set [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/cpu_limit") f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"}) }) - It("should provide node allocatable (memory) as default memory limit if the limit is not set [Conformance] [sig-storage]", func() { + It("should provide node allocatable (memory) as default memory limit if the limit is not set [Conformance]", func() { podName := "downwardapi-volume-" + string(uuid.NewUUID()) pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/memory_limit") diff --git a/test/e2e/common/secrets.go b/test/e2e/common/secrets.go index bceb386a0b5..ebf7b30ea52 100644 --- a/test/e2e/common/secrets.go +++ b/test/e2e/common/secrets.go @@ -18,8 +18,6 @@ package common import ( "fmt" - "os" - "path" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,303 +25,11 @@ import ( "k8s.io/kubernetes/test/e2e/framework" . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" ) -var _ = framework.KubeDescribe("Secrets", func() { +var _ = Describe("[sig-api-machinery] Secrets", func() { f := framework.NewDefaultFramework("secrets") - It("should be consumable from pods in volume [Conformance] [sig-storage]", func() { - doSecretE2EWithoutMapping(f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID()), nil, nil) - }) - - It("should be consumable from pods in volume with defaultMode set [Conformance] [sig-storage]", func() { - defaultMode := int32(0400) - doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), nil, nil) - }) - - It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Conformance] [sig-storage]", func() { - defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */ - fsGroup := int64(1001) - uid := int64(1000) - doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), &fsGroup, &uid) - }) - - It("should be consumable from pods in volume with mappings [Conformance] [sig-storage]", func() { - doSecretE2EWithMapping(f, nil) - }) - - It("should be consumable from pods in volume with mappings and Item Mode set [Conformance] [sig-storage]", func() { - mode := int32(0400) - doSecretE2EWithMapping(f, &mode) - }) - - It("should be able to mount in a volume regardless of a different secret existing with same name in different namespace [sig-storage]", func() { - var ( - namespace2 *v1.Namespace - err error - secret2Name = "secret-test-" + string(uuid.NewUUID()) - ) - - if namespace2, err = f.CreateNamespace("secret-namespace", nil); err != nil { - framework.Failf("unable to create new namespace %s: %v", namespace2.Name, err) - } - - secret2 := secretForTest(namespace2.Name, secret2Name) - secret2.Data = map[string][]byte{ - "this_should_not_match_content_of_other_secret": []byte("similarly_this_should_not_match_content_of_other_secret\n"), - } - if secret2, err = f.ClientSet.Core().Secrets(namespace2.Name).Create(secret2); err != nil { - framework.Failf("unable to create test secret %s: %v", secret2.Name, err) - } - doSecretE2EWithoutMapping(f, nil /* default mode */, secret2.Name, nil, nil) - }) - - It("should be consumable in multiple volumes in a pod [Conformance] [sig-storage]", func() { - // This test ensures that the same secret can be mounted in multiple - // volumes in the same pod. This test case exists to prevent - // regressions that break this use-case. - var ( - name = "secret-test-" + string(uuid.NewUUID()) - volumeName = "secret-volume" - volumeMountPath = "/etc/secret-volume" - volumeName2 = "secret-volume-2" - volumeMountPath2 = "/etc/secret-volume-2" - secret = secretForTest(f.Namespace.Name, name) - ) - - By(fmt.Sprintf("Creating secret with name %s", secret.Name)) - var err error - if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { - framework.Failf("unable to create test secret %s: %v", secret.Name, err) - } - - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-secrets-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: name, - }, - }, - }, - { - Name: volumeName2, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: name, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "secret-volume-test", - Image: mountImage, - Args: []string{ - "--file_content=/etc/secret-volume/data-1", - "--file_mode=/etc/secret-volume/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - ReadOnly: true, - }, - { - Name: volumeName2, - MountPath: volumeMountPath2, - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - f.TestContainerOutput("consume secrets", pod, 0, []string{ - "content of file \"/etc/secret-volume/data-1\": value-1", - "mode of file \"/etc/secret-volume/data-1\": -rw-r--r--", - }) - }) - - It("optional updates should be reflected in volume [Conformance] [sig-storage]", func() { - podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) - containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) - trueVal := true - volumeMountPath := "/etc/secret-volumes" - - deleteName := "s-test-opt-del-" + string(uuid.NewUUID()) - deleteContainerName := "dels-volume-test" - deleteVolumeName := "deletes-volume" - deleteSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: deleteName, - }, - Data: map[string][]byte{ - "data-1": []byte("value-1"), - }, - } - - updateName := "s-test-opt-upd-" + string(uuid.NewUUID()) - updateContainerName := "upds-volume-test" - updateVolumeName := "updates-volume" - updateSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: updateName, - }, - Data: map[string][]byte{ - "data-1": []byte("value-1"), - }, - } - - createName := "s-test-opt-create-" + string(uuid.NewUUID()) - createContainerName := "creates-volume-test" - createVolumeName := "creates-volume" - createSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: f.Namespace.Name, - Name: createName, - }, - Data: map[string][]byte{ - "data-1": []byte("value-1"), - }, - } - - By(fmt.Sprintf("Creating secret with name %s", deleteSecret.Name)) - var err error - if deleteSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(deleteSecret); err != nil { - framework.Failf("unable to create test secret %s: %v", deleteSecret.Name, err) - } - - By(fmt.Sprintf("Creating secret with name %s", updateSecret.Name)) - if updateSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(updateSecret); err != nil { - framework.Failf("unable to create test secret %s: %v", updateSecret.Name, err) - } - - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-secrets-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - Volumes: []v1.Volume{ - { - Name: deleteVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: deleteName, - Optional: &trueVal, - }, - }, - }, - { - Name: updateVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: updateName, - Optional: &trueVal, - }, - }, - }, - { - Name: createVolumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: createName, - Optional: &trueVal, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: deleteContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/delete/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: deleteVolumeName, - MountPath: path.Join(volumeMountPath, "delete"), - ReadOnly: true, - }, - }, - }, - { - Name: updateContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/update/data-3"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: updateVolumeName, - MountPath: path.Join(volumeMountPath, "update"), - ReadOnly: true, - }, - }, - }, - { - Name: createContainerName, - Image: mountImage, - Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: createVolumeName, - MountPath: path.Join(volumeMountPath, "create"), - ReadOnly: true, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - By("Creating the pod") - f.PodClient().CreateSync(pod) - - pollCreateLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName) - } - Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/create/data-1")) - - pollUpdateLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName) - } - Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/update/data-3")) - - pollDeleteLogs := func() (string, error) { - return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName) - } - Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) - - By(fmt.Sprintf("Deleting secret %v", deleteSecret.Name)) - err = f.ClientSet.Core().Secrets(f.Namespace.Name).Delete(deleteSecret.Name, &metav1.DeleteOptions{}) - Expect(err).NotTo(HaveOccurred(), "Failed to delete secret %q in namespace %q", deleteSecret.Name, f.Namespace.Name) - - By(fmt.Sprintf("Updating secret %v", updateSecret.Name)) - updateSecret.ResourceVersion = "" // to force update - delete(updateSecret.Data, "data-1") - updateSecret.Data["data-3"] = []byte("value-3") - _, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Update(updateSecret) - Expect(err).NotTo(HaveOccurred(), "Failed to update secret %q in namespace %q", updateSecret.Name, f.Namespace.Name) - - By(fmt.Sprintf("Creating secret with name %s", createSecret.Name)) - if createSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(createSecret); err != nil { - framework.Failf("unable to create test secret %s: %v", createSecret.Name, err) - } - - By("waiting to observe update in volume") - - Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) - Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3")) - Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1")) - }) - It("should be consumable from pods in env vars [Conformance]", func() { name := "secret-test-" + string(uuid.NewUUID()) secret := secretForTest(f.Namespace.Name, name) @@ -422,159 +128,3 @@ func newEnvFromSecret(namespace, name string) *v1.Secret { }, } } - -func secretForTest(namespace, name string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: map[string][]byte{ - "data-1": []byte("value-1\n"), - "data-2": []byte("value-2\n"), - "data-3": []byte("value-3\n"), - }, - } -} - -func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secretName string, - fsGroup *int64, uid *int64) { - var ( - volumeName = "secret-volume" - volumeMountPath = "/etc/secret-volume" - secret = secretForTest(f.Namespace.Name, secretName) - ) - - By(fmt.Sprintf("Creating secret with name %s", secret.Name)) - var err error - if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { - framework.Failf("unable to create test secret %s: %v", secret.Name, err) - } - - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-secrets-" + string(uuid.NewUUID()), - Namespace: f.Namespace.Name, - }, - Spec: v1.PodSpec{ - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: secretName, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "secret-volume-test", - Image: mountImage, - Args: []string{ - "--file_content=/etc/secret-volume/data-1", - "--file_mode=/etc/secret-volume/data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - if defaultMode != nil { - pod.Spec.Volumes[0].VolumeSource.Secret.DefaultMode = defaultMode - } else { - mode := int32(0644) - defaultMode = &mode - } - - if fsGroup != nil || uid != nil { - pod.Spec.SecurityContext = &v1.PodSecurityContext{ - FSGroup: fsGroup, - RunAsUser: uid, - } - } - - modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode)) - expectedOutput := []string{ - "content of file \"/etc/secret-volume/data-1\": value-1", - "mode of file \"/etc/secret-volume/data-1\": " + modeString, - } - - f.TestContainerOutput("consume secrets", pod, 0, expectedOutput) -} - -func doSecretE2EWithMapping(f *framework.Framework, mode *int32) { - var ( - name = "secret-test-map-" + string(uuid.NewUUID()) - volumeName = "secret-volume" - volumeMountPath = "/etc/secret-volume" - secret = secretForTest(f.Namespace.Name, name) - ) - - By(fmt.Sprintf("Creating secret with name %s", secret.Name)) - var err error - if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { - framework.Failf("unable to create test secret %s: %v", secret.Name, err) - } - - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod-secrets-" + string(uuid.NewUUID()), - }, - Spec: v1.PodSpec{ - Volumes: []v1.Volume{ - { - Name: volumeName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: name, - Items: []v1.KeyToPath{ - { - Key: "data-1", - Path: "new-path-data-1", - }, - }, - }, - }, - }, - }, - Containers: []v1.Container{ - { - Name: "secret-volume-test", - Image: mountImage, - Args: []string{ - "--file_content=/etc/secret-volume/new-path-data-1", - "--file_mode=/etc/secret-volume/new-path-data-1"}, - VolumeMounts: []v1.VolumeMount{ - { - Name: volumeName, - MountPath: volumeMountPath, - }, - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - if mode != nil { - pod.Spec.Volumes[0].VolumeSource.Secret.Items[0].Mode = mode - } else { - defaultItemMode := int32(0644) - mode = &defaultItemMode - } - - modeString := fmt.Sprintf("%v", os.FileMode(*mode)) - expectedOutput := []string{ - "content of file \"/etc/secret-volume/new-path-data-1\": value-1", - "mode of file \"/etc/secret-volume/new-path-data-1\": " + modeString, - } - - f.TestContainerOutput("consume secrets", pod, 0, expectedOutput) -} diff --git a/test/e2e/common/secrets_volume.go b/test/e2e/common/secrets_volume.go new file mode 100644 index 00000000000..494f6c31fa3 --- /dev/null +++ b/test/e2e/common/secrets_volume.go @@ -0,0 +1,482 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "fmt" + "os" + "path" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/framework" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("[sig-storage] Secrets", func() { + f := framework.NewDefaultFramework("secrets") + + It("should be consumable from pods in volume [Conformance]", func() { + doSecretE2EWithoutMapping(f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID()), nil, nil) + }) + + It("should be consumable from pods in volume with defaultMode set [Conformance]", func() { + defaultMode := int32(0400) + doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), nil, nil) + }) + + It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Conformance]", func() { + defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */ + fsGroup := int64(1001) + uid := int64(1000) + doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), &fsGroup, &uid) + }) + + It("should be consumable from pods in volume with mappings [Conformance]", func() { + doSecretE2EWithMapping(f, nil) + }) + + It("should be consumable from pods in volume with mappings and Item Mode set [Conformance]", func() { + mode := int32(0400) + doSecretE2EWithMapping(f, &mode) + }) + + It("should be able to mount in a volume regardless of a different secret existing with same name in different namespace", func() { + var ( + namespace2 *v1.Namespace + err error + secret2Name = "secret-test-" + string(uuid.NewUUID()) + ) + + if namespace2, err = f.CreateNamespace("secret-namespace", nil); err != nil { + framework.Failf("unable to create new namespace %s: %v", namespace2.Name, err) + } + + secret2 := secretForTest(namespace2.Name, secret2Name) + secret2.Data = map[string][]byte{ + "this_should_not_match_content_of_other_secret": []byte("similarly_this_should_not_match_content_of_other_secret\n"), + } + if secret2, err = f.ClientSet.Core().Secrets(namespace2.Name).Create(secret2); err != nil { + framework.Failf("unable to create test secret %s: %v", secret2.Name, err) + } + doSecretE2EWithoutMapping(f, nil /* default mode */, secret2.Name, nil, nil) + }) + + It("should be consumable in multiple volumes in a pod [Conformance]", func() { + // This test ensures that the same secret can be mounted in multiple + // volumes in the same pod. This test case exists to prevent + // regressions that break this use-case. + var ( + name = "secret-test-" + string(uuid.NewUUID()) + volumeName = "secret-volume" + volumeMountPath = "/etc/secret-volume" + volumeName2 = "secret-volume-2" + volumeMountPath2 = "/etc/secret-volume-2" + secret = secretForTest(f.Namespace.Name, name) + ) + + By(fmt.Sprintf("Creating secret with name %s", secret.Name)) + var err error + if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { + framework.Failf("unable to create test secret %s: %v", secret.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-secrets-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: name, + }, + }, + }, + { + Name: volumeName2, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: name, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "secret-volume-test", + Image: mountImage, + Args: []string{ + "--file_content=/etc/secret-volume/data-1", + "--file_mode=/etc/secret-volume/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + { + Name: volumeName2, + MountPath: volumeMountPath2, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + f.TestContainerOutput("consume secrets", pod, 0, []string{ + "content of file \"/etc/secret-volume/data-1\": value-1", + "mode of file \"/etc/secret-volume/data-1\": -rw-r--r--", + }) + }) + + It("optional updates should be reflected in volume [Conformance]", func() { + podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet) + containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) + trueVal := true + volumeMountPath := "/etc/secret-volumes" + + deleteName := "s-test-opt-del-" + string(uuid.NewUUID()) + deleteContainerName := "dels-volume-test" + deleteVolumeName := "deletes-volume" + deleteSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: deleteName, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1"), + }, + } + + updateName := "s-test-opt-upd-" + string(uuid.NewUUID()) + updateContainerName := "upds-volume-test" + updateVolumeName := "updates-volume" + updateSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: updateName, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1"), + }, + } + + createName := "s-test-opt-create-" + string(uuid.NewUUID()) + createContainerName := "creates-volume-test" + createVolumeName := "creates-volume" + createSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: createName, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1"), + }, + } + + By(fmt.Sprintf("Creating secret with name %s", deleteSecret.Name)) + var err error + if deleteSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(deleteSecret); err != nil { + framework.Failf("unable to create test secret %s: %v", deleteSecret.Name, err) + } + + By(fmt.Sprintf("Creating secret with name %s", updateSecret.Name)) + if updateSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(updateSecret); err != nil { + framework.Failf("unable to create test secret %s: %v", updateSecret.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-secrets-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + Name: deleteVolumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: deleteName, + Optional: &trueVal, + }, + }, + }, + { + Name: updateVolumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: updateName, + Optional: &trueVal, + }, + }, + }, + { + Name: createVolumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: createName, + Optional: &trueVal, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: deleteContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/delete/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: deleteVolumeName, + MountPath: path.Join(volumeMountPath, "delete"), + ReadOnly: true, + }, + }, + }, + { + Name: updateContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/update/data-3"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: updateVolumeName, + MountPath: path.Join(volumeMountPath, "update"), + ReadOnly: true, + }, + }, + }, + { + Name: createContainerName, + Image: mountImage, + Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: createVolumeName, + MountPath: path.Join(volumeMountPath, "create"), + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + By("Creating the pod") + f.PodClient().CreateSync(pod) + + pollCreateLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName) + } + Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/create/data-1")) + + pollUpdateLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName) + } + Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/update/data-3")) + + pollDeleteLogs := func() (string, error) { + return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName) + } + Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) + + By(fmt.Sprintf("Deleting secret %v", deleteSecret.Name)) + err = f.ClientSet.Core().Secrets(f.Namespace.Name).Delete(deleteSecret.Name, &metav1.DeleteOptions{}) + Expect(err).NotTo(HaveOccurred(), "Failed to delete secret %q in namespace %q", deleteSecret.Name, f.Namespace.Name) + + By(fmt.Sprintf("Updating secret %v", updateSecret.Name)) + updateSecret.ResourceVersion = "" // to force update + delete(updateSecret.Data, "data-1") + updateSecret.Data["data-3"] = []byte("value-3") + _, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Update(updateSecret) + Expect(err).NotTo(HaveOccurred(), "Failed to update secret %q in namespace %q", updateSecret.Name, f.Namespace.Name) + + By(fmt.Sprintf("Creating secret with name %s", createSecret.Name)) + if createSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(createSecret); err != nil { + framework.Failf("unable to create test secret %s: %v", createSecret.Name, err) + } + + By("waiting to observe update in volume") + + Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1")) + Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3")) + Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1")) + }) +}) + +func secretForTest(namespace, name string) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1\n"), + "data-2": []byte("value-2\n"), + "data-3": []byte("value-3\n"), + }, + } +} + +func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secretName string, + fsGroup *int64, uid *int64) { + var ( + volumeName = "secret-volume" + volumeMountPath = "/etc/secret-volume" + secret = secretForTest(f.Namespace.Name, secretName) + ) + + By(fmt.Sprintf("Creating secret with name %s", secret.Name)) + var err error + if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { + framework.Failf("unable to create test secret %s: %v", secret.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-secrets-" + string(uuid.NewUUID()), + Namespace: f.Namespace.Name, + }, + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: secretName, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "secret-volume-test", + Image: mountImage, + Args: []string{ + "--file_content=/etc/secret-volume/data-1", + "--file_mode=/etc/secret-volume/data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + if defaultMode != nil { + pod.Spec.Volumes[0].VolumeSource.Secret.DefaultMode = defaultMode + } else { + mode := int32(0644) + defaultMode = &mode + } + + if fsGroup != nil || uid != nil { + pod.Spec.SecurityContext = &v1.PodSecurityContext{ + FSGroup: fsGroup, + RunAsUser: uid, + } + } + + modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode)) + expectedOutput := []string{ + "content of file \"/etc/secret-volume/data-1\": value-1", + "mode of file \"/etc/secret-volume/data-1\": " + modeString, + } + + f.TestContainerOutput("consume secrets", pod, 0, expectedOutput) +} + +func doSecretE2EWithMapping(f *framework.Framework, mode *int32) { + var ( + name = "secret-test-map-" + string(uuid.NewUUID()) + volumeName = "secret-volume" + volumeMountPath = "/etc/secret-volume" + secret = secretForTest(f.Namespace.Name, name) + ) + + By(fmt.Sprintf("Creating secret with name %s", secret.Name)) + var err error + if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil { + framework.Failf("unable to create test secret %s: %v", secret.Name, err) + } + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-secrets-" + string(uuid.NewUUID()), + }, + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + Name: volumeName, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: name, + Items: []v1.KeyToPath{ + { + Key: "data-1", + Path: "new-path-data-1", + }, + }, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "secret-volume-test", + Image: mountImage, + Args: []string{ + "--file_content=/etc/secret-volume/new-path-data-1", + "--file_mode=/etc/secret-volume/new-path-data-1"}, + VolumeMounts: []v1.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + } + + if mode != nil { + pod.Spec.Volumes[0].VolumeSource.Secret.Items[0].Mode = mode + } else { + defaultItemMode := int32(0644) + mode = &defaultItemMode + } + + modeString := fmt.Sprintf("%v", os.FileMode(*mode)) + expectedOutput := []string{ + "content of file \"/etc/secret-volume/new-path-data-1\": value-1", + "mode of file \"/etc/secret-volume/new-path-data-1\": " + modeString, + } + + f.TestContainerOutput("consume secrets", pod, 0, expectedOutput) +}