diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2021-03-29 17:10:25 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-29 17:10:25 +0000 |
commit | b7bb8a1fb942a0568a1ff257798702bd0cde5d37 (patch) | |
tree | 35c403f30e1b4f1b98646b26b9192a2d58b4ede0 /pkg/specgen/generate/kube | |
parent | bb3e8a4d13d3ffc2ff6f57b0509febcb4ed0c17a (diff) | |
parent | c59eb6f12b2e53819ef0c1ff561cc0df125398b2 (diff) | |
download | podman-b7bb8a1fb942a0568a1ff257798702bd0cde5d37.tar.gz podman-b7bb8a1fb942a0568a1ff257798702bd0cde5d37.tar.bz2 podman-b7bb8a1fb942a0568a1ff257798702bd0cde5d37.zip |
Merge pull request #9842 from AlbanBedel/play-kube-env-from-secrets
Add support for env from secrets in play kube
Diffstat (limited to 'pkg/specgen/generate/kube')
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 100 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/play_test.go | 359 |
2 files changed, 424 insertions, 35 deletions
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index d61c8bd19..31ed3fd7c 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -2,11 +2,13 @@ package kube import ( "context" + "encoding/json" "fmt" "net" "strings" "github.com/containers/common/pkg/parse" + "github.com/containers/common/pkg/secrets" "github.com/containers/podman/v3/libpod/image" ann "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/specgen" @@ -94,6 +96,8 @@ type CtrSpecGenOptions struct { RestartPolicy string // NetNSIsHost tells the container to use the host netns NetNSIsHost bool + // SecretManager to access the secrets + SecretsManager *secrets.SecretsManager } func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { @@ -210,12 +214,18 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } for _, env := range opts.Container.Env { - value := envVarValue(env, opts.ConfigMaps) + value, err := envVarValue(env, opts) + if err != nil { + return nil, err + } envs[env.Name] = value } for _, envFrom := range opts.Container.EnvFrom { - cmEnvs := envVarsFromConfigMap(envFrom, opts.ConfigMaps) + cmEnvs, err := envVarsFrom(envFrom, opts) + if err != nil { + return nil, err + } for k, v := range cmEnvs { envs[k] = v @@ -325,40 +335,96 @@ func quantityToInt64(quantity *resource.Quantity) (int64, error) { return 0, errors.Errorf("Quantity cannot be represented as int64: %v", quantity) } -// envVarsFromConfigMap returns all key-value pairs as env vars from a configMap that matches the envFrom setting of a container -func envVarsFromConfigMap(envFrom v1.EnvFromSource, configMaps []v1.ConfigMap) map[string]string { +// read a k8s secret in JSON format from the secret manager +func k8sSecretFromSecretManager(name string, secretsManager *secrets.SecretsManager) (map[string][]byte, error) { + _, jsonSecret, err := secretsManager.LookupSecretData(name) + if err != nil { + return nil, err + } + + var secrets map[string][]byte + if err := json.Unmarshal(jsonSecret, &secrets); err != nil { + return nil, errors.Errorf("Secret %v is not valid JSON: %v", name, err) + } + return secrets, nil +} + +// envVarsFrom returns all key-value pairs as env vars from a configMap or secret that matches the envFrom setting of a container +func envVarsFrom(envFrom v1.EnvFromSource, opts *CtrSpecGenOptions) (map[string]string, error) { envs := map[string]string{} if envFrom.ConfigMapRef != nil { - cmName := envFrom.ConfigMapRef.Name + cmRef := envFrom.ConfigMapRef + err := errors.Errorf("Configmap %v not found", cmRef.Name) - for _, c := range configMaps { - if cmName == c.Name { + for _, c := range opts.ConfigMaps { + if cmRef.Name == c.Name { envs = c.Data + err = nil break } } + + if err != nil && (cmRef.Optional == nil || !*cmRef.Optional) { + return nil, err + } } - return envs + if envFrom.SecretRef != nil { + secRef := envFrom.SecretRef + secret, err := k8sSecretFromSecretManager(secRef.Name, opts.SecretsManager) + if err == nil { + for k, v := range secret { + envs[k] = string(v) + } + } else if secRef.Optional == nil || !*secRef.Optional { + return nil, err + } + } + + return envs, nil } // envVarValue returns the environment variable value configured within the container's env setting. -// It gets the value from a configMap if specified, otherwise returns env.Value -func envVarValue(env v1.EnvVar, configMaps []v1.ConfigMap) string { - for _, c := range configMaps { - if env.ValueFrom != nil { - if env.ValueFrom.ConfigMapKeyRef != nil { - if env.ValueFrom.ConfigMapKeyRef.Name == c.Name { - if value, ok := c.Data[env.ValueFrom.ConfigMapKeyRef.Key]; ok { - return value +// It gets the value from a configMap or secret if specified, otherwise returns env.Value +func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (string, error) { + if env.ValueFrom != nil { + if env.ValueFrom.ConfigMapKeyRef != nil { + cmKeyRef := env.ValueFrom.ConfigMapKeyRef + err := errors.Errorf("Cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name) + + for _, c := range opts.ConfigMaps { + if cmKeyRef.Name == c.Name { + if value, ok := c.Data[cmKeyRef.Key]; ok { + return value, nil } + err = errors.Errorf("Cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name) + break + } + } + if cmKeyRef.Optional == nil || !*cmKeyRef.Optional { + return "", err + } + return "", nil + } + + if env.ValueFrom.SecretKeyRef != nil { + secKeyRef := env.ValueFrom.SecretKeyRef + secret, err := k8sSecretFromSecretManager(secKeyRef.Name, opts.SecretsManager) + if err == nil { + if val, ok := secret[secKeyRef.Key]; ok { + return string(val), nil } + err = errors.Errorf("Secret %v has not %v key", secKeyRef.Name, secKeyRef.Key) + } + if secKeyRef.Optional == nil || !*secKeyRef.Optional { + return "", errors.Errorf("Cannot set env %v: %v", env.Name, err) } + return "", nil } } - return env.Value + return env.Value, nil } // getPodPorts converts a slice of kube container descriptions to an diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go index 148540e9f..f714826f0 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -1,19 +1,49 @@ package kube import ( + "encoding/json" + "io/ioutil" + "os" "testing" + "github.com/containers/common/pkg/secrets" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestEnvVarsFromConfigMap(t *testing.T) { +func createSecrets(t *testing.T, d string) *secrets.SecretsManager { + secretsManager, err := secrets.NewManager(d) + assert.NoError(t, err) + + driver := "file" + driverOpts := map[string]string{ + "path": d, + } + + for _, s := range k8sSecrets { + data, err := json.Marshal(s.Data) + assert.NoError(t, err) + + _, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, driverOpts) + assert.NoError(t, err) + } + + return secretsManager +} + +func TestEnvVarsFrom(t *testing.T) { + d, err := ioutil.TempDir("", "secrets") + assert.NoError(t, err) + defer os.RemoveAll(d) + secretsManager := createSecrets(t, d) + tests := []struct { - name string - envFrom v1.EnvFromSource - configMapList []v1.ConfigMap - expected map[string]string + name string + envFrom v1.EnvFromSource + options CtrSpecGenOptions + succeed bool + expected map[string]string }{ { "ConfigMapExists", @@ -24,7 +54,10 @@ func TestEnvVarsFromConfigMap(t *testing.T) { }, }, }, - configMapList, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + true, map[string]string{ "myvar": "foo", }, @@ -38,7 +71,26 @@ func TestEnvVarsFromConfigMap(t *testing.T) { }, }, }, - configMapList, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + false, + nil, + }, + { + "OptionalConfigMapDoesNotExist", + v1.EnvFromSource{ + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Optional: &optional, + }, + }, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + true, map[string]string{}, }, { @@ -50,7 +102,74 @@ func TestEnvVarsFromConfigMap(t *testing.T) { }, }, }, - []v1.ConfigMap{}, + CtrSpecGenOptions{ + ConfigMaps: []v1.ConfigMap{}, + }, + false, + nil, + }, + { + "OptionalEmptyConfigMapList", + v1.EnvFromSource{ + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Optional: &optional, + }, + }, + CtrSpecGenOptions{ + ConfigMaps: []v1.ConfigMap{}, + }, + true, + map[string]string{}, + }, + { + "SecretExists", + v1.EnvFromSource{ + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + true, + map[string]string{ + "myvar": "foo", + }, + }, + { + "SecretDoesNotExist", + v1.EnvFromSource{ + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + false, + nil, + }, + { + "OptionalSecretDoesNotExist", + v1.EnvFromSource{ + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Optional: &optional, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + true, map[string]string{}, }, } @@ -58,18 +177,25 @@ func TestEnvVarsFromConfigMap(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - result := envVarsFromConfigMap(test.envFrom, test.configMapList) + result, err := envVarsFrom(test.envFrom, &test.options) + assert.Equal(t, err == nil, test.succeed) assert.Equal(t, test.expected, result) }) } } func TestEnvVarValue(t *testing.T) { + d, err := ioutil.TempDir("", "secrets") + assert.NoError(t, err) + defer os.RemoveAll(d) + secretsManager := createSecrets(t, d) + tests := []struct { - name string - envVar v1.EnvVar - configMapList []v1.ConfigMap - expected string + name string + envVar v1.EnvVar + options CtrSpecGenOptions + succeed bool + expected string }{ { "ConfigMapExists", @@ -84,7 +210,10 @@ func TestEnvVarValue(t *testing.T) { }, }, }, - configMapList, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + true, "foo", }, { @@ -100,7 +229,30 @@ func TestEnvVarValue(t *testing.T) { }, }, }, - configMapList, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + false, + "", + }, + { + "OptionalContainerKeyDoesNotExistInConfigMap", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "doesnotexist", + Optional: &optional, + }, + }, + }, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + true, "", }, { @@ -116,7 +268,30 @@ func TestEnvVarValue(t *testing.T) { }, }, }, - configMapList, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + false, + "", + }, + { + "OptionalConfigMapDoesNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Key: "myvar", + Optional: &optional, + }, + }, + }, + CtrSpecGenOptions{ + ConfigMaps: configMapList, + }, + true, "", }, { @@ -132,7 +307,127 @@ func TestEnvVarValue(t *testing.T) { }, }, }, - []v1.ConfigMap{}, + CtrSpecGenOptions{ + ConfigMaps: []v1.ConfigMap{}, + }, + false, + "", + }, + { + "OptionalEmptyConfigMapList", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "myvar", + Optional: &optional, + }, + }, + }, + CtrSpecGenOptions{ + ConfigMaps: []v1.ConfigMap{}, + }, + true, + "", + }, + { + "SecretExists", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "myvar", + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + true, + "foo", + }, + { + "ContainerKeyDoesNotExistInSecret", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "doesnotexist", + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + false, + "", + }, + { + "OptionalContainerKeyDoesNotExistInSecret", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "doesnotexist", + Optional: &optional, + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + true, + "", + }, + { + "SecretDoesNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Key: "myvar", + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + false, + "", + }, + { + "OptionalSecretDoesNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Key: "myvar", + Optional: &optional, + }, + }, + }, + CtrSpecGenOptions{ + SecretsManager: secretsManager, + }, + true, "", }, } @@ -140,7 +435,8 @@ func TestEnvVarValue(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - result := envVarValue(test.envVar, test.configMapList) + result, err := envVarValue(test.envVar, &test.options) + assert.Equal(t, err == nil, test.succeed) assert.Equal(t, test.expected, result) }) } @@ -170,3 +466,30 @@ var configMapList = []v1.ConfigMap{ }, }, } + +var optional = true + +var k8sSecrets = []v1.Secret{ + { + TypeMeta: v12.TypeMeta{ + Kind: "Secret", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "bar", + }, + Data: map[string][]byte{ + "myvar": []byte("bar"), + }, + }, + { + TypeMeta: v12.TypeMeta{ + Kind: "Secret", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + Data: map[string][]byte{ + "myvar": []byte("foo"), + }, + }, +} |