secrets_volume.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package common
  14. import (
  15. "context"
  16. "fmt"
  17. "path"
  18. "k8s.io/api/core/v1"
  19. apierrors "k8s.io/apimachinery/pkg/api/errors"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/util/uuid"
  22. "k8s.io/kubernetes/test/e2e/framework"
  23. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  24. imageutils "k8s.io/kubernetes/test/utils/image"
  25. "github.com/onsi/ginkgo"
  26. "github.com/onsi/gomega"
  27. )
  28. var _ = ginkgo.Describe("[sig-storage] Secrets", func() {
  29. f := framework.NewDefaultFramework("secrets")
  30. /*
  31. Release : v1.9
  32. Testname: Secrets Volume, default
  33. 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.
  34. */
  35. framework.ConformanceIt("should be consumable from pods in volume [NodeConformance]", func() {
  36. doSecretE2EWithoutMapping(f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID()), nil, nil)
  37. })
  38. /*
  39. Release : v1.9
  40. Testname: Secrets Volume, volume mode 0400
  41. 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.
  42. This test is marked LinuxOnly since Windows does not support setting specific file permissions.
  43. */
  44. framework.ConformanceIt("should be consumable from pods in volume with defaultMode set [LinuxOnly] [NodeConformance]", func() {
  45. defaultMode := int32(0400)
  46. doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), nil, nil)
  47. })
  48. /*
  49. Release : v1.9
  50. Testname: Secrets Volume, volume mode 0440, fsGroup 1001 and uid 1000
  51. 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.
  52. This test is marked LinuxOnly since Windows does not support setting specific file permissions, or running as UID / GID.
  53. */
  54. framework.ConformanceIt("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [LinuxOnly] [NodeConformance]", func() {
  55. defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */
  56. fsGroup := int64(1001)
  57. doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), &fsGroup, &nonRootTestUserID)
  58. })
  59. /*
  60. Release : v1.9
  61. Testname: Secrets Volume, mapping
  62. 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.
  63. */
  64. framework.ConformanceIt("should be consumable from pods in volume with mappings [NodeConformance]", func() {
  65. doSecretE2EWithMapping(f, nil)
  66. })
  67. /*
  68. Release : v1.9
  69. Testname: Secrets Volume, mapping, volume mode 0400
  70. 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--.
  71. This test is marked LinuxOnly since Windows does not support setting specific file permissions.
  72. */
  73. framework.ConformanceIt("should be consumable from pods in volume with mappings and Item Mode set [LinuxOnly] [NodeConformance]", func() {
  74. mode := int32(0400)
  75. doSecretE2EWithMapping(f, &mode)
  76. })
  77. /*
  78. Release : v1.12
  79. Testname: Secrets Volume, volume mode default, secret with same name in different namespace
  80. 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.
  81. */
  82. framework.ConformanceIt("should be able to mount in a volume regardless of a different secret existing with same name in different namespace [NodeConformance]", func() {
  83. var (
  84. namespace2 *v1.Namespace
  85. err error
  86. secret2Name = "secret-test-" + string(uuid.NewUUID())
  87. )
  88. if namespace2, err = f.CreateNamespace("secret-namespace", nil); err != nil {
  89. framework.Failf("unable to create new namespace %s: %v", namespace2.Name, err)
  90. }
  91. secret2 := secretForTest(namespace2.Name, secret2Name)
  92. secret2.Data = map[string][]byte{
  93. "this_should_not_match_content_of_other_secret": []byte("similarly_this_should_not_match_content_of_other_secret\n"),
  94. }
  95. if secret2, err = f.ClientSet.CoreV1().Secrets(namespace2.Name).Create(context.TODO(), secret2, metav1.CreateOptions{}); err != nil {
  96. framework.Failf("unable to create test secret %s: %v", secret2.Name, err)
  97. }
  98. doSecretE2EWithoutMapping(f, nil /* default mode */, secret2.Name, nil, nil)
  99. })
  100. /*
  101. Release : v1.9
  102. Testname: Secrets Volume, mapping multiple volume paths
  103. 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.
  104. */
  105. framework.ConformanceIt("should be consumable in multiple volumes in a pod [NodeConformance]", func() {
  106. // This test ensures that the same secret can be mounted in multiple
  107. // volumes in the same pod. This test case exists to prevent
  108. // regressions that break this use-case.
  109. var (
  110. name = "secret-test-" + string(uuid.NewUUID())
  111. volumeName = "secret-volume"
  112. volumeMountPath = "/etc/secret-volume"
  113. volumeName2 = "secret-volume-2"
  114. volumeMountPath2 = "/etc/secret-volume-2"
  115. secret = secretForTest(f.Namespace.Name, name)
  116. )
  117. ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name))
  118. var err error
  119. if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
  120. framework.Failf("unable to create test secret %s: %v", secret.Name, err)
  121. }
  122. pod := &v1.Pod{
  123. ObjectMeta: metav1.ObjectMeta{
  124. Name: "pod-secrets-" + string(uuid.NewUUID()),
  125. },
  126. Spec: v1.PodSpec{
  127. Volumes: []v1.Volume{
  128. {
  129. Name: volumeName,
  130. VolumeSource: v1.VolumeSource{
  131. Secret: &v1.SecretVolumeSource{
  132. SecretName: name,
  133. },
  134. },
  135. },
  136. {
  137. Name: volumeName2,
  138. VolumeSource: v1.VolumeSource{
  139. Secret: &v1.SecretVolumeSource{
  140. SecretName: name,
  141. },
  142. },
  143. },
  144. },
  145. Containers: []v1.Container{
  146. {
  147. Name: "secret-volume-test",
  148. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  149. Args: []string{
  150. "--file_content=/etc/secret-volume/data-1",
  151. "--file_mode=/etc/secret-volume/data-1"},
  152. VolumeMounts: []v1.VolumeMount{
  153. {
  154. Name: volumeName,
  155. MountPath: volumeMountPath,
  156. ReadOnly: true,
  157. },
  158. {
  159. Name: volumeName2,
  160. MountPath: volumeMountPath2,
  161. ReadOnly: true,
  162. },
  163. },
  164. },
  165. },
  166. RestartPolicy: v1.RestartPolicyNever,
  167. },
  168. }
  169. fileModeRegexp := framework.GetFileModeRegex("/etc/secret-volume/data-1", nil)
  170. f.TestContainerOutputRegexp("consume secrets", pod, 0, []string{
  171. "content of file \"/etc/secret-volume/data-1\": value-1",
  172. fileModeRegexp,
  173. })
  174. })
  175. /*
  176. Release : v1.9
  177. Testname: Secrets Volume, create, update and delete
  178. 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.
  179. */
  180. framework.ConformanceIt("optional updates should be reflected in volume [NodeConformance]", func() {
  181. podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet)
  182. containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds()))
  183. trueVal := true
  184. volumeMountPath := "/etc/secret-volumes"
  185. deleteName := "s-test-opt-del-" + string(uuid.NewUUID())
  186. deleteContainerName := "dels-volume-test"
  187. deleteVolumeName := "deletes-volume"
  188. deleteSecret := &v1.Secret{
  189. ObjectMeta: metav1.ObjectMeta{
  190. Namespace: f.Namespace.Name,
  191. Name: deleteName,
  192. },
  193. Data: map[string][]byte{
  194. "data-1": []byte("value-1"),
  195. },
  196. }
  197. updateName := "s-test-opt-upd-" + string(uuid.NewUUID())
  198. updateContainerName := "upds-volume-test"
  199. updateVolumeName := "updates-volume"
  200. updateSecret := &v1.Secret{
  201. ObjectMeta: metav1.ObjectMeta{
  202. Namespace: f.Namespace.Name,
  203. Name: updateName,
  204. },
  205. Data: map[string][]byte{
  206. "data-1": []byte("value-1"),
  207. },
  208. }
  209. createName := "s-test-opt-create-" + string(uuid.NewUUID())
  210. createContainerName := "creates-volume-test"
  211. createVolumeName := "creates-volume"
  212. createSecret := &v1.Secret{
  213. ObjectMeta: metav1.ObjectMeta{
  214. Namespace: f.Namespace.Name,
  215. Name: createName,
  216. },
  217. Data: map[string][]byte{
  218. "data-1": []byte("value-1"),
  219. },
  220. }
  221. ginkgo.By(fmt.Sprintf("Creating secret with name %s", deleteSecret.Name))
  222. var err error
  223. if deleteSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), deleteSecret, metav1.CreateOptions{}); err != nil {
  224. framework.Failf("unable to create test secret %s: %v", deleteSecret.Name, err)
  225. }
  226. ginkgo.By(fmt.Sprintf("Creating secret with name %s", updateSecret.Name))
  227. if updateSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), updateSecret, metav1.CreateOptions{}); err != nil {
  228. framework.Failf("unable to create test secret %s: %v", updateSecret.Name, err)
  229. }
  230. pod := &v1.Pod{
  231. ObjectMeta: metav1.ObjectMeta{
  232. Name: "pod-secrets-" + string(uuid.NewUUID()),
  233. },
  234. Spec: v1.PodSpec{
  235. Volumes: []v1.Volume{
  236. {
  237. Name: deleteVolumeName,
  238. VolumeSource: v1.VolumeSource{
  239. Secret: &v1.SecretVolumeSource{
  240. SecretName: deleteName,
  241. Optional: &trueVal,
  242. },
  243. },
  244. },
  245. {
  246. Name: updateVolumeName,
  247. VolumeSource: v1.VolumeSource{
  248. Secret: &v1.SecretVolumeSource{
  249. SecretName: updateName,
  250. Optional: &trueVal,
  251. },
  252. },
  253. },
  254. {
  255. Name: createVolumeName,
  256. VolumeSource: v1.VolumeSource{
  257. Secret: &v1.SecretVolumeSource{
  258. SecretName: createName,
  259. Optional: &trueVal,
  260. },
  261. },
  262. },
  263. },
  264. Containers: []v1.Container{
  265. {
  266. Name: deleteContainerName,
  267. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  268. Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/delete/data-1"},
  269. VolumeMounts: []v1.VolumeMount{
  270. {
  271. Name: deleteVolumeName,
  272. MountPath: path.Join(volumeMountPath, "delete"),
  273. ReadOnly: true,
  274. },
  275. },
  276. },
  277. {
  278. Name: updateContainerName,
  279. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  280. Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/update/data-3"},
  281. VolumeMounts: []v1.VolumeMount{
  282. {
  283. Name: updateVolumeName,
  284. MountPath: path.Join(volumeMountPath, "update"),
  285. ReadOnly: true,
  286. },
  287. },
  288. },
  289. {
  290. Name: createContainerName,
  291. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  292. Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"},
  293. VolumeMounts: []v1.VolumeMount{
  294. {
  295. Name: createVolumeName,
  296. MountPath: path.Join(volumeMountPath, "create"),
  297. ReadOnly: true,
  298. },
  299. },
  300. },
  301. },
  302. RestartPolicy: v1.RestartPolicyNever,
  303. },
  304. }
  305. ginkgo.By("Creating the pod")
  306. f.PodClient().CreateSync(pod)
  307. pollCreateLogs := func() (string, error) {
  308. return e2epod.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName)
  309. }
  310. gomega.Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/create/data-1"))
  311. pollUpdateLogs := func() (string, error) {
  312. return e2epod.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName)
  313. }
  314. gomega.Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/update/data-3"))
  315. pollDeleteLogs := func() (string, error) {
  316. return e2epod.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName)
  317. }
  318. gomega.Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1"))
  319. ginkgo.By(fmt.Sprintf("Deleting secret %v", deleteSecret.Name))
  320. err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), deleteSecret.Name, &metav1.DeleteOptions{})
  321. framework.ExpectNoError(err, "Failed to delete secret %q in namespace %q", deleteSecret.Name, f.Namespace.Name)
  322. ginkgo.By(fmt.Sprintf("Updating secret %v", updateSecret.Name))
  323. updateSecret.ResourceVersion = "" // to force update
  324. delete(updateSecret.Data, "data-1")
  325. updateSecret.Data["data-3"] = []byte("value-3")
  326. _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), updateSecret, metav1.UpdateOptions{})
  327. framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", updateSecret.Name, f.Namespace.Name)
  328. ginkgo.By(fmt.Sprintf("Creating secret with name %s", createSecret.Name))
  329. if createSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), createSecret, metav1.CreateOptions{}); err != nil {
  330. framework.Failf("unable to create test secret %s: %v", createSecret.Name, err)
  331. }
  332. ginkgo.By("waiting to observe update in volume")
  333. gomega.Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-1"))
  334. gomega.Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("value-3"))
  335. gomega.Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1"))
  336. })
  337. // It should be forbidden to change data for secrets marked as immutable, but
  338. // allowed to modify its metadata independently of its state.
  339. ginkgo.It("should be immutable if `immutable` field is set [Feature:ImmutableEphemeralVolume]", func() {
  340. name := "immutable"
  341. secret := secretForTest(f.Namespace.Name, name)
  342. currentSecret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{})
  343. framework.ExpectNoError(err, "Failed to create secret %q in namespace %q", secret.Name, secret.Namespace)
  344. currentSecret.Data["data-4"] = []byte("value-4\n")
  345. currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), currentSecret, metav1.UpdateOptions{})
  346. framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace)
  347. // Mark secret as immutable.
  348. trueVal := true
  349. currentSecret.Immutable = &trueVal
  350. currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), currentSecret, metav1.UpdateOptions{})
  351. framework.ExpectNoError(err, "Failed to mark secret %q in namespace %q as immutable", secret.Name, secret.Namespace)
  352. // Ensure data can't be changed now.
  353. currentSecret.Data["data-5"] = []byte("value-5\n")
  354. _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), currentSecret, metav1.UpdateOptions{})
  355. framework.ExpectEqual(apierrors.IsInvalid(err), true)
  356. // Ensure secret can't be switched from immutable to mutable.
  357. currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
  358. framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace)
  359. framework.ExpectEqual(*currentSecret.Immutable, true)
  360. falseVal := false
  361. currentSecret.Immutable = &falseVal
  362. _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), currentSecret, metav1.UpdateOptions{})
  363. framework.ExpectEqual(apierrors.IsInvalid(err), true)
  364. // Ensure that metadata can be changed.
  365. currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
  366. framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace)
  367. currentSecret.Labels = map[string]string{"label1": "value1"}
  368. _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), currentSecret, metav1.UpdateOptions{})
  369. framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace)
  370. // Ensure that immutable secret can be deleted.
  371. err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), name, &metav1.DeleteOptions{})
  372. framework.ExpectNoError(err, "Failed to delete secret %q in namespace %q", secret.Name, secret.Namespace)
  373. })
  374. // The secret is in pending during volume creation until the secret objects are available
  375. // or until mount the secret volume times out. There is no secret object defined for the pod, so it should return timout exception unless it is marked optional.
  376. // Slow (~5 mins)
  377. ginkgo.It("Should fail non-optional pod creation due to secret object does not exist [Slow]", func() {
  378. volumeMountPath := "/etc/secret-volumes"
  379. podName := "pod-secrets-" + string(uuid.NewUUID())
  380. err := createNonOptionalSecretPod(f, volumeMountPath, podName)
  381. framework.ExpectError(err, "created pod %q with non-optional secret in namespace %q", podName, f.Namespace.Name)
  382. })
  383. // Secret object defined for the pod, If a key is specified which is not present in the secret,
  384. // the volume setup will error unless it is marked optional, during the pod creation.
  385. // Slow (~5 mins)
  386. ginkgo.It("Should fail non-optional pod creation due to the key in the secret object does not exist [Slow]", func() {
  387. volumeMountPath := "/etc/secret-volumes"
  388. podName := "pod-secrets-" + string(uuid.NewUUID())
  389. err := createNonOptionalSecretPodWithSecret(f, volumeMountPath, podName)
  390. framework.ExpectError(err, "created pod %q with non-optional secret in namespace %q", podName, f.Namespace.Name)
  391. })
  392. })
  393. func secretForTest(namespace, name string) *v1.Secret {
  394. return &v1.Secret{
  395. ObjectMeta: metav1.ObjectMeta{
  396. Namespace: namespace,
  397. Name: name,
  398. },
  399. Data: map[string][]byte{
  400. "data-1": []byte("value-1\n"),
  401. "data-2": []byte("value-2\n"),
  402. "data-3": []byte("value-3\n"),
  403. },
  404. }
  405. }
  406. func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secretName string,
  407. fsGroup *int64, uid *int64) {
  408. var (
  409. volumeName = "secret-volume"
  410. volumeMountPath = "/etc/secret-volume"
  411. secret = secretForTest(f.Namespace.Name, secretName)
  412. )
  413. ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name))
  414. var err error
  415. if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
  416. framework.Failf("unable to create test secret %s: %v", secret.Name, err)
  417. }
  418. pod := &v1.Pod{
  419. ObjectMeta: metav1.ObjectMeta{
  420. Name: "pod-secrets-" + string(uuid.NewUUID()),
  421. Namespace: f.Namespace.Name,
  422. },
  423. Spec: v1.PodSpec{
  424. Volumes: []v1.Volume{
  425. {
  426. Name: volumeName,
  427. VolumeSource: v1.VolumeSource{
  428. Secret: &v1.SecretVolumeSource{
  429. SecretName: secretName,
  430. },
  431. },
  432. },
  433. },
  434. Containers: []v1.Container{
  435. {
  436. Name: "secret-volume-test",
  437. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  438. Args: []string{
  439. "--file_content=/etc/secret-volume/data-1",
  440. "--file_mode=/etc/secret-volume/data-1"},
  441. VolumeMounts: []v1.VolumeMount{
  442. {
  443. Name: volumeName,
  444. MountPath: volumeMountPath,
  445. },
  446. },
  447. },
  448. },
  449. RestartPolicy: v1.RestartPolicyNever,
  450. },
  451. }
  452. if defaultMode != nil {
  453. pod.Spec.Volumes[0].VolumeSource.Secret.DefaultMode = defaultMode
  454. }
  455. if fsGroup != nil || uid != nil {
  456. pod.Spec.SecurityContext = &v1.PodSecurityContext{
  457. FSGroup: fsGroup,
  458. RunAsUser: uid,
  459. }
  460. }
  461. fileModeRegexp := framework.GetFileModeRegex("/etc/secret-volume/data-1", defaultMode)
  462. expectedOutput := []string{
  463. "content of file \"/etc/secret-volume/data-1\": value-1",
  464. fileModeRegexp,
  465. }
  466. f.TestContainerOutputRegexp("consume secrets", pod, 0, expectedOutput)
  467. }
  468. func doSecretE2EWithMapping(f *framework.Framework, mode *int32) {
  469. var (
  470. name = "secret-test-map-" + string(uuid.NewUUID())
  471. volumeName = "secret-volume"
  472. volumeMountPath = "/etc/secret-volume"
  473. secret = secretForTest(f.Namespace.Name, name)
  474. )
  475. ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name))
  476. var err error
  477. if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
  478. framework.Failf("unable to create test secret %s: %v", secret.Name, err)
  479. }
  480. pod := &v1.Pod{
  481. ObjectMeta: metav1.ObjectMeta{
  482. Name: "pod-secrets-" + string(uuid.NewUUID()),
  483. },
  484. Spec: v1.PodSpec{
  485. Volumes: []v1.Volume{
  486. {
  487. Name: volumeName,
  488. VolumeSource: v1.VolumeSource{
  489. Secret: &v1.SecretVolumeSource{
  490. SecretName: name,
  491. Items: []v1.KeyToPath{
  492. {
  493. Key: "data-1",
  494. Path: "new-path-data-1",
  495. },
  496. },
  497. },
  498. },
  499. },
  500. },
  501. Containers: []v1.Container{
  502. {
  503. Name: "secret-volume-test",
  504. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  505. Args: []string{
  506. "--file_content=/etc/secret-volume/new-path-data-1",
  507. "--file_mode=/etc/secret-volume/new-path-data-1"},
  508. VolumeMounts: []v1.VolumeMount{
  509. {
  510. Name: volumeName,
  511. MountPath: volumeMountPath,
  512. },
  513. },
  514. },
  515. },
  516. RestartPolicy: v1.RestartPolicyNever,
  517. },
  518. }
  519. if mode != nil {
  520. pod.Spec.Volumes[0].VolumeSource.Secret.Items[0].Mode = mode
  521. }
  522. fileModeRegexp := framework.GetFileModeRegex("/etc/secret-volume/new-path-data-1", mode)
  523. expectedOutput := []string{
  524. "content of file \"/etc/secret-volume/new-path-data-1\": value-1",
  525. fileModeRegexp,
  526. }
  527. f.TestContainerOutputRegexp("consume secrets", pod, 0, expectedOutput)
  528. }
  529. func createNonOptionalSecretPod(f *framework.Framework, volumeMountPath, podName string) error {
  530. podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet)
  531. containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds()))
  532. falseValue := false
  533. createName := "s-test-opt-create-" + string(uuid.NewUUID())
  534. createContainerName := "creates-volume-test"
  535. createVolumeName := "creates-volume"
  536. // creating a pod without secret object created, by mentioning the secret volume source reference name
  537. pod := &v1.Pod{
  538. ObjectMeta: metav1.ObjectMeta{
  539. Name: podName,
  540. },
  541. Spec: v1.PodSpec{
  542. Volumes: []v1.Volume{
  543. {
  544. Name: createVolumeName,
  545. VolumeSource: v1.VolumeSource{
  546. Secret: &v1.SecretVolumeSource{
  547. SecretName: createName,
  548. Optional: &falseValue,
  549. },
  550. },
  551. },
  552. },
  553. Containers: []v1.Container{
  554. {
  555. Name: createContainerName,
  556. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  557. Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"},
  558. VolumeMounts: []v1.VolumeMount{
  559. {
  560. Name: createVolumeName,
  561. MountPath: path.Join(volumeMountPath, "create"),
  562. ReadOnly: true,
  563. },
  564. },
  565. },
  566. },
  567. RestartPolicy: v1.RestartPolicyNever,
  568. },
  569. }
  570. ginkgo.By("Creating the pod")
  571. pod = f.PodClient().Create(pod)
  572. return f.WaitForPodRunning(pod.Name)
  573. }
  574. func createNonOptionalSecretPodWithSecret(f *framework.Framework, volumeMountPath, podName string) error {
  575. podLogTimeout := framework.GetPodSecretUpdateTimeout(f.ClientSet)
  576. containerTimeoutArg := fmt.Sprintf("--retry_time=%v", int(podLogTimeout.Seconds()))
  577. falseValue := false
  578. createName := "s-test-opt-create-" + string(uuid.NewUUID())
  579. createContainerName := "creates-volume-test"
  580. createVolumeName := "creates-volume"
  581. secret := secretForTest(f.Namespace.Name, createName)
  582. ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name))
  583. var err error
  584. if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
  585. framework.Failf("unable to create test secret %s: %v", secret.Name, err)
  586. }
  587. // creating a pod with secret object, with the key which is not present in secret object.
  588. pod := &v1.Pod{
  589. ObjectMeta: metav1.ObjectMeta{
  590. Name: podName,
  591. },
  592. Spec: v1.PodSpec{
  593. Volumes: []v1.Volume{
  594. {
  595. Name: createVolumeName,
  596. VolumeSource: v1.VolumeSource{
  597. Secret: &v1.SecretVolumeSource{
  598. SecretName: createName,
  599. Items: []v1.KeyToPath{
  600. {
  601. Key: "data_4",
  602. Path: "value-4\n",
  603. },
  604. },
  605. Optional: &falseValue,
  606. },
  607. },
  608. },
  609. },
  610. Containers: []v1.Container{
  611. {
  612. Name: createContainerName,
  613. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  614. Command: []string{"/mounttest", "--break_on_expected_content=false", containerTimeoutArg, "--file_content_in_loop=/etc/secret-volumes/create/data-1"},
  615. VolumeMounts: []v1.VolumeMount{
  616. {
  617. Name: createVolumeName,
  618. MountPath: path.Join(volumeMountPath, "create"),
  619. ReadOnly: true,
  620. },
  621. },
  622. },
  623. },
  624. RestartPolicy: v1.RestartPolicyNever,
  625. },
  626. }
  627. ginkgo.By("Creating the pod")
  628. pod = f.PodClient().Create(pod)
  629. return f.WaitForPodRunning(pod.Name)
  630. }