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,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

View File

@ -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")