k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/common/storage/secrets_volume.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 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/uuid" 28 "k8s.io/kubernetes/test/e2e/framework" 29 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 30 e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 31 imageutils "k8s.io/kubernetes/test/utils/image" 32 admissionapi "k8s.io/pod-security-admission/api" 33 34 "github.com/onsi/ginkgo/v2" 35 "github.com/onsi/gomega" 36 ) 37 38 var _ = SIGDescribe("Secrets", func() { 39 f := framework.NewDefaultFramework("secrets") 40 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 41 42 /* 43 Release: v1.9 44 Testname: Secrets Volume, default 45 Description: Create a secret. Create a Pod with secret volume source configured into the container. Pod MUST be able to read the secret from the mounted volume from the container runtime and the file mode of the secret MUST be -rw-r--r-- by default. 46 */ 47 framework.ConformanceIt("should be consumable from pods in volume", f.WithNodeConformance(), func(ctx context.Context) { 48 doSecretE2EWithoutMapping(ctx, f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID()), nil, nil) 49 }) 50 51 /* 52 Release: v1.9 53 Testname: Secrets Volume, volume mode 0400 54 Description: Create a secret. Create a Pod with secret volume source configured into the container with file mode set to 0x400. Pod MUST be able to read the secret from the mounted volume from the container runtime and the file mode of the secret MUST be -r-------- by default. 55 This test is marked LinuxOnly since Windows does not support setting specific file permissions. 56 */ 57 framework.ConformanceIt("should be consumable from pods in volume with defaultMode set [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) { 58 defaultMode := int32(0400) 59 doSecretE2EWithoutMapping(ctx, f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), nil, nil) 60 }) 61 62 /* 63 Release: v1.9 64 Testname: Secrets Volume, volume mode 0440, fsGroup 1001 and uid 1000 65 Description: Create a secret. Create a Pod with secret volume source configured into the container with file mode set to 0x440 as a non-root user with uid 1000 and fsGroup id 1001. Pod MUST be able to read the secret from the mounted volume from the container runtime and the file mode of the secret MUST be -r--r-----by default. 66 This test is marked LinuxOnly since Windows does not support setting specific file permissions, or running as UID / GID. 67 */ 68 framework.ConformanceIt("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) { 69 defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */ 70 fsGroup := int64(1001) 71 doSecretE2EWithoutMapping(ctx, f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), &fsGroup, &nonRootTestUserID) 72 }) 73 74 /* 75 Release: v1.9 76 Testname: Secrets Volume, mapping 77 Description: Create a secret. Create a Pod with secret volume source configured into the container with a custom path. Pod MUST be able to read the secret from the mounted volume from the specified custom path. The file mode of the secret MUST be -rw-r--r-- by default. 78 */ 79 framework.ConformanceIt("should be consumable from pods in volume with mappings", f.WithNodeConformance(), func(ctx context.Context) { 80 doSecretE2EWithMapping(ctx, f, nil) 81 }) 82 83 /* 84 Release: v1.9 85 Testname: Secrets Volume, mapping, volume mode 0400 86 Description: Create a secret. Create a Pod with secret volume source configured into the container with a custom path and file mode set to 0x400. Pod MUST be able to read the secret from the mounted volume from the specified custom path. The file mode of the secret MUST be -r--r--r--. 87 This test is marked LinuxOnly since Windows does not support setting specific file permissions. 88 */ 89 framework.ConformanceIt("should be consumable from pods in volume with mappings and Item Mode set [LinuxOnly]", f.WithNodeConformance(), func(ctx context.Context) { 90 mode := int32(0400) 91 doSecretE2EWithMapping(ctx, f, &mode) 92 }) 93 94 /* 95 Release: v1.12 96 Testname: Secrets Volume, volume mode default, secret with same name in different namespace 97 Description: Create a secret with same name in two namespaces. Create a Pod with secret volume source configured into the container. Pod MUST be able to read the secrets from the mounted volume from the container runtime and only secrets which are associated with namespace where pod is created. The file mode of the secret MUST be -rw-r--r-- by default. 98 */ 99 framework.ConformanceIt("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) { 100 var ( 101 namespace2 *v1.Namespace 102 err error 103 secret2Name = "secret-test-" + string(uuid.NewUUID()) 104 ) 105 106 if namespace2, err = f.CreateNamespace(ctx, "secret-namespace", nil); err != nil { 107 framework.Failf("unable to create new namespace %s: %v", namespace2.Name, err) 108 } 109 110 secret2 := secretForTest(namespace2.Name, secret2Name) 111 secret2.Data = map[string][]byte{ 112 "this_should_not_match_content_of_other_secret": []byte("similarly_this_should_not_match_content_of_other_secret\n"), 113 } 114 if secret2, err = f.ClientSet.CoreV1().Secrets(namespace2.Name).Create(ctx, secret2, metav1.CreateOptions{}); err != nil { 115 framework.Failf("unable to create test secret %s: %v", secret2.Name, err) 116 } 117 doSecretE2EWithoutMapping(ctx, f, nil /* default mode */, secret2.Name, nil, nil) 118 }) 119 120 /* 121 Release: v1.9 122 Testname: Secrets Volume, mapping multiple volume paths 123 Description: Create a secret. Create a Pod with two secret volume sources configured into the container in to two different custom paths. Pod MUST be able to read the secret from the both the mounted volumes from the two specified custom paths. 124 */ 125 framework.ConformanceIt("should be consumable in multiple volumes in a pod", f.WithNodeConformance(), func(ctx context.Context) { 126 // This test ensures that the same secret can be mounted in multiple 127 // volumes in the same pod. This test case exists to prevent 128 // regressions that break this use-case. 129 var ( 130 name = "secret-test-" + string(uuid.NewUUID()) 131 volumeName = "secret-volume" 132 volumeMountPath = "/etc/secret-volume" 133 volumeName2 = "secret-volume-2" 134 volumeMountPath2 = "/etc/secret-volume-2" 135 secret = secretForTest(f.Namespace.Name, name) 136 ) 137 138 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name)) 139 var err error 140 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 141 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 142 } 143 144 pod := &v1.Pod{ 145 ObjectMeta: metav1.ObjectMeta{ 146 Name: "pod-secrets-" + string(uuid.NewUUID()), 147 }, 148 Spec: v1.PodSpec{ 149 Volumes: []v1.Volume{ 150 { 151 Name: volumeName, 152 VolumeSource: v1.VolumeSource{ 153 Secret: &v1.SecretVolumeSource{ 154 SecretName: name, 155 }, 156 }, 157 }, 158 { 159 Name: volumeName2, 160 VolumeSource: v1.VolumeSource{ 161 Secret: &v1.SecretVolumeSource{ 162 SecretName: name, 163 }, 164 }, 165 }, 166 }, 167 Containers: []v1.Container{ 168 { 169 Name: "secret-volume-test", 170 Image: imageutils.GetE2EImage(imageutils.Agnhost), 171 Args: []string{ 172 "mounttest", 173 "--file_content=/etc/secret-volume/data-1", 174 "--file_mode=/etc/secret-volume/data-1"}, 175 VolumeMounts: []v1.VolumeMount{ 176 { 177 Name: volumeName, 178 MountPath: volumeMountPath, 179 ReadOnly: true, 180 }, 181 { 182 Name: volumeName2, 183 MountPath: volumeMountPath2, 184 ReadOnly: true, 185 }, 186 }, 187 }, 188 }, 189 RestartPolicy: v1.RestartPolicyNever, 190 }, 191 } 192 193 fileModeRegexp := getFileModeRegex("/etc/secret-volume/data-1", nil) 194 e2epodoutput.TestContainerOutputRegexp(ctx, f, "consume secrets", pod, 0, []string{ 195 "content of file \"/etc/secret-volume/data-1\": value-1", 196 fileModeRegexp, 197 }) 198 }) 199 200 /* 201 Release: v1.9 202 Testname: Secrets Volume, create, update and delete 203 Description: Create a Pod with three containers with secrets volume sources namely a create, update and delete container. Create Container when started MUST not have 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. 204 */ 205 framework.ConformanceIt("optional updates should be reflected in volume", f.WithNodeConformance(), func(ctx context.Context) { 206 podLogTimeout := e2epod.GetPodSecretUpdateTimeout(ctx, f.ClientSet) 207 containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) 208 trueVal := true 209 volumeMountPath := "/etc/secret-volumes" 210 211 deleteName := "s-test-opt-del-" + string(uuid.NewUUID()) 212 deleteContainerName := "dels-volume-test" 213 deleteVolumeName := "deletes-volume" 214 deleteSecret := &v1.Secret{ 215 ObjectMeta: metav1.ObjectMeta{ 216 Namespace: f.Namespace.Name, 217 Name: deleteName, 218 }, 219 Data: map[string][]byte{ 220 "data-1": []byte("value-1"), 221 }, 222 } 223 224 updateName := "s-test-opt-upd-" + string(uuid.NewUUID()) 225 updateContainerName := "upds-volume-test" 226 updateVolumeName := "updates-volume" 227 updateSecret := &v1.Secret{ 228 ObjectMeta: metav1.ObjectMeta{ 229 Namespace: f.Namespace.Name, 230 Name: updateName, 231 }, 232 Data: map[string][]byte{ 233 "data-1": []byte("value-1"), 234 }, 235 } 236 237 createName := "s-test-opt-create-" + string(uuid.NewUUID()) 238 createContainerName := "creates-volume-test" 239 createVolumeName := "creates-volume" 240 createSecret := &v1.Secret{ 241 ObjectMeta: metav1.ObjectMeta{ 242 Namespace: f.Namespace.Name, 243 Name: createName, 244 }, 245 Data: map[string][]byte{ 246 "data-1": []byte("value-1"), 247 }, 248 } 249 250 ginkgo.By(fmt.Sprintf("Creating secret with name %s", deleteSecret.Name)) 251 var err error 252 if deleteSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, deleteSecret, metav1.CreateOptions{}); err != nil { 253 framework.Failf("unable to create test secret %s: %v", deleteSecret.Name, err) 254 } 255 256 ginkgo.By(fmt.Sprintf("Creating secret with name %s", updateSecret.Name)) 257 if updateSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, updateSecret, metav1.CreateOptions{}); err != nil { 258 framework.Failf("unable to create test secret %s: %v", updateSecret.Name, err) 259 } 260 261 pod := &v1.Pod{ 262 ObjectMeta: metav1.ObjectMeta{ 263 Name: "pod-secrets-" + string(uuid.NewUUID()), 264 }, 265 Spec: v1.PodSpec{ 266 Volumes: []v1.Volume{ 267 { 268 Name: deleteVolumeName, 269 VolumeSource: v1.VolumeSource{ 270 Secret: &v1.SecretVolumeSource{ 271 SecretName: deleteName, 272 Optional: &trueVal, 273 }, 274 }, 275 }, 276 { 277 Name: updateVolumeName, 278 VolumeSource: v1.VolumeSource{ 279 Secret: &v1.SecretVolumeSource{ 280 SecretName: updateName, 281 Optional: &trueVal, 282 }, 283 }, 284 }, 285 { 286 Name: createVolumeName, 287 VolumeSource: v1.VolumeSource{ 288 Secret: &v1.SecretVolumeSource{ 289 SecretName: createName, 290 Optional: &trueVal, 291 }, 292 }, 293 }, 294 }, 295 Containers: []v1.Container{ 296 { 297 Name: deleteContainerName, 298 Image: imageutils.GetE2EImage(imageutils.Agnhost), 299 Args: []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/delete/data-1"}, 300 VolumeMounts: []v1.VolumeMount{ 301 { 302 Name: deleteVolumeName, 303 MountPath: path.Join(volumeMountPath, "delete"), 304 ReadOnly: true, 305 }, 306 }, 307 }, 308 { 309 Name: updateContainerName, 310 Image: imageutils.GetE2EImage(imageutils.Agnhost), 311 Args: []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/update/data-3"}, 312 VolumeMounts: []v1.VolumeMount{ 313 { 314 Name: updateVolumeName, 315 MountPath: path.Join(volumeMountPath, "update"), 316 ReadOnly: true, 317 }, 318 }, 319 }, 320 { 321 Name: createContainerName, 322 Image: imageutils.GetE2EImage(imageutils.Agnhost), 323 Args: []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"}, 324 VolumeMounts: []v1.VolumeMount{ 325 { 326 Name: createVolumeName, 327 MountPath: path.Join(volumeMountPath, "create"), 328 ReadOnly: true, 329 }, 330 }, 331 }, 332 }, 333 RestartPolicy: v1.RestartPolicyNever, 334 }, 335 } 336 ginkgo.By("Creating the pod") 337 e2epod.NewPodClient(f).CreateSync(ctx, pod) 338 339 pollCreateLogs := func() (string, error) { 340 return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, createContainerName) 341 } 342 gomega.Eventually(ctx, pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/create/data-1")) 343 344 pollUpdateLogs := func() (string, error) { 345 return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName) 346 } 347 gomega.Eventually(ctx, pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/update/data-3")) 348 349 pollDeleteLogs := func() (string, error) { 350 return e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName) 351 } 352 gomega.Eventually(ctx, pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1")) 353 354 ginkgo.By(fmt.Sprintf("Deleting secret %v", deleteSecret.Name)) 355 err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(ctx, deleteSecret.Name, metav1.DeleteOptions{}) 356 framework.ExpectNoError(err, "Failed to delete secret %q in namespace %q", deleteSecret.Name, f.Namespace.Name) 357 358 ginkgo.By(fmt.Sprintf("Updating secret %v", updateSecret.Name)) 359 updateSecret.ResourceVersion = "" // to force update 360 delete(updateSecret.Data, "data-1") 361 updateSecret.Data["data-3"] = []byte("value-3") 362 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, updateSecret, metav1.UpdateOptions{}) 363 framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", updateSecret.Name, f.Namespace.Name) 364 365 ginkgo.By(fmt.Sprintf("Creating secret with name %s", createSecret.Name)) 366 if createSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, createSecret, metav1.CreateOptions{}); err != nil { 367 framework.Failf("unable to create test secret %s: %v", createSecret.Name, err) 368 } 369 370 ginkgo.By("waiting to observe update in volume") 371 372 gomega.Eventually(ctx, pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1")) 373 gomega.Eventually(ctx, pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-3")) 374 gomega.Eventually(ctx, pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1")) 375 }) 376 377 /* 378 Release: v1.21 379 Testname: Secrets Volume, immutability 380 Description: Create a secret. Update it's data field, the update MUST succeed. 381 Mark the secret as immutable, the update MUST succeed. Try to update its data, the update MUST fail. 382 Try to mark the secret back as not immutable, the update MUST fail. 383 Try to update the secret`s metadata (labels), the update must succeed. 384 Try to delete the secret, the deletion must succeed. 385 */ 386 framework.ConformanceIt("should be immutable if `immutable` field is set", func(ctx context.Context) { 387 name := "immutable" 388 secret := secretForTest(f.Namespace.Name, name) 389 390 currentSecret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}) 391 framework.ExpectNoError(err, "Failed to create secret %q in namespace %q", secret.Name, secret.Namespace) 392 393 currentSecret.Data["data-4"] = []byte("value-4\n") 394 currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, currentSecret, metav1.UpdateOptions{}) 395 framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace) 396 397 // Mark secret as immutable. 398 trueVal := true 399 currentSecret.Immutable = &trueVal 400 currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, currentSecret, metav1.UpdateOptions{}) 401 framework.ExpectNoError(err, "Failed to mark secret %q in namespace %q as immutable", secret.Name, secret.Namespace) 402 403 // Ensure data can't be changed now. 404 currentSecret.Data["data-5"] = []byte("value-5\n") 405 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, currentSecret, metav1.UpdateOptions{}) 406 if !apierrors.IsInvalid(err) { 407 framework.Failf("expected 'invalid' as error, got instead: %v", err) 408 } 409 410 // Ensure secret can't be switched from immutable to mutable. 411 currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, name, metav1.GetOptions{}) 412 framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace) 413 if !*currentSecret.Immutable { 414 framework.Failf("currentSecret %s can be switched from immutable to mutable", currentSecret.Name) 415 } 416 417 falseVal := false 418 currentSecret.Immutable = &falseVal 419 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, currentSecret, metav1.UpdateOptions{}) 420 if !apierrors.IsInvalid(err) { 421 framework.Failf("expected 'invalid' as error, got instead: %v", err) 422 } 423 424 // Ensure that metadata can be changed. 425 currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, name, metav1.GetOptions{}) 426 framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace) 427 currentSecret.Labels = map[string]string{"label1": "value1"} 428 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(ctx, currentSecret, metav1.UpdateOptions{}) 429 framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace) 430 431 // Ensure that immutable secret can be deleted. 432 err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(ctx, name, metav1.DeleteOptions{}) 433 framework.ExpectNoError(err, "Failed to delete secret %q in namespace %q", secret.Name, secret.Namespace) 434 }) 435 436 // The secret is in pending during volume creation until the secret objects are available 437 // 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. 438 // Slow (~5 mins) 439 f.It("Should fail non-optional pod creation due to secret object does not exist", f.WithSlow(), func(ctx context.Context) { 440 volumeMountPath := "/etc/secret-volumes" 441 podName := "pod-secrets-" + string(uuid.NewUUID()) 442 pod := createNonOptionalSecretPod(ctx, f, volumeMountPath, podName) 443 getPod := e2epod.Get(f.ClientSet, pod) 444 gomega.Consistently(ctx, getPod).WithTimeout(f.Timeouts.PodStart).Should(e2epod.BeInPhase(v1.PodPending)) 445 }) 446 447 // Secret object defined for the pod, If a key is specified which is not present in the secret, 448 // the volume setup will error unless it is marked optional, during the pod creation. 449 // Slow (~5 mins) 450 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) { 451 volumeMountPath := "/etc/secret-volumes" 452 podName := "pod-secrets-" + string(uuid.NewUUID()) 453 pod := createNonOptionalSecretPodWithSecret(ctx, f, volumeMountPath, podName) 454 getPod := e2epod.Get(f.ClientSet, pod) 455 gomega.Consistently(ctx, getPod).WithTimeout(f.Timeouts.PodStart).Should(e2epod.BeInPhase(v1.PodPending)) 456 }) 457 }) 458 459 func secretForTest(namespace, name string) *v1.Secret { 460 return &v1.Secret{ 461 ObjectMeta: metav1.ObjectMeta{ 462 Namespace: namespace, 463 Name: name, 464 }, 465 Data: map[string][]byte{ 466 "data-1": []byte("value-1\n"), 467 "data-2": []byte("value-2\n"), 468 "data-3": []byte("value-3\n"), 469 }, 470 } 471 } 472 473 func doSecretE2EWithoutMapping(ctx context.Context, f *framework.Framework, defaultMode *int32, secretName string, 474 fsGroup *int64, uid *int64) { 475 var ( 476 volumeName = "secret-volume" 477 volumeMountPath = "/etc/secret-volume" 478 secret = secretForTest(f.Namespace.Name, secretName) 479 ) 480 481 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name)) 482 var err error 483 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 484 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 485 } 486 487 pod := &v1.Pod{ 488 ObjectMeta: metav1.ObjectMeta{ 489 Name: "pod-secrets-" + string(uuid.NewUUID()), 490 Namespace: f.Namespace.Name, 491 }, 492 Spec: v1.PodSpec{ 493 Volumes: []v1.Volume{ 494 { 495 Name: volumeName, 496 VolumeSource: v1.VolumeSource{ 497 Secret: &v1.SecretVolumeSource{ 498 SecretName: secretName, 499 }, 500 }, 501 }, 502 }, 503 Containers: []v1.Container{ 504 { 505 Name: "secret-volume-test", 506 Image: imageutils.GetE2EImage(imageutils.Agnhost), 507 Args: []string{ 508 "mounttest", 509 "--file_content=/etc/secret-volume/data-1", 510 "--file_mode=/etc/secret-volume/data-1"}, 511 VolumeMounts: []v1.VolumeMount{ 512 { 513 Name: volumeName, 514 MountPath: volumeMountPath, 515 }, 516 }, 517 }, 518 }, 519 RestartPolicy: v1.RestartPolicyNever, 520 }, 521 } 522 523 if defaultMode != nil { 524 pod.Spec.Volumes[0].VolumeSource.Secret.DefaultMode = defaultMode 525 } 526 527 if fsGroup != nil || uid != nil { 528 pod.Spec.SecurityContext = &v1.PodSecurityContext{ 529 FSGroup: fsGroup, 530 RunAsUser: uid, 531 } 532 } 533 534 fileModeRegexp := getFileModeRegex("/etc/secret-volume/data-1", defaultMode) 535 expectedOutput := []string{ 536 "content of file \"/etc/secret-volume/data-1\": value-1", 537 fileModeRegexp, 538 } 539 540 e2epodoutput.TestContainerOutputRegexp(ctx, f, "consume secrets", pod, 0, expectedOutput) 541 } 542 543 func doSecretE2EWithMapping(ctx context.Context, f *framework.Framework, mode *int32) { 544 var ( 545 name = "secret-test-map-" + string(uuid.NewUUID()) 546 volumeName = "secret-volume" 547 volumeMountPath = "/etc/secret-volume" 548 secret = secretForTest(f.Namespace.Name, name) 549 ) 550 551 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name)) 552 var err error 553 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 554 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 555 } 556 557 pod := &v1.Pod{ 558 ObjectMeta: metav1.ObjectMeta{ 559 Name: "pod-secrets-" + string(uuid.NewUUID()), 560 }, 561 Spec: v1.PodSpec{ 562 Volumes: []v1.Volume{ 563 { 564 Name: volumeName, 565 VolumeSource: v1.VolumeSource{ 566 Secret: &v1.SecretVolumeSource{ 567 SecretName: name, 568 Items: []v1.KeyToPath{ 569 { 570 Key: "data-1", 571 Path: "new-path-data-1", 572 }, 573 }, 574 }, 575 }, 576 }, 577 }, 578 Containers: []v1.Container{ 579 { 580 Name: "secret-volume-test", 581 Image: imageutils.GetE2EImage(imageutils.Agnhost), 582 Args: []string{ 583 "mounttest", 584 "--file_content=/etc/secret-volume/new-path-data-1", 585 "--file_mode=/etc/secret-volume/new-path-data-1"}, 586 VolumeMounts: []v1.VolumeMount{ 587 { 588 Name: volumeName, 589 MountPath: volumeMountPath, 590 }, 591 }, 592 }, 593 }, 594 RestartPolicy: v1.RestartPolicyNever, 595 }, 596 } 597 598 if mode != nil { 599 pod.Spec.Volumes[0].VolumeSource.Secret.Items[0].Mode = mode 600 } 601 602 fileModeRegexp := getFileModeRegex("/etc/secret-volume/new-path-data-1", mode) 603 expectedOutput := []string{ 604 "content of file \"/etc/secret-volume/new-path-data-1\": value-1", 605 fileModeRegexp, 606 } 607 608 e2epodoutput.TestContainerOutputRegexp(ctx, f, "consume secrets", pod, 0, expectedOutput) 609 } 610 611 func createNonOptionalSecretPod(ctx context.Context, f *framework.Framework, volumeMountPath, podName string) *v1.Pod { 612 podLogTimeout := e2epod.GetPodSecretUpdateTimeout(ctx, f.ClientSet) 613 containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) 614 falseValue := false 615 616 createName := "s-test-opt-create-" + string(uuid.NewUUID()) 617 createContainerName := "creates-volume-test" 618 createVolumeName := "creates-volume" 619 620 // creating a pod without secret object created, by mentioning the secret volume source reference name 621 pod := &v1.Pod{ 622 ObjectMeta: metav1.ObjectMeta{ 623 Name: podName, 624 }, 625 Spec: v1.PodSpec{ 626 Volumes: []v1.Volume{ 627 { 628 Name: createVolumeName, 629 VolumeSource: v1.VolumeSource{ 630 Secret: &v1.SecretVolumeSource{ 631 SecretName: createName, 632 Optional: &falseValue, 633 }, 634 }, 635 }, 636 }, 637 Containers: []v1.Container{ 638 { 639 Name: createContainerName, 640 Image: imageutils.GetE2EImage(imageutils.Agnhost), 641 Args: []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"}, 642 VolumeMounts: []v1.VolumeMount{ 643 { 644 Name: createVolumeName, 645 MountPath: path.Join(volumeMountPath, "create"), 646 ReadOnly: true, 647 }, 648 }, 649 }, 650 }, 651 RestartPolicy: v1.RestartPolicyNever, 652 }, 653 } 654 ginkgo.By("Creating the pod") 655 pod = e2epod.NewPodClient(f).Create(ctx, pod) 656 return pod 657 } 658 659 func createNonOptionalSecretPodWithSecret(ctx context.Context, f *framework.Framework, volumeMountPath, podName string) *v1.Pod { 660 podLogTimeout := e2epod.GetPodSecretUpdateTimeout(ctx, f.ClientSet) 661 containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds())) 662 falseValue := false 663 664 createName := "s-test-opt-create-" + string(uuid.NewUUID()) 665 createContainerName := "creates-volume-test" 666 createVolumeName := "creates-volume" 667 668 secret := secretForTest(f.Namespace.Name, createName) 669 670 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name)) 671 var err error 672 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 673 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 674 } 675 // creating a pod with secret object, with the key which is not present in secret object. 676 pod := &v1.Pod{ 677 ObjectMeta: metav1.ObjectMeta{ 678 Name: podName, 679 }, 680 Spec: v1.PodSpec{ 681 Volumes: []v1.Volume{ 682 { 683 Name: createVolumeName, 684 VolumeSource: v1.VolumeSource{ 685 Secret: &v1.SecretVolumeSource{ 686 SecretName: createName, 687 Items: []v1.KeyToPath{ 688 { 689 Key: "data_4", 690 Path: "value-4\n", 691 }, 692 }, 693 Optional: &falseValue, 694 }, 695 }, 696 }, 697 }, 698 Containers: []v1.Container{ 699 { 700 Name: createContainerName, 701 Image: imageutils.GetE2EImage(imageutils.Agnhost), 702 Args: []string{"mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"}, 703 VolumeMounts: []v1.VolumeMount{ 704 { 705 Name: createVolumeName, 706 MountPath: path.Join(volumeMountPath, "create"), 707 ReadOnly: true, 708 }, 709 }, 710 }, 711 }, 712 RestartPolicy: v1.RestartPolicyNever, 713 }, 714 } 715 ginkgo.By("Creating the pod") 716 pod = e2epod.NewPodClient(f).Create(ctx, pod) 717 return pod 718 }