From 2ceab119478502fd06703036bc79953ed6f37f74 Mon Sep 17 00:00:00 2001 From: Yaron Dayagi Date: Sun, 30 Jan 2022 20:45:30 +0200 Subject: play kube envVar.valueFrom.fieldRef add support for env vars values from pod spec fields see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core relates to issue https://github.com/containers/podman/issues/12756 Signed-off-by: Yaron Dayagi --- pkg/domain/infra/abi/play.go | 10 ++ pkg/specgen/generate/kube/kube.go | 61 +++++++++-- pkg/specgen/generate/kube/play_test.go | 194 ++++++++++++++++++++++++++++++--- 3 files changed, 241 insertions(+), 24 deletions(-) (limited to 'pkg') diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 86a60e92d..cad8c4609 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -365,6 +365,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } + + for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels + labels[k] = v + } + specgenOpts := kube.CtrSpecGenOptions{ Annotations: annotations, Container: initCtr, @@ -405,7 +410,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } + for k, v := range podSpec.PodSpecGen.Labels { // add podYAML labels + labels[k] = v + } + specgenOpts := kube.CtrSpecGenOptions{ + Annotations: annotations, Container: container, Image: pulledImage, Volumes: volumes, diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 2fd149b49..beaec1135 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net" + "regexp" "strings" "time" @@ -291,9 +292,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener return nil, err } - // Only set the env if the value is not "" - if value != "" { - envs[env.Name] = value + // Only set the env if the value is not nil + if value != nil { + envs[env.Name] = *value } } for _, envFrom := range opts.Container.EnvFrom { @@ -609,7 +610,7 @@ func envVarsFrom(envFrom v1.EnvFromSource, opts *CtrSpecGenOptions) (map[string] // envVarValue returns the environment variable value configured within the container's env setting. // It gets the value from a configMap or secret if specified, otherwise returns env.Value -func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (string, error) { +func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { if env.ValueFrom != nil { if env.ValueFrom.ConfigMapKeyRef != nil { cmKeyRef := env.ValueFrom.ConfigMapKeyRef @@ -618,16 +619,16 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (string, error) { for _, c := range opts.ConfigMaps { if cmKeyRef.Name == c.Name { if value, ok := c.Data[cmKeyRef.Key]; ok { - return value, nil + 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, err } - return "", nil + return nil, nil } if env.ValueFrom.SecretKeyRef != nil { @@ -635,18 +636,56 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (string, error) { secret, err := k8sSecretFromSecretManager(secKeyRef.Name, opts.SecretsManager) if err == nil { if val, ok := secret[secKeyRef.Key]; ok { - return string(val), nil + value := string(val) + return &value, 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, errors.Errorf("Cannot set env %v: %v", env.Name, err) } - return "", nil + return nil, nil + } + + if env.ValueFrom.FieldRef != nil { + return envVarValueFieldRef(env, opts) } } - return env.Value, nil + return &env.Value, nil +} + +func envVarValueFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { + fieldRef := env.ValueFrom.FieldRef + + fieldPathLabelPattern := `^metadata.labels\['(.+)'\]$` + fieldPathLabelRegex := regexp.MustCompile(fieldPathLabelPattern) + fieldPathAnnotationPattern := `^metadata.annotations\['(.+)'\]$` + fieldPathAnnotationRegex := regexp.MustCompile(fieldPathAnnotationPattern) + + fieldPath := fieldRef.FieldPath + + if fieldPath == "metadata.name" { + return &opts.PodName, nil + } + if fieldPath == "metadata.uid" { + return &opts.PodID, nil + } + fieldPathMatches := fieldPathLabelRegex.FindStringSubmatch(fieldPath) + if len(fieldPathMatches) == 2 { // 1 for entire regex and 1 for subexp + labelValue := opts.Labels[fieldPathMatches[1]] // not existent label is OK + return &labelValue, nil + } + fieldPathMatches = fieldPathAnnotationRegex.FindStringSubmatch(fieldPath) + if len(fieldPathMatches) == 2 { // 1 for entire regex and 1 for subexp + annotationValue := opts.Annotations[fieldPathMatches[1]] // not existent annotation is OK + return &annotationValue, nil + } + + return nil, errors.Errorf( + "Can not set env %v. Reason: fieldPath %v is either not valid or not supported", + env.Name, fieldPath, + ) } // 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 f714826f0..2ac268c79 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -189,13 +189,15 @@ func TestEnvVarValue(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(d) secretsManager := createSecrets(t, d) + value := "foo" + emptyValue := "" tests := []struct { name string envVar v1.EnvVar options CtrSpecGenOptions succeed bool - expected string + expected *string }{ { "ConfigMapExists", @@ -214,7 +216,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - "foo", + &value, }, { "ContainerKeyDoesNotExistInConfigMap", @@ -233,7 +235,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, false, - "", + nil, }, { "OptionalContainerKeyDoesNotExistInConfigMap", @@ -253,7 +255,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - "", + nil, }, { "ConfigMapDoesNotExist", @@ -272,7 +274,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, false, - "", + nil, }, { "OptionalConfigMapDoesNotExist", @@ -292,7 +294,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - "", + nil, }, { "EmptyConfigMapList", @@ -311,7 +313,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: []v1.ConfigMap{}, }, false, - "", + nil, }, { "OptionalEmptyConfigMapList", @@ -331,7 +333,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: []v1.ConfigMap{}, }, true, - "", + nil, }, { "SecretExists", @@ -350,7 +352,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - "foo", + &value, }, { "ContainerKeyDoesNotExistInSecret", @@ -369,7 +371,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, false, - "", + nil, }, { "OptionalContainerKeyDoesNotExistInSecret", @@ -389,7 +391,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - "", + nil, }, { "SecretDoesNotExist", @@ -408,7 +410,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, false, - "", + nil, }, { "OptionalSecretDoesNotExist", @@ -428,7 +430,173 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - "", + nil, + }, + { + "FieldRefMetadataName", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + CtrSpecGenOptions{ + PodName: value, + }, + true, + &value, + }, + { + "FieldRefMetadataUID", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.uid", + }, + }, + }, + CtrSpecGenOptions{ + PodID: value, + }, + true, + &value, + }, + { + "FieldRefMetadataLabelsExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.labels['label']", + }, + }, + }, + CtrSpecGenOptions{ + Labels: map[string]string{"label": value}, + }, + true, + &value, + }, + { + "FieldRefMetadataLabelsEmpty", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.labels['label']", + }, + }, + }, + CtrSpecGenOptions{ + Labels: map[string]string{"label": ""}, + }, + true, + &emptyValue, + }, + { + "FieldRefMetadataLabelsNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.labels['label']", + }, + }, + }, + CtrSpecGenOptions{}, + true, + &emptyValue, + }, + { + "FieldRefMetadataAnnotationsExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.annotations['annotation']", + }, + }, + }, + CtrSpecGenOptions{ + Annotations: map[string]string{"annotation": value}, + }, + true, + &value, + }, + { + "FieldRefMetadataAnnotationsEmpty", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.annotations['annotation']", + }, + }, + }, + CtrSpecGenOptions{ + Annotations: map[string]string{"annotation": ""}, + }, + true, + &emptyValue, + }, + { + "FieldRefMetadataAnnotationsNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.annotations['annotation']", + }, + }, + }, + CtrSpecGenOptions{}, + true, + &emptyValue, + }, + { + "FieldRefInvalid1", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.annotations['annotation]", + }, + }, + }, + CtrSpecGenOptions{}, + false, + nil, + }, + { + "FieldRefInvalid2", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.dummy['annotation']", + }, + }, + }, + CtrSpecGenOptions{}, + false, + nil, + }, + { + "FieldRefNotSupported", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + CtrSpecGenOptions{}, + false, + nil, }, } -- cgit v1.2.3-54-g00ecf