...

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

View as plain text