Use secret.items to create volume mounts if present

If the kube yaml volumes has secret.items set, then use
the values from that to set up the paths inside the container
similar to what we do for configMap.
Add tests for this as well.

Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
This commit is contained in:
Urvashi Mohnani
2023-03-30 15:50:55 -04:00
parent 3e44a7afed
commit 8b6a7c1826
2 changed files with 155 additions and 8 deletions

View File

@ -147,21 +147,32 @@ func VolumeFromSecret(secretSource *v1.SecretVolumeSource, secretsManager *secre
return nil, err return nil, err
} }
data := &v1.Secret{} secret := &v1.Secret{}
err = yaml.Unmarshal(secretByte, data) err = yaml.Unmarshal(secretByte, secret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 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 // add key: value pairs to the items array
for key, entry := range data.Data { for key, entry := range secret.Data {
kv.Items[key] = entry kv.Items[key] = entry
} }
for key, entry := range data.StringData { for key, entry := range secret.StringData {
kv.Items[key] = []byte(entry) kv.Items[key] = []byte(entry)
} }
}
return kv, nil return kv, nil
} }

View File

@ -492,6 +492,19 @@ data:
{{ end }} {{ end }}
` `
var secretYamlTemplate = `
apiVersion: v1
kind: Secret
metadata:
name: {{ .Name }}
data:
{{ with .Data }}
{{ range $key, $value := . }}
{{ $key }}: {{ $value }}
{{ end }}
{{ end }}
`
var persistentVolumeClaimYamlTemplate = ` var persistentVolumeClaimYamlTemplate = `
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
@ -692,6 +705,18 @@ spec:
path: {{ .path }} path: {{ .path }}
{{- end }} {{- end }}
{{- 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 }} {{ end }}
{{ end }} {{ end }}
@ -1019,6 +1044,7 @@ var (
defaultVolName = "testVol" defaultVolName = "testVol"
defaultDeploymentName = "testDeployment" defaultDeploymentName = "testDeployment"
defaultConfigMapName = "testConfigMap" defaultConfigMapName = "testConfigMap"
defaultSecretName = "testSecret"
defaultPVCName = "testPVC" defaultPVCName = "testPVC"
seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`) seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`)
// CPU Period in ms // CPU Period in ms
@ -1041,6 +1067,8 @@ func getKubeYaml(kind string, object interface{}) (string, error) {
yamlTemplate = deploymentYamlTemplate yamlTemplate = deploymentYamlTemplate
case "persistentVolumeClaim": case "persistentVolumeClaim":
yamlTemplate = persistentVolumeClaimYamlTemplate yamlTemplate = persistentVolumeClaimYamlTemplate
case "secret":
yamlTemplate = secretYamlTemplate
default: default:
return "", fmt.Errorf("unsupported kubernetes kind") return "", fmt.Errorf("unsupported kubernetes kind")
} }
@ -1089,6 +1117,39 @@ func createSecret(podmanTest *PodmanTestIntegration, name string, value []byte)
Expect(secret).Should(Exit(0)) 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 // CM describes the options a kube yaml can be configured at configmap level
type CM struct { type CM struct {
Name string Name string
@ -1568,6 +1629,12 @@ type ConfigMap struct {
Optional bool Optional bool
} }
type SecretVol struct {
SecretName string
Items []map[string]string
Optional bool
}
type EmptyDir struct{} type EmptyDir struct{}
type Volume struct { type Volume struct {
@ -1577,6 +1644,7 @@ type Volume struct {
PersistentVolumeClaim PersistentVolumeClaim
ConfigMap ConfigMap
EmptyDir EmptyDir
SecretVol
} }
// getHostPathVolume takes a type and a location for a HostPath // 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 { func getEmptyDirVolume() *Volume {
return &Volume{ return &Volume{
VolumeType: "EmptyDir", VolumeType: "EmptyDir",
@ -4730,6 +4810,62 @@ ENV OPENJ9_JAVA_OPTIONS=%q
deleteAndTestSecret(podmanTest, "newsecret") 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() { It("podman play kube with disabled cgroup", func() {
SkipIfRunc(podmanTest, "Test not supported with runc, see issue #17436") SkipIfRunc(podmanTest, "Test not supported with runc, see issue #17436")
conffile := filepath.Join(podmanTest.TempDir, "container.conf") conffile := filepath.Join(podmanTest.TempDir, "container.conf")