...

Source file src/k8s.io/kubernetes/test/e2e/common/storage/projected_configmap.go

Documentation: k8s.io/kubernetes/test/e2e/common/storage

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"path"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/util/uuid"
    27  	"k8s.io/kubernetes/test/e2e/framework"
    28  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    29  	e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
    30  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    31  	"k8s.io/kubernetes/test/e2e/nodefeature"
    32  	imageutils "k8s.io/kubernetes/test/utils/image"
    33  	admissionapi "k8s.io/pod-security-admission/api"
    34  
    35  	"github.com/onsi/ginkgo/v2"
    36  	"github.com/onsi/gomega"
    37  )
    38  
    39  var _ = SIGDescribe("Projected configMap", func() {
    40  	f := framework.NewDefaultFramework("projected")
    41  	f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
    42  
    43  	/*
    44  	   Release: v1.9
    45  	   Testname: Projected Volume, ConfigMap, volume mode default
    46  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap with default permission mode. Pod MUST be able to read the content of the ConfigMap successfully and the mode on the volume MUST be -rw-r--r--.
    47  	*/
    48  	framework.ConformanceIt("should be consumable from pods in volume", f.WithNodeConformance(), func(ctx context.Context) {
    49  		doProjectedConfigMapE2EWithoutMappings(ctx, f, false, 0, nil)
    50  	})
    51  
    52  	/*
    53  	   Release: v1.9
    54  	   Testname: Projected Volume, ConfigMap, volume mode 0400
    55  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap with permission mode set to 0400. Pod MUST be able to read the content of the ConfigMap successfully and the mode on the volume MUST be -r--------.
    56  	   This test is marked LinuxOnly since Windows does not support setting specific file permissions.
    57  	*/
    58  	framework.ConformanceIt("should be consumable from pods in volume with defaultMode set [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) {
    59  		defaultMode := int32(0400)
    60  		doProjectedConfigMapE2EWithoutMappings(ctx, f, false, 0, &defaultMode)
    61  	})
    62  
    63  	f.It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [LinuxOnly]", nodefeature.FSGroup, func(ctx context.Context) {
    64  		// Windows does not support RunAsUser / FSGroup SecurityContext options, and it does not support setting file permissions.
    65  		e2eskipper.SkipIfNodeOSDistroIs("windows")
    66  		defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */
    67  		doProjectedConfigMapE2EWithoutMappings(ctx, f, true, 1001, &defaultMode)
    68  	})
    69  
    70  	/*
    71  	   Release: v1.9
    72  	   Testname: Projected Volume, ConfigMap, non-root user
    73  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap as non-root user with uid 1000. Pod MUST be able to read the content of the ConfigMap successfully and the mode on the volume MUST be -rw-r--r--.
    74  	*/
    75  	framework.ConformanceIt("should be consumable from pods in volume as non-root", f.WithNodeConformance(), func(ctx context.Context) {
    76  		doProjectedConfigMapE2EWithoutMappings(ctx, f, true, 0, nil)
    77  	})
    78  
    79  	f.It("should be consumable from pods in volume as non-root with FSGroup [LinuxOnly]", nodefeature.FSGroup, func(ctx context.Context) {
    80  		// Windows does not support RunAsUser / FSGroup SecurityContext options.
    81  		e2eskipper.SkipIfNodeOSDistroIs("windows")
    82  		doProjectedConfigMapE2EWithoutMappings(ctx, f, true, 1001, nil)
    83  	})
    84  
    85  	/*
    86  	   Release: v1.9
    87  	   Testname: Projected Volume, ConfigMap, mapped
    88  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap with default permission mode. The ConfigMap is also mapped to a custom path. Pod MUST be able to read the content of the ConfigMap from the custom location successfully and the mode on the volume MUST be -rw-r--r--.
    89  	*/
    90  	framework.ConformanceIt("should be consumable from pods in volume with mappings", f.WithNodeConformance(), func(ctx context.Context) {
    91  		doProjectedConfigMapE2EWithMappings(ctx, f, false, 0, nil)
    92  	})
    93  
    94  	/*
    95  	   Release: v1.9
    96  	   Testname: Projected Volume, ConfigMap, mapped, volume mode 0400
    97  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap with permission mode set to 0400. The ConfigMap is also mapped to a custom path. Pod MUST be able to read the content of the ConfigMap from the custom location successfully and the mode on the volume MUST be -r--r--r--.
    98  	   This test is marked LinuxOnly since Windows does not support setting specific file permissions.
    99  	*/
   100  	framework.ConformanceIt("should be consumable from pods in volume with mappings and Item mode set [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) {
   101  		mode := int32(0400)
   102  		doProjectedConfigMapE2EWithMappings(ctx, f, false, 0, &mode)
   103  	})
   104  
   105  	/*
   106  	   Release: v1.9
   107  	   Testname: Projected Volume, ConfigMap, mapped, non-root user
   108  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap as non-root user with uid 1000. The ConfigMap is also mapped to a custom path. Pod MUST be able to read the content of the ConfigMap from the custom location successfully and the mode on the volume MUST be -r--r--r--.
   109  	*/
   110  	framework.ConformanceIt("should be consumable from pods in volume with mappings as non-root", f.WithNodeConformance(), func(ctx context.Context) {
   111  		doProjectedConfigMapE2EWithMappings(ctx, f, true, 0, nil)
   112  	})
   113  
   114  	f.It("should be consumable from pods in volume with mappings as non-root with FSGroup [LinuxOnly]", nodefeature.FSGroup, func(ctx context.Context) {
   115  		// Windows does not support RunAsUser / FSGroup SecurityContext options.
   116  		e2eskipper.SkipIfNodeOSDistroIs("windows")
   117  		doProjectedConfigMapE2EWithMappings(ctx, f, true, 1001, nil)
   118  	})
   119  
   120  	/*
   121  	   Release: v1.9
   122  	   Testname: Projected Volume, ConfigMap, update
   123  	   Description: A Pod is created with projected volume source 'ConfigMap' to store a configMap and performs a create and update to new value. Pod MUST be able to create the configMap with value-1. Pod MUST be able to update the value in the confgiMap to value-2.
   124  	*/
   125  	framework.ConformanceIt("updates should be reflected in volume", f.WithNodeConformance(), func(ctx context.Context) {
   126  		podLogTimeout := e2epod.GetPodSecretUpdateTimeout(ctx, f.ClientSet)
   127  		containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds()))
   128  
   129  		name := "projected-configmap-test-upd-" + string(uuid.NewUUID())
   130  		volumeName := "projected-configmap-volume"
   131  		volumeMountPath := "/etc/projected-configmap-volume"
   132  		configMap := &v1.ConfigMap{
   133  			ObjectMeta: metav1.ObjectMeta{
   134  				Namespace: f.Namespace.Name,
   135  				Name:      name,
   136  			},
   137  			Data: map[string]string{
   138  				"data-1": "value-1",
   139  			},
   140  		}
   141  
   142  		ginkgo.By(fmt.Sprintf("Creating projection with configMap that has name %s", configMap.Name))
   143  		var err error
   144  		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
   145  			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
   146  		}
   147  
   148  		pod := createProjectedConfigMapMounttestPod(f.Namespace.Name, volumeName, name, volumeMountPath,
   149  			"--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/projected-configmap-volume/data-1")
   150  
   151  		ginkgo.By("Creating the pod")
   152  		e2epod.NewPodClient(f).CreateSync(ctx, pod)
   153  
   154  		pollLogs := func() (string, error) {
   155  			return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
   156  		}
   157  
   158  		gomega.Eventually(ctx, pollLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1"))
   159  
   160  		ginkgo.By(fmt.Sprintf("Updating configmap %v", configMap.Name))
   161  		configMap.ResourceVersion = "" // to force update
   162  		configMap.Data["data-1"] = "value-2"
   163  		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(ctx, configMap, metav1.UpdateOptions{})
   164  		framework.ExpectNoError(err, "Failed to update configmap %q in namespace %q", configMap.Name, f.Namespace.Name)
   165  
   166  		ginkgo.By("waiting to observe update in volume")
   167  		gomega.Eventually(ctx, pollLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-2"))
   168  	})
   169  
   170  	/*
   171  	   Release: v1.9
   172  	   Testname: Projected Volume, ConfigMap, create, update and delete
   173  	   Description: Create a Pod with three containers with ConfigMaps namely a create, update and delete container. Create Container when started MUST not have configMap, update and delete containers MUST be created with a ConfigMap value as 'value-1'. Create a configMap in the create container, the Pod MUST be able to read the configMap from the create container. Update the configMap in the update container, Pod MUST be able to read the updated configMap value. Delete the configMap in the delete container. Pod MUST fail to read the configMap from the delete container.
   174  	*/
   175  	framework.ConformanceIt("optional updates should be reflected in volume", f.WithNodeConformance(), func(ctx context.Context) {
   176  		podLogTimeout := e2epod.GetPodSecretUpdateTimeout(ctx, f.ClientSet)
   177  		containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds()))
   178  		trueVal := true
   179  		volumeMountPath := "/etc/projected-configmap-volumes"
   180  
   181  		deleteName := "cm-test-opt-del-" + string(uuid.NewUUID())
   182  		deleteContainerName := "delcm-volume-test"
   183  		deleteVolumeName := "deletecm-volume"
   184  		deleteConfigMap := &v1.ConfigMap{
   185  			ObjectMeta: metav1.ObjectMeta{
   186  				Namespace: f.Namespace.Name,
   187  				Name:      deleteName,
   188  			},
   189  			Data: map[string]string{
   190  				"data-1": "value-1",
   191  			},
   192  		}
   193  
   194  		updateName := "cm-test-opt-upd-" + string(uuid.NewUUID())
   195  		updateContainerName := "updcm-volume-test"
   196  		updateVolumeName := "updatecm-volume"
   197  		updateConfigMap := &v1.ConfigMap{
   198  			ObjectMeta: metav1.ObjectMeta{
   199  				Namespace: f.Namespace.Name,
   200  				Name:      updateName,
   201  			},
   202  			Data: map[string]string{
   203  				"data-1": "value-1",
   204  			},
   205  		}
   206  
   207  		createName := "cm-test-opt-create-" + string(uuid.NewUUID())
   208  		createContainerName := "createcm-volume-test"
   209  		createVolumeName := "createcm-volume"
   210  		createConfigMap := &v1.ConfigMap{
   211  			ObjectMeta: metav1.ObjectMeta{
   212  				Namespace: f.Namespace.Name,
   213  				Name:      createName,
   214  			},
   215  			Data: map[string]string{
   216  				"data-1": "value-1",
   217  			},
   218  		}
   219  
   220  		ginkgo.By(fmt.Sprintf("Creating configMap with name %s", deleteConfigMap.Name))
   221  		var err error
   222  		if deleteConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, deleteConfigMap, metav1.CreateOptions{}); err != nil {
   223  			framework.Failf("unable to create test configMap %s: %v", deleteConfigMap.Name, err)
   224  		}
   225  
   226  		ginkgo.By(fmt.Sprintf("Creating configMap with name %s", updateConfigMap.Name))
   227  		if updateConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, updateConfigMap, metav1.CreateOptions{}); err != nil {
   228  			framework.Failf("unable to create test configMap %s: %v", updateConfigMap.Name, err)
   229  		}
   230  
   231  		pod := &v1.Pod{
   232  			ObjectMeta: metav1.ObjectMeta{
   233  				Name: "pod-projected-configmaps-" + string(uuid.NewUUID()),
   234  			},
   235  			Spec: v1.PodSpec{
   236  				Volumes: []v1.Volume{
   237  					{
   238  						Name: deleteVolumeName,
   239  						VolumeSource: v1.VolumeSource{
   240  							Projected: &v1.ProjectedVolumeSource{
   241  								Sources: []v1.VolumeProjection{
   242  									{
   243  										ConfigMap: &v1.ConfigMapProjection{
   244  											LocalObjectReference: v1.LocalObjectReference{
   245  												Name: deleteName,
   246  											},
   247  											Optional: &trueVal,
   248  										},
   249  									},
   250  								},
   251  							},
   252  						},
   253  					},
   254  					{
   255  						Name: updateVolumeName,
   256  						VolumeSource: v1.VolumeSource{
   257  							Projected: &v1.ProjectedVolumeSource{
   258  								Sources: []v1.VolumeProjection{
   259  									{
   260  										ConfigMap: &v1.ConfigMapProjection{
   261  											LocalObjectReference: v1.LocalObjectReference{
   262  												Name: updateName,
   263  											},
   264  											Optional: &trueVal,
   265  										},
   266  									},
   267  								},
   268  							},
   269  						},
   270  					},
   271  					{
   272  						Name: createVolumeName,
   273  						VolumeSource: v1.VolumeSource{
   274  							Projected: &v1.ProjectedVolumeSource{
   275  								Sources: []v1.VolumeProjection{
   276  									{
   277  										ConfigMap: &v1.ConfigMapProjection{
   278  											LocalObjectReference: v1.LocalObjectReference{
   279  												Name: createName,
   280  											},
   281  											Optional: &trueVal,
   282  										},
   283  									},
   284  								},
   285  							},
   286  						},
   287  					},
   288  				},
   289  				Containers: []v1.Container{
   290  					{
   291  						Name:  deleteContainerName,
   292  						Image: imageutils.GetE2EImage(imageutils.Agnhost),
   293  						Args:  []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/projected-configmap-volumes/delete/data-1"},
   294  						VolumeMounts: []v1.VolumeMount{
   295  							{
   296  								Name:      deleteVolumeName,
   297  								MountPath: path.Join(volumeMountPath, "delete"),
   298  								ReadOnly:  true,
   299  							},
   300  						},
   301  					},
   302  					{
   303  						Name:  updateContainerName,
   304  						Image: imageutils.GetE2EImage(imageutils.Agnhost),
   305  						Args:  []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/projected-configmap-volumes/update/data-3"},
   306  						VolumeMounts: []v1.VolumeMount{
   307  							{
   308  								Name:      updateVolumeName,
   309  								MountPath: path.Join(volumeMountPath, "update"),
   310  								ReadOnly:  true,
   311  							},
   312  						},
   313  					},
   314  					{
   315  						Name:  createContainerName,
   316  						Image: imageutils.GetE2EImage(imageutils.Agnhost),
   317  						Args:  []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/projected-configmap-volumes/create/data-1"},
   318  						VolumeMounts: []v1.VolumeMount{
   319  							{
   320  								Name:      createVolumeName,
   321  								MountPath: path.Join(volumeMountPath, "create"),
   322  								ReadOnly:  true,
   323  							},
   324  						},
   325  					},
   326  				},
   327  				RestartPolicy: v1.RestartPolicyNever,
   328  			},
   329  		}
   330  		ginkgo.By("Creating the pod")
   331  		e2epod.NewPodClient(f).CreateSync(ctx, pod)
   332  
   333  		pollCreateLogs := func() (string, error) {
   334  			return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, createContainerName)
   335  		}
   336  		gomega.Eventually(ctx, pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/projected-configmap-volumes/create/data-1"))
   337  
   338  		pollUpdateLogs := func() (string, error) {
   339  			return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName)
   340  		}
   341  		gomega.Eventually(ctx, pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/projected-configmap-volumes/update/data-3"))
   342  
   343  		pollDeleteLogs := func() (string, error) {
   344  			return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName)
   345  		}
   346  		gomega.Eventually(ctx, pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1"))
   347  
   348  		ginkgo.By(fmt.Sprintf("Deleting configmap %v", deleteConfigMap.Name))
   349  		err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Delete(ctx, deleteConfigMap.Name, metav1.DeleteOptions{})
   350  		framework.ExpectNoError(err, "Failed to delete configmap %q in namespace %q", deleteConfigMap.Name, f.Namespace.Name)
   351  
   352  		ginkgo.By(fmt.Sprintf("Updating configmap %v", updateConfigMap.Name))
   353  		updateConfigMap.ResourceVersion = "" // to force update
   354  		delete(updateConfigMap.Data, "data-1")
   355  		updateConfigMap.Data["data-3"] = "value-3"
   356  		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(ctx, updateConfigMap, metav1.UpdateOptions{})
   357  		framework.ExpectNoError(err, "Failed to update configmap %q in namespace %q", updateConfigMap.Name, f.Namespace.Name)
   358  
   359  		ginkgo.By(fmt.Sprintf("Creating configMap with name %s", createConfigMap.Name))
   360  		if createConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, createConfigMap, metav1.CreateOptions{}); err != nil {
   361  			framework.Failf("unable to create test configMap %s: %v", createConfigMap.Name, err)
   362  		}
   363  
   364  		ginkgo.By("waiting to observe update in volume")
   365  
   366  		gomega.Eventually(ctx, pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1"))
   367  		gomega.Eventually(ctx, pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-3"))
   368  		gomega.Eventually(ctx, pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/projected-configmap-volumes/delete/data-1"))
   369  	})
   370  
   371  	/*
   372  	   Release: v1.9
   373  	   Testname: Projected Volume, ConfigMap, multiple volume paths
   374  	   Description: A Pod is created with a projected volume source 'ConfigMap' to store a configMap. The configMap is mapped to two different volume mounts. Pod MUST be able to read the content of the configMap successfully from the two volume mounts.
   375  	*/
   376  	framework.ConformanceIt("should be consumable in multiple volumes in the same pod", f.WithNodeConformance(), func(ctx context.Context) {
   377  		var (
   378  			name             = "projected-configmap-test-volume-" + string(uuid.NewUUID())
   379  			volumeName       = "projected-configmap-volume"
   380  			volumeMountPath  = "/etc/projected-configmap-volume"
   381  			volumeName2      = "projected-configmap-volume-2"
   382  			volumeMountPath2 = "/etc/projected-configmap-volume-2"
   383  			configMap        = newConfigMap(f, name)
   384  		)
   385  
   386  		ginkgo.By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
   387  		var err error
   388  		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
   389  			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
   390  		}
   391  
   392  		pod := &v1.Pod{
   393  			ObjectMeta: metav1.ObjectMeta{
   394  				Name: "pod-projected-configmaps-" + string(uuid.NewUUID()),
   395  			},
   396  			Spec: v1.PodSpec{
   397  				Volumes: []v1.Volume{
   398  					{
   399  						Name: volumeName,
   400  						VolumeSource: v1.VolumeSource{
   401  							Projected: &v1.ProjectedVolumeSource{
   402  								Sources: []v1.VolumeProjection{
   403  									{
   404  										ConfigMap: &v1.ConfigMapProjection{
   405  											LocalObjectReference: v1.LocalObjectReference{
   406  												Name: name,
   407  											},
   408  										},
   409  									},
   410  								},
   411  							},
   412  						},
   413  					},
   414  					{
   415  						Name: volumeName2,
   416  						VolumeSource: v1.VolumeSource{
   417  							Projected: &v1.ProjectedVolumeSource{
   418  								Sources: []v1.VolumeProjection{
   419  									{
   420  
   421  										ConfigMap: &v1.ConfigMapProjection{
   422  											LocalObjectReference: v1.LocalObjectReference{
   423  												Name: name,
   424  											},
   425  										},
   426  									},
   427  								},
   428  							},
   429  						},
   430  					},
   431  				},
   432  				Containers: []v1.Container{
   433  					{
   434  						Name:  "projected-configmap-volume-test",
   435  						Image: imageutils.GetE2EImage(imageutils.Agnhost),
   436  						Args:  []string{"mounttest", "--file_content=/etc/projected-configmap-volume/data-1"},
   437  						VolumeMounts: []v1.VolumeMount{
   438  							{
   439  								Name:      volumeName,
   440  								MountPath: volumeMountPath,
   441  								ReadOnly:  true,
   442  							},
   443  							{
   444  								Name:      volumeName2,
   445  								MountPath: volumeMountPath2,
   446  								ReadOnly:  true,
   447  							},
   448  						},
   449  					},
   450  				},
   451  				RestartPolicy: v1.RestartPolicyNever,
   452  			},
   453  		}
   454  
   455  		e2epodoutput.TestContainerOutput(ctx, f, "consume configMaps", pod, 0, []string{
   456  			"content of file \"/etc/projected-configmap-volume/data-1\": value-1",
   457  		})
   458  
   459  	})
   460  
   461  	//The pod is in pending during volume creation until the configMap objects are available
   462  	//or until mount the configMap volume times out. There is no configMap object defined for the pod, so it should return timeout exception unless it is marked optional.
   463  	//Slow (~5 mins)
   464  	f.It("Should fail non-optional pod creation due to configMap object does not exist", f.WithSlow(), func(ctx context.Context) {
   465  		volumeMountPath := "/etc/projected-configmap-volumes"
   466  		pod := createNonOptionalConfigMapPod(ctx, f, volumeMountPath)
   467  		getPod := e2epod.Get(f.ClientSet, pod)
   468  		gomega.Consistently(ctx, getPod).WithTimeout(f.Timeouts.PodStart).Should(e2epod.BeInPhase(v1.PodPending))
   469  	})
   470  
   471  	//ConfigMap object defined for the pod, If a key is specified which is not present in the ConfigMap,
   472  	// the volume setup will error unless it is marked optional, during the pod creation.
   473  	//Slow (~5 mins)
   474  	f.It("Should fail non-optional pod creation due to the key in the configMap object does not exist", f.WithSlow(), func(ctx context.Context) {
   475  		volumeMountPath := "/etc/configmap-volumes"
   476  		pod := createNonOptionalConfigMapPodWithConfig(ctx, f, volumeMountPath)
   477  		getPod := e2epod.Get(f.ClientSet, pod)
   478  		gomega.Consistently(ctx, getPod).WithTimeout(f.Timeouts.PodStart).Should(e2epod.BeInPhase(v1.PodPending))
   479  	})
   480  })
   481  
   482  func doProjectedConfigMapE2EWithoutMappings(ctx context.Context, f *framework.Framework, asUser bool, fsGroup int64, defaultMode *int32) {
   483  	groupID := int64(fsGroup)
   484  
   485  	var (
   486  		name            = "projected-configmap-test-volume-" + string(uuid.NewUUID())
   487  		volumeName      = "projected-configmap-volume"
   488  		volumeMountPath = "/etc/projected-configmap-volume"
   489  		configMap       = newConfigMap(f, name)
   490  	)
   491  
   492  	ginkgo.By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
   493  	var err error
   494  	if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
   495  		framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
   496  	}
   497  
   498  	pod := createProjectedConfigMapMounttestPod(f.Namespace.Name, volumeName, name, volumeMountPath,
   499  		"--file_content=/etc/projected-configmap-volume/data-1", "--file_mode=/etc/projected-configmap-volume/data-1")
   500  
   501  	if asUser {
   502  		setPodNonRootUser(pod)
   503  	}
   504  
   505  	if groupID != 0 {
   506  		pod.Spec.SecurityContext.FSGroup = &groupID
   507  	}
   508  
   509  	if defaultMode != nil {
   510  		//pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].ConfigMap.DefaultMode = defaultMode
   511  		pod.Spec.Volumes[0].VolumeSource.Projected.DefaultMode = defaultMode
   512  	}
   513  
   514  	fileModeRegexp := getFileModeRegex("/etc/projected-configmap-volume/data-1", defaultMode)
   515  	output := []string{
   516  		"content of file \"/etc/projected-configmap-volume/data-1\": value-1",
   517  		fileModeRegexp,
   518  	}
   519  	e2epodoutput.TestContainerOutputRegexp(ctx, f, "consume configMaps", pod, 0, output)
   520  }
   521  
   522  func doProjectedConfigMapE2EWithMappings(ctx context.Context, f *framework.Framework, asUser bool, fsGroup int64, itemMode *int32) {
   523  	groupID := int64(fsGroup)
   524  
   525  	var (
   526  		name            = "projected-configmap-test-volume-map-" + string(uuid.NewUUID())
   527  		volumeName      = "projected-configmap-volume"
   528  		volumeMountPath = "/etc/projected-configmap-volume"
   529  		configMap       = newConfigMap(f, name)
   530  	)
   531  
   532  	ginkgo.By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
   533  
   534  	var err error
   535  	if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
   536  		framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
   537  	}
   538  
   539  	pod := createProjectedConfigMapMounttestPod(f.Namespace.Name, volumeName, name, volumeMountPath,
   540  		"--file_content=/etc/projected-configmap-volume/path/to/data-2", "--file_mode=/etc/projected-configmap-volume/path/to/data-2")
   541  	pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].ConfigMap.Items = []v1.KeyToPath{
   542  		{
   543  			Key:  "data-2",
   544  			Path: "path/to/data-2",
   545  		},
   546  	}
   547  
   548  	if asUser {
   549  		setPodNonRootUser(pod)
   550  	}
   551  
   552  	if groupID != 0 {
   553  		pod.Spec.SecurityContext.FSGroup = &groupID
   554  	}
   555  
   556  	if itemMode != nil {
   557  		//pod.Spec.Volumes[0].VolumeSource.ConfigMap.Items[0].Mode = itemMode
   558  		pod.Spec.Volumes[0].VolumeSource.Projected.DefaultMode = itemMode
   559  	}
   560  
   561  	// Just check file mode if fsGroup is not set. If fsGroup is set, the
   562  	// final mode is adjusted and we are not testing that case.
   563  	output := []string{
   564  		"content of file \"/etc/projected-configmap-volume/path/to/data-2\": value-2",
   565  	}
   566  	if fsGroup == 0 {
   567  		fileModeRegexp := getFileModeRegex("/etc/projected-configmap-volume/path/to/data-2", itemMode)
   568  		output = append(output, fileModeRegexp)
   569  	}
   570  	e2epodoutput.TestContainerOutputRegexp(ctx, f, "consume configMaps", pod, 0, output)
   571  }
   572  
   573  func createProjectedConfigMapMounttestPod(namespace, volumeName, referenceName, mountPath string, mounttestArgs ...string) *v1.Pod {
   574  	volumes := []v1.Volume{
   575  		{
   576  			Name: volumeName,
   577  			VolumeSource: v1.VolumeSource{
   578  				Projected: &v1.ProjectedVolumeSource{
   579  					Sources: []v1.VolumeProjection{
   580  						{
   581  							ConfigMap: &v1.ConfigMapProjection{
   582  								LocalObjectReference: v1.LocalObjectReference{
   583  									Name: referenceName,
   584  								},
   585  							},
   586  						},
   587  					},
   588  				},
   589  			},
   590  		},
   591  	}
   592  	podName := "pod-projected-configmaps-" + string(uuid.NewUUID())
   593  	mounttestArgs = append([]string{"mounttest"}, mounttestArgs...)
   594  	pod := e2epod.NewAgnhostPod(namespace, podName, volumes, createMounts(volumeName, mountPath, true), nil, mounttestArgs...)
   595  	pod.Spec.RestartPolicy = v1.RestartPolicyNever
   596  	return pod
   597  }
   598  

View as plain text