diff --git a/hack/verify-flags/exceptions.txt b/hack/verify-flags/exceptions.txt index d449dc908a3..460fbe5f679 100644 --- a/hack/verify-flags/exceptions.txt +++ b/hack/verify-flags/exceptions.txt @@ -96,6 +96,7 @@ test/e2e/host_path.go: fmt.Sprintf("--file_content_in_loop=%v", filePath), test/e2e/host_path.go: fmt.Sprintf("--file_content_in_loop=%v", filePathInReader), test/e2e/host_path.go: fmt.Sprintf("--retry_time=%d", retryDuration), test/e2e/host_path.go: fmt.Sprintf("--retry_time=%d", retryDuration), +test/e2e_node/configmap.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"}, test/images/mount-tester/mt.go: flag.BoolVar(&breakOnExpectedContent, "break_on_expected_content", true, "Break out of loop on expected content, (use with --file_content_in_loop flag only)") test/images/mount-tester/mt.go: flag.IntVar(&retryDuration, "retry_time", 180, "Retry time during the loop") test/images/mount-tester/mt.go: flag.StringVar(&readFileContentInLoopPath, "file_content_in_loop", "", "Path to read the file content in loop from") diff --git a/test/e2e_node/configmap.go b/test/e2e_node/configmap.go new file mode 100644 index 00000000000..4e6c40ec7f7 --- /dev/null +++ b/test/e2e_node/configmap.go @@ -0,0 +1,346 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 e2e_node + +import ( + "fmt" + "time" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/test/e2e/framework" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = framework.KubeDescribe("ConfigMap", func() { + + f := NewDefaultFramework("configmap") + + It("should be consumable from pods in volumpe [Conformance]", func() { + doConfigMapE2EWithoutMappings(f, 0, 0) + }) + + It("should be consumable from pods in volume as non-root [Conformance]", func() { + doConfigMapE2EWithoutMappings(f, 1000, 0) + }) + + It("should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]", func() { + doConfigMapE2EWithoutMappings(f, 1000, 1001) + }) + + It("should be consumable from pods in volume with mappings [Conformance]", func() { + doConfigMapE2EWithMappings(f, 0, 0) + }) + + It("should be consumable from pods in volume with mappings as non-root [Conformance]", func() { + doConfigMapE2EWithMappings(f, 1000, 0) + }) + + It("should be consumable from pods in volume with mappings as non-root with FSGroup [Feature:FSGroup]", func() { + doConfigMapE2EWithMappings(f, 1000, 1001) + }) + + It("updates should be reflected in volume [Conformance]", func() { + + // We may have to wait or a full sync period to elapse before the + // Kubelet projects the update into the volume and the container picks + // it up. This timeout is based on the default Kubelet sync period (1 + // minute) plus additional time for fudge factor. + const podLogTimeout = 300 * time.Second + + name := "configmap-test-upd-" + string(util.NewUUID()) + volumeName := "configmap-volume" + volumeMountPath := "/etc/configmap-volume" + containerName := "configmap-volume-test" + + configMap := &api.ConfigMap{ + ObjectMeta: api.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.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-configmaps-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + Volumes: []api.Volume{ + { + Name: volumeName, + VolumeSource: api.VolumeSource{ + ConfigMap: &api.ConfigMapVolumeSource{ + LocalObjectReference: api.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + }, + Containers: []api.Container{ + { + Name: containerName, + Image: "gcr.io/google_containers/mounttest:0.6", + Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"}, + VolumeMounts: []api.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: api.RestartPolicyNever, + }, + } + + assignPodToNode(pod) + + By("Creating the pod") + _, err = f.Client.Pods(f.Namespace.Name).Create(pod) + Expect(err).NotTo(HaveOccurred()) + + framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.Client, pod.Name, f.Namespace.Name)) + + pollLogs := func() (string, error) { + return framework.GetPodLogs(f.Client, 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.Client.ConfigMaps(f.Namespace.Name).Update(configMap) + Expect(err).NotTo(HaveOccurred()) + + By("waiting to observe update in volume") + Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-2")) + }) + + It("should be consumable via environment variable [Conformance]", func() { + name := "configmap-test-" + string(util.NewUUID()) + configMap := newConfigMap(f, name) + By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name)) + + var err error + if configMap, err = f.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-configmaps-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "env-test", + Image: "gcr.io/google_containers/busybox:1.24", + Command: []string{"sh", "-c", "env"}, + Env: []api.EnvVar{ + { + Name: "CONFIG_DATA_1", + ValueFrom: &api.EnvVarSource{ + ConfigMapKeyRef: &api.ConfigMapKeySelector{ + LocalObjectReference: api.LocalObjectReference{ + Name: name, + }, + Key: "data-1", + }, + }, + }, + }, + }, + }, + RestartPolicy: api.RestartPolicyNever, + }, + } + + assignPodToNode(pod) + + framework.TestContainerOutput("consume configMaps", f.Client, pod, 0, []string{ + "CONFIG_DATA_1=value-1", + }, f.Namespace.Name) + }) +}) + +func newConfigMap(f *framework.Framework, name string) *api.ConfigMap { + return &api.ConfigMap{ + ObjectMeta: api.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) { + var ( + name = "configmap-test-volume-" + string(util.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.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-configmaps-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + SecurityContext: &api.PodSecurityContext{}, + Volumes: []api.Volume{ + { + Name: volumeName, + VolumeSource: api.VolumeSource{ + ConfigMap: &api.ConfigMapVolumeSource{ + LocalObjectReference: api.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + }, + Containers: []api.Container{ + { + Name: "configmap-volume-test", + Image: "gcr.io/google_containers/mounttest:0.6", + Args: []string{"--file_content=/etc/configmap-volume/data-1"}, + VolumeMounts: []api.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: api.RestartPolicyNever, + }, + } + + if uid != 0 { + pod.Spec.SecurityContext.RunAsUser = &uid + } + + if fsGroup != 0 { + pod.Spec.SecurityContext.FSGroup = &fsGroup + } + + assignPodToNode(pod) + + framework.TestContainerOutput("consume configMaps", f.Client, pod, 0, []string{ + "content of file \"/etc/configmap-volume/data-1\": value-1", + }, f.Namespace.Name) + +} + +func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) { + var ( + name = "configmap-test-volume-map-" + string(util.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.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { + framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) + } + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-configmaps-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + SecurityContext: &api.PodSecurityContext{}, + Volumes: []api.Volume{ + { + Name: volumeName, + VolumeSource: api.VolumeSource{ + ConfigMap: &api.ConfigMapVolumeSource{ + LocalObjectReference: api.LocalObjectReference{ + Name: name, + }, + Items: []api.KeyToPath{ + { + Key: "data-2", + Path: "path/to/data-2", + }, + }, + }, + }, + }, + }, + Containers: []api.Container{ + { + Name: "configmap-volume-test", + Image: "gcr.io/google_containers/mounttest:0.6", + Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"}, + VolumeMounts: []api.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + }, + }, + }, + RestartPolicy: api.RestartPolicyNever, + }, + } + + if uid != 0 { + pod.Spec.SecurityContext.RunAsUser = &uid + } + + if fsGroup != 0 { + pod.Spec.SecurityContext.FSGroup = &fsGroup + } + + assignPodToNode(pod) + + framework.TestContainerOutput("consume configMaps", f.Client, pod, 0, []string{ + "content of file \"/etc/configmap-volume/path/to/data-2\": value-2", + }, f.Namespace.Name) +}