k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/common/storage/projected_secret.go (about) 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 }