mirror of https://github.com/knative/func.git
src: envs: use `:` instead of `.` as separators (#394)
* src: envs: use `:` instead of `.` as separators Signed-off-by: Zbynek Roubalik <zroubali@redhat.com> * leftovers Signed-off-by: Zbynek Roubalik <zroubali@redhat.com>
This commit is contained in:
parent
54f60a2552
commit
76b5800c62
35
config.go
35
config.go
|
@ -16,6 +16,14 @@ import (
|
|||
// ConfigFile is the name of the config's serialized form.
|
||||
const ConfigFile = "func.yaml"
|
||||
|
||||
var (
|
||||
regWholeSecret = regexp.MustCompile(`^{{\s*secret:((?:\w|['-]\w)+)\s*}}$`)
|
||||
regKeyFromSecret = regexp.MustCompile(`^{{\s*secret:((?:\w|['-]\w)+):(\w+)\s*}}$`)
|
||||
regWholeConfigMap = regexp.MustCompile(`^{{\s*configMap:((?:\w|['-]\w)+)\s*}}$`)
|
||||
regKeyFromConfigMap = regexp.MustCompile(`^{{\s*configMap:((?:\w|['-]\w)+):(\w+)\s*}}$`)
|
||||
regLocalEnv = regexp.MustCompile(`^{{\s*env:(\w+)\s*}}$`)
|
||||
)
|
||||
|
||||
type Volumes []Volume
|
||||
type Volume struct {
|
||||
Secret *string `yaml:"secret,omitempty"`
|
||||
|
@ -234,31 +242,24 @@ func validateVolumes(volumes Volumes) (errors []string) {
|
|||
// - name: EXAMPLE1 # ENV directly from a value
|
||||
// value: value1
|
||||
// - name: EXAMPLE2 # ENV from the local ENV var
|
||||
// value: {{ env.MY_ENV }}
|
||||
// value: {{ env:MY_ENV }}
|
||||
// - name: EXAMPLE3
|
||||
// value: {{ secret.secretName.key }} # ENV from a key in secret
|
||||
// - value: {{ secret.secretName }} # all key-pair values from secret are set as ENV
|
||||
// value: {{ secret:secretName:key }} # ENV from a key in secret
|
||||
// - value: {{ secret:secretName }} # all key-pair values from secret are set as ENV
|
||||
// - name: EXAMPLE4
|
||||
// value: {{ configMap.configMapName.key }} # ENV from a key in configMap
|
||||
// - value: {{ configMap.configMapName }} # all key-pair values from configMap are set as ENV
|
||||
// value: {{ configMap:configMapName:key }} # ENV from a key in configMap
|
||||
// - value: {{ configMap:configMapName }} # all key-pair values from configMap are set as ENV
|
||||
func ValidateEnvs(envs Envs) (errors []string) {
|
||||
|
||||
// there could be '-' char in the secret/configMap name, but not in the key
|
||||
regWholeSecret := regexp.MustCompile(`^{{\s*secret\.(?:\w|['-]\w)+\s*}}$`)
|
||||
regKeyFromSecret := regexp.MustCompile(`^{{\s*secret\.(?:\w|['-]\w)+\.\w+\s*}}$`)
|
||||
regWholeConfigMap := regexp.MustCompile(`^{{\s*configMap\.(?:\w|['-]\w)+\s*}}$`)
|
||||
regKeyFromConfigMap := regexp.MustCompile(`^{{\s*configMap\.(?:\w|['-]\w)+\.\w+\s*}}$`)
|
||||
regLocalEnv := regexp.MustCompile(`^{{\s*env\.(\w+)\s*}}$`)
|
||||
|
||||
for i, env := range envs {
|
||||
if env.Name == nil && env.Value == nil {
|
||||
errors = append(errors, fmt.Sprintf("env entry #%d is not properly set", i))
|
||||
} else if env.Value == nil {
|
||||
errors = append(errors, fmt.Sprintf("env entry #%d is missing value field, only name '%s' is set", i, *env.Name))
|
||||
} else if env.Name == nil {
|
||||
// all key-pair values from secret are set as ENV; {{ secret.secretName }} or {{ configMap.configMapName }}
|
||||
// all key-pair values from secret are set as ENV; {{ secret:secretName }} or {{ configMap:configMapName }}
|
||||
if !regWholeSecret.MatchString(*env.Value) && !regWholeConfigMap.MatchString(*env.Value) {
|
||||
errors = append(errors, fmt.Sprintf("env entry #%d has invalid value field set, it has '%s', but allowed is only '{{ secret.secretName }}' or '{{ configMap.configMapName }}'",
|
||||
errors = append(errors, fmt.Sprintf("env entry #%d has invalid value field set, it has '%s', but allowed is only '{{ secret:secretName }}' or '{{ configMap:configMapName }}'",
|
||||
i, *env.Value))
|
||||
}
|
||||
} else {
|
||||
|
@ -268,13 +269,13 @@ func ValidateEnvs(envs Envs) (errors []string) {
|
|||
}
|
||||
|
||||
if strings.HasPrefix(*env.Value, "{{") {
|
||||
// ENV from the local ENV var; {{ env.MY_ENV }}
|
||||
// ENV from the local ENV var; {{ env:MY_ENV }}
|
||||
// or
|
||||
// ENV from a key in secret/configMap; {{ secret.secretName.key }} or {{ configMap.configMapName.key }}
|
||||
// ENV from a key in secret/configMap; {{ secret:secretName:key }} or {{ configMap:configMapName:key }}
|
||||
if !regLocalEnv.MatchString(*env.Value) && !regKeyFromSecret.MatchString(*env.Value) && !regKeyFromConfigMap.MatchString(*env.Value) {
|
||||
errors = append(errors,
|
||||
fmt.Sprintf(
|
||||
"env entry #%d with name '%s' has invalid value field set, it has '%s', but allowed is only '{{ env.MY_ENV }}', '{{ secret.secretName.key }}' or '{{ configMap.configMapName.key }}'",
|
||||
"env entry #%d with name '%s' has invalid value field set, it has '%s', but allowed is only '{{ env:MY_ENV }}', '{{ secret:secretName:key }}' or '{{ configMap:configMapName:key }}'",
|
||||
i, *env.Name, *env.Value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,28 +154,28 @@ func Test_validateEnvs(t *testing.T) {
|
|||
incorrectName := ",foo"
|
||||
incorrectName2 := ":foo"
|
||||
|
||||
valueLocalEnv := "{{ env.MY_ENV }}"
|
||||
valueLocalEnv2 := "{{ env.MY_ENV2 }}"
|
||||
valueLocalEnv3 := "{{env.MY_ENV3}}"
|
||||
valueLocalEnvIncorrect := "{{ envs.MY_ENV }}"
|
||||
valueLocalEnv := "{{ env:MY_ENV }}"
|
||||
valueLocalEnv2 := "{{ env:MY_ENV2 }}"
|
||||
valueLocalEnv3 := "{{env:MY_ENV3}}"
|
||||
valueLocalEnvIncorrect := "{{ envs:MY_ENV }}"
|
||||
valueLocalEnvIncorrect2 := "{{ MY_ENV }}"
|
||||
valueLocalEnvIncorrect3 := "{{env.MY_ENV}}foo"
|
||||
valueLocalEnvIncorrect3 := "{{env:MY_ENV}}foo"
|
||||
|
||||
valueSecretKey := "{{ secret.mysecret.key }}"
|
||||
valueSecretKey2 := "{{secret.my-secret.key }}"
|
||||
valueSecretKey3 := "{{secret.my-secret.key2}}"
|
||||
valueSecretKeyIncorrect := "{{ secret.my-secret.key.key }}"
|
||||
valueSecretKeyIncorrect2 := "{{ my-secret.key }}"
|
||||
valueSecretKeyIncorrect3 := "{{ secret.my-secret.key }}foo"
|
||||
valueConfigMapKey := "{{ configMap.myconfigmap.key }}"
|
||||
valueSecretKey := "{{ secret:mysecret:key }}"
|
||||
valueSecretKey2 := "{{secret:my-secret:key }}"
|
||||
valueSecretKey3 := "{{secret:my-secret:key2}}"
|
||||
valueSecretKeyIncorrect := "{{ secret:my-secret:key.key }}"
|
||||
valueSecretKeyIncorrect2 := "{{ my-secret:key }}"
|
||||
valueSecretKeyIncorrect3 := "{{ secret:my-secret:key }}foo"
|
||||
valueConfigMapKey := "{{ configMap:myconfigmap:key }}"
|
||||
|
||||
valueSecret := "{{ secret.my-secret }}"
|
||||
valueSecret2 := "{{ secret.mysecret }}"
|
||||
valueSecret3 := "{{ secret.mysecret}}"
|
||||
valueSecret := "{{ secret:my-secret }}"
|
||||
valueSecret2 := "{{ secret:mysecret }}"
|
||||
valueSecret3 := "{{ secret:mysecret}}"
|
||||
valueSecretIncorrect := "{{ my-secret }}"
|
||||
valueSecretIncorrect2 := "my-secret"
|
||||
valueSecretIncorrect3 := "{{ secret.my-secret }}foo"
|
||||
valueConfigMap := "{{ configMap.myconfigmap }}"
|
||||
valueSecretIncorrect3 := "{{ secret:my-secret }}foo"
|
||||
valueConfigMap := "{{ configMap:myconfigmap }}"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
@ -144,15 +144,15 @@ func (n *Runner) Run(ctx context.Context, f bosonFunc.Function) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// run command supports only ENV values in from FOO=bar or FOO={{ env.LOCAL_VALUE }}
|
||||
var evRegex = regexp.MustCompile(`^{{\s*(\w+)\s*.(\w+)\s*}}$`)
|
||||
// run command supports only ENV values in from FOO=bar or FOO={{ env:LOCAL_VALUE }}
|
||||
var evRegex = regexp.MustCompile(`^{{\s*(\w+)\s*:(\w+)\s*}}$`)
|
||||
|
||||
const (
|
||||
ctxIdx = 1
|
||||
valIdx = 2
|
||||
)
|
||||
|
||||
// processEnvValue returns only value for ENV variable, that is defined in form FOO=bar or FOO={{ env.LOCAL_VALUE }}
|
||||
// processEnvValue returns only value for ENV variable, that is defined in form FOO=bar or FOO={{ env:LOCAL_VALUE }}
|
||||
// if the value is correct, it is returned and the second return parameter is set to `true`
|
||||
// otherwise it is set to `false`
|
||||
// if the specified value is correct, but the required local variable is not set, error is returned as well
|
||||
|
|
|
@ -33,22 +33,22 @@ it's typically unnecessary to modify the `builder` field, using values from
|
|||
The `envs` field allows you to set environment variables that will be
|
||||
available to your function at runtime.
|
||||
1. Environment variable can be set directly from a value
|
||||
2. Environment variable can be set from a local environment value. Eg. `'{{ env.LOCAL_ENV_VALUE }}'`, for more details see [Local Environment Variables section](#local-environment-variables).
|
||||
3. Environment variable can be set from a key in a Kubernetes Secret or ConfigMap. This Secret/ConfigMap needs to be created before it is referenced in a function. Eg. `'{{ secret.mysecret.key }}'` where `mysecret` is the name of the Secret and `key` is the referenced key; or `{{ configMap.myconfigmap.key }}` where `myconfigmap` is the name of the ConfigMap and `key` is the referenced key.
|
||||
4. All key-value pairs from a Kubernetes Secret or ConfigMap will be set as environment variables. This Secret/ConfigMap needs to be created before it is referenced in a function. Eg. `'{{ secret.mysecret2 }}'` where `mysecret2` is the name of the Secret; or `{{ configMap.myconfigmap }}` where `myconfigmap` is the name of the ConfigMap.
|
||||
2. Environment variable can be set from a local environment value. Eg. `'{{ env:LOCAL_ENV_VALUE }}'`, for more details see [Local Environment Variables section](#local-environment-variables).
|
||||
3. Environment variable can be set from a key in a Kubernetes Secret or ConfigMap. This Secret/ConfigMap needs to be created before it is referenced in a function. Eg. `'{{ secret:mysecret:key }}'` where `mysecret` is the name of the Secret and `key` is the referenced key; or `{{ configMap:myconfigmap:key }}` where `myconfigmap` is the name of the ConfigMap and `key` is the referenced key.
|
||||
4. All key-value pairs from a Kubernetes Secret or ConfigMap will be set as environment variables. This Secret/ConfigMap needs to be created before it is referenced in a function. Eg. `'{{ secret:mysecret2 }}'` where `mysecret2` is the name of the Secret: or `{{ configMap:myconfigmap }}` where `myconfigmap` is the name of the ConfigMap.
|
||||
|
||||
```yaml
|
||||
envs:
|
||||
- name: EXAMPLE1 # (1) env variable directly from a value
|
||||
value: value
|
||||
- name: EXAMPLE2 # (2) env variable from a local environment value
|
||||
value: '{{ env.LOCAL_ENV_VALUE }}'
|
||||
value: '{{ env:LOCAL_ENV_VALUE }}'
|
||||
- name: EXAMPLE3 # (3) env variable from a key in Secret
|
||||
value: '{{ secret.mysecret.key }}'
|
||||
value: '{{ secret:mysecret:key }}'
|
||||
- name: EXAMPLE4 # (3) env variable from a key in ConfigMap
|
||||
value: '{{ configMap.myconfigmap.key }}'
|
||||
- value: '{{ secret.mysecret2 }}' # (4) all key-value pairs in Secret as env variables
|
||||
- value: '{{ configMap.myconfigmap2 }}' # (4) all key-value pairs in ConfigMap as env variables
|
||||
value: '{{ configMap:myconfigmap:key }}'
|
||||
- value: '{{ secret:mysecret2 }}' # (4) all key-value pairs in Secret as env variables
|
||||
- value: '{{ configMap:myconfigmap2 }}' # (4) all key-value pairs in ConfigMap as env variables
|
||||
```
|
||||
|
||||
### `volumes`
|
||||
|
@ -120,10 +120,10 @@ variable available in the local environment. For example, if I would like
|
|||
to avoid storing sensitive information such as an API key in my function
|
||||
configuration, I may have this value set from the local environment. To do
|
||||
this, prefix the local environment variable with `{{` and `}}` and prefix
|
||||
the name with `env.`. For example:
|
||||
the name with `env:`. For example:
|
||||
|
||||
```yaml
|
||||
envs:
|
||||
- name: API_KEY
|
||||
value: '{{ env.API_KEY }}'
|
||||
value: '{{ env:API_KEY }}'
|
||||
```
|
||||
|
|
|
@ -245,13 +245,13 @@ func updateService(image string, newEnv []corev1.EnvVar, newEnvFrom []corev1.Env
|
|||
// - name: EXAMPLE1 # ENV directly from a value
|
||||
// value: value1
|
||||
// - name: EXAMPLE2 # ENV from the local ENV var
|
||||
// value: {{ env.MY_ENV }}
|
||||
// value: {{ env:MY_ENV }}
|
||||
// - name: EXAMPLE3
|
||||
// value: {{ secret.example-secret.key }} # ENV from a key in Secret
|
||||
// - value: {{ secret.example-secret }} # all ENVs from Secret
|
||||
// value: {{ secret:example-secret:key }} # ENV from a key in Secret
|
||||
// - value: {{ secret:example-secret }} # all ENVs from Secret
|
||||
// - name: EXAMPLE4
|
||||
// value: {{ configMap.configMapName.key }} # ENV from a key in ConfigMap
|
||||
// - value: {{ configMap.configMapName }} # all key-pair values from ConfigMap are set as ENV
|
||||
// value: {{ configMap:configMapName:key }} # ENV from a key in ConfigMap
|
||||
// - value: {{ configMap:configMapName }} # all key-pair values from ConfigMap are set as ENV
|
||||
func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.String) ([]corev1.EnvVar, []corev1.EnvFromSource, error) {
|
||||
|
||||
envVars := []corev1.EnvVar{{Name: "BUILT", Value: time.Now().Format("20060102T150405")}}
|
||||
|
@ -259,7 +259,7 @@ func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.Str
|
|||
|
||||
for _, env := range envs {
|
||||
if env.Name == nil && env.Value != nil {
|
||||
// all key-pair values from secret/configMap are set as ENV, eg. {{ secret.secretName }} or {{ configMap.configMapName }}
|
||||
// all key-pair values from secret/configMap are set as ENV, eg. {{ secret:secretName }} or {{ configMap:configMapName }}
|
||||
if strings.HasPrefix(*env.Value, "{{") {
|
||||
envFromSource, err := createEnvFromSource(*env.Value, referencedSecrets, referencedConfigMaps)
|
||||
if err != nil {
|
||||
|
@ -270,9 +270,9 @@ func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.Str
|
|||
}
|
||||
} else if env.Name != nil && env.Value != nil {
|
||||
if strings.HasPrefix(*env.Value, "{{") {
|
||||
slices := strings.Split(strings.Trim(*env.Value, "{} "), ".")
|
||||
slices := strings.Split(strings.Trim(*env.Value, "{} "), ":")
|
||||
if len(slices) == 3 {
|
||||
// ENV from a key in secret/configMap, eg. FOO={{ secret.secretName.key }} FOO={{ configMap.configMapName.key }}
|
||||
// ENV from a key in secret/configMap, eg. FOO={{ secret:secretName:key }} FOO={{ configMap:configMapName.key }}
|
||||
valueFrom, err := createEnvVarSource(slices, referencedSecrets, referencedConfigMaps)
|
||||
envVars = append(envVars, corev1.EnvVar{Name: *env.Name, ValueFrom: valueFrom})
|
||||
if err != nil {
|
||||
|
@ -280,7 +280,7 @@ func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.Str
|
|||
}
|
||||
continue
|
||||
} else if len(slices) == 2 {
|
||||
// ENV from the local ENV var, eg. FOO={{ env.LOCAL_ENV }}
|
||||
// ENV from the local ENV var, eg. FOO={{ env:LOCAL_ENV }}
|
||||
localValue, err := processLocalEnvValue(*env.Value)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -301,9 +301,9 @@ func processEnvs(envs fn.Envs, referencedSecrets, referencedConfigMaps *sets.Str
|
|||
}
|
||||
|
||||
func createEnvFromSource(value string, referencedSecrets, referencedConfigMaps *sets.String) (*corev1.EnvFromSource, error) {
|
||||
slices := strings.Split(strings.Trim(value, "{} "), ".")
|
||||
slices := strings.Split(strings.Trim(value, "{} "), ":")
|
||||
if len(slices) != 2 {
|
||||
return nil, fmt.Errorf("env requires a value in form \"resourceType.name\" where \"resourceType\" can be one of \"configMap\" or \"secret\"; got %q", slices)
|
||||
return nil, fmt.Errorf("env requires a value in form \"resourceType:name\" where \"resourceType\" can be one of \"configMap\" or \"secret\"; got %q", slices)
|
||||
}
|
||||
|
||||
envVarSource := corev1.EnvFromSource{}
|
||||
|
@ -334,7 +334,7 @@ func createEnvFromSource(value string, referencedSecrets, referencedConfigMaps *
|
|||
referencedSecrets.Insert(sourceName)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported env source type \"%q\"; supported source types are \"configMap\" or \"secret\"", slices[0])
|
||||
return nil, fmt.Errorf("unsupported env source type %q; supported source types are \"configMap\" or \"secret\"", slices[0])
|
||||
}
|
||||
|
||||
if len(sourceName) == 0 {
|
||||
|
@ -347,7 +347,7 @@ func createEnvFromSource(value string, referencedSecrets, referencedConfigMaps *
|
|||
func createEnvVarSource(slices []string, referencedSecrets, referencedConfigMaps *sets.String) (*corev1.EnvVarSource, error) {
|
||||
|
||||
if len(slices) != 3 {
|
||||
return nil, fmt.Errorf("env requires a value in form \"resourceType.name.key\" where \"resourceType\" can be one of \"configMap\" or \"secret\"; got %q", slices)
|
||||
return nil, fmt.Errorf("env requires a value in form \"resourceType:name:key\" where \"resourceType\" can be one of \"configMap\" or \"secret\"; got %q", slices)
|
||||
}
|
||||
|
||||
envVarSource := corev1.EnvVarSource{}
|
||||
|
@ -382,7 +382,7 @@ func createEnvVarSource(slices []string, referencedSecrets, referencedConfigMaps
|
|||
referencedSecrets.Insert(sourceName)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported env source type \"%q\"; supported source types are \"configMap\" or \"secret\"", slices[0])
|
||||
return nil, fmt.Errorf("unsupported env source type %q; supported source types are \"configMap\" or \"secret\"", slices[0])
|
||||
}
|
||||
|
||||
if len(sourceName) == 0 {
|
||||
|
@ -390,13 +390,13 @@ func createEnvVarSource(slices []string, referencedSecrets, referencedConfigMaps
|
|||
}
|
||||
|
||||
if len(sourceKey) == 0 {
|
||||
return nil, fmt.Errorf("the key referenced by resource %s \"%s\" cannot be an empty string", sourceType, sourceName)
|
||||
return nil, fmt.Errorf("the key referenced by resource %s %q cannot be an empty string", sourceType, sourceName)
|
||||
}
|
||||
|
||||
return &envVarSource, nil
|
||||
}
|
||||
|
||||
var evRegex = regexp.MustCompile(`^{{\s*(\w+)\s*.(\w+)\s*}}$`)
|
||||
var evRegex = regexp.MustCompile(`^{{\s*(\w+)\s*:(\w+)\s*}}$`)
|
||||
|
||||
const (
|
||||
ctxIdx = 1
|
||||
|
@ -407,12 +407,12 @@ func processLocalEnvValue(val string) (string, error) {
|
|||
match := evRegex.FindStringSubmatch(val)
|
||||
if len(match) > valIdx {
|
||||
if match[ctxIdx] != "env" {
|
||||
return "", fmt.Errorf("allowed env value entry is \"{{ env.LOCAL_VALUE }}\"1; got: %q", match[ctxIdx])
|
||||
return "", fmt.Errorf("allowed env value entry is \"{{ env:LOCAL_VALUE }}\"; got: %q", match[ctxIdx])
|
||||
}
|
||||
if v, ok := os.LookupEnv(match[valIdx]); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return "", fmt.Errorf("required local environment variable \"%q\" is not set", match[valIdx])
|
||||
return "", fmt.Errorf("required local environment variable %q is not set", match[valIdx])
|
||||
}
|
||||
} else {
|
||||
return val, nil
|
||||
|
|
|
@ -31,9 +31,9 @@ func Test_processValue(t *testing.T) {
|
|||
wantErr bool
|
||||
}{
|
||||
{name: "simple value", arg: "A_VALUE", want: "A_VALUE", wantErr: false},
|
||||
{name: "using envvar value", arg: "{{ env.TEST_KNATIVE_DEPLOYER }}", want: "VALUE_FOR_TEST_KNATIVE_DEPLOYER", wantErr: false},
|
||||
{name: "bad context", arg: "{{secret.S}}", want: "", wantErr: true},
|
||||
{name: "unset envvar", arg: "{{env.SOME_UNSET_VAR}}", want: "", wantErr: true},
|
||||
{name: "using envvar value", arg: "{{ env:TEST_KNATIVE_DEPLOYER }}", want: "VALUE_FOR_TEST_KNATIVE_DEPLOYER", wantErr: false},
|
||||
{name: "bad context", arg: "{{secret:S}}", want: "", wantErr: true},
|
||||
{name: "unset envvar", arg: "{{env:SOME_UNSET_VAR}}", want: "", wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue