diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index b87557280d..148490f76e 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -147,20 +147,31 @@ func VolumeFromSecret(secretSource *v1.SecretVolumeSource, secretsManager *secre return nil, err } - data := &v1.Secret{} + secret := &v1.Secret{} - err = yaml.Unmarshal(secretByte, data) + err = yaml.Unmarshal(secretByte, secret) if err != nil { return nil, err } - // add key: value pairs to the items array - for key, entry := range data.Data { - kv.Items[key] = entry - } + // If there are Items specified in the volumeSource, that overwrites the Data from the Secret + if len(secretSource.Items) > 0 { + for _, item := range secretSource.Items { + if val, ok := secret.Data[item.Key]; ok { + kv.Items[item.Path] = val + } else if val, ok := secret.StringData[item.Key]; ok { + kv.Items[item.Path] = []byte(val) + } + } + } else { + // add key: value pairs to the items array + for key, entry := range secret.Data { + kv.Items[key] = entry + } - for key, entry := range data.StringData { - kv.Items[key] = []byte(entry) + for key, entry := range secret.StringData { + kv.Items[key] = []byte(entry) + } } return kv, nil diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index b95456ceaa..1ffd196bfb 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -492,6 +492,19 @@ data: {{ end }} ` +var secretYamlTemplate = ` +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Name }} +data: +{{ with .Data }} + {{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{ end }} +{{ end }} +` + var persistentVolumeClaimYamlTemplate = ` apiVersion: v1 kind: PersistentVolumeClaim @@ -692,6 +705,18 @@ spec: path: {{ .path }} {{- end }} {{- end }} + {{- end }} + {{- if (eq .VolumeType "Secret") }} + secret: + secretName: {{ .SecretVol.SecretName }} + optional: {{ .SecretVol.Optional }} + {{- with .SecretVol.Items }} + items: + {{- range . }} + - key: {{ .key }} + path: {{ .path }} + {{- end }} + {{- end }} {{- end }} {{ end }} {{ end }} @@ -1019,6 +1044,7 @@ var ( defaultVolName = "testVol" defaultDeploymentName = "testDeployment" defaultConfigMapName = "testConfigMap" + defaultSecretName = "testSecret" defaultPVCName = "testPVC" seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`) // CPU Period in ms @@ -1041,6 +1067,8 @@ func getKubeYaml(kind string, object interface{}) (string, error) { yamlTemplate = deploymentYamlTemplate case "persistentVolumeClaim": yamlTemplate = persistentVolumeClaimYamlTemplate + case "secret": + yamlTemplate = secretYamlTemplate default: return "", fmt.Errorf("unsupported kubernetes kind") } @@ -1089,6 +1117,39 @@ func createSecret(podmanTest *PodmanTestIntegration, name string, value []byte) Expect(secret).Should(Exit(0)) } +// Secret describes the options a kube yaml can be configured at secret level +type Secret struct { + Name string + Data map[string]string +} + +func getSecret(options ...secretOption) *Secret { + secret := Secret{ + Name: defaultSecretName, + Data: map[string]string{}, + } + + for _, option := range options { + option(&secret) + } + + return &secret +} + +type secretOption func(*Secret) + +func withSecretName(name string) secretOption { + return func(secret *Secret) { + secret.Name = name + } +} + +func withSecretData(k, v string) secretOption { + return func(secret *Secret) { + secret.Data[k] = base64.StdEncoding.EncodeToString([]byte(v)) + } +} + // CM describes the options a kube yaml can be configured at configmap level type CM struct { Name string @@ -1568,6 +1629,12 @@ type ConfigMap struct { Optional bool } +type SecretVol struct { + SecretName string + Items []map[string]string + Optional bool +} + type EmptyDir struct{} type Volume struct { @@ -1577,6 +1644,7 @@ type Volume struct { PersistentVolumeClaim ConfigMap EmptyDir + SecretVol } // getHostPathVolume takes a type and a location for a HostPath @@ -1618,6 +1686,18 @@ func getConfigMapVolume(vName string, items []map[string]string, optional bool) } } +func getSecretVolume(vName string, items []map[string]string, optional bool) *Volume { + return &Volume{ + VolumeType: "Secret", + Name: defaultVolName, + SecretVol: SecretVol{ + SecretName: vName, + Items: items, + Optional: optional, + }, + } +} + func getEmptyDirVolume() *Volume { return &Volume{ VolumeType: "EmptyDir", @@ -4730,6 +4810,62 @@ ENV OPENJ9_JAVA_OPTIONS=%q deleteAndTestSecret(podmanTest, "newsecret") }) + It("podman play kube secret as volume with no items", func() { + volumeName := "secretVol" + secret := getSecret(withSecretName(volumeName), withSecretData("FOO", "testuser")) + secretYaml, err := getKubeYaml("secret", secret) + Expect(err).ToNot(HaveOccurred()) + + ctr := getCtr(withVolumeMount("/test", "", false), withImage(BB)) + pod := getPod(withVolume(getSecretVolume(volumeName, []map[string]string{}, false)), withCtr(ctr)) + podYaml, err := getKubeYaml("pod", pod) + Expect(err).ToNot(HaveOccurred()) + yamls := []string{secretYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).ToNot(HaveOccurred()) + + kube := podmanTest.Podman([]string{"kube", "play", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + secretData := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/FOO"}) + secretData.WaitWithDefaultTimeout() + Expect(secretData).Should(Exit(0)) + Expect(secretData.OutputToString()).To(Equal("testuser")) + }) + + It("podman play kube secret as volume with items", func() { + volumeName := "secretVol" + secret := getSecret(withSecretName(volumeName), withSecretData("FOO", "foobar")) + secretYaml, err := getKubeYaml("secret", secret) + Expect(err).ToNot(HaveOccurred()) + volumeContents := []map[string]string{{ + "key": "FOO", + "path": "BAR", + }} + + ctr := getCtr(withVolumeMount("/test", "", false), withImage(BB)) + pod := getPod(withVolume(getSecretVolume(volumeName, volumeContents, false)), withCtr(ctr)) + podYaml, err := getKubeYaml("pod", pod) + Expect(err).ToNot(HaveOccurred()) + yamls := []string{secretYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).ToNot(HaveOccurred()) + + kube := podmanTest.Podman([]string{"kube", "play", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + secretData := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/BAR"}) + secretData.WaitWithDefaultTimeout() + Expect(secretData).Should(Exit(0)) + Expect(secretData.OutputToString()).To(Equal("foobar")) + + secretData = podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/FOO"}) + secretData.WaitWithDefaultTimeout() + Expect(secretData).Should(Not(Exit(0))) + }) + It("podman play kube with disabled cgroup", func() { SkipIfRunc(podmanTest, "Test not supported with runc, see issue #17436") conffile := filepath.Join(podmanTest.TempDir, "container.conf")