Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cfgparser: add env_split for splitting of ENV variable values on comma #691

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/reference/config-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ directive0 {env:VAR}
Parse is forgiving and incomplete variable placeholder (e.g. '{env:VAR') will
be left as-is. Variables are expanded inside quotes too.

Environment values that are comma separated can optionally be split to space separated strings using {env_split:COMMA_SEPARATED_VARIABLE_NAME}

```
# SEP_VAR=foo,bar,baz

directive1 {env_split:SEP_VAR}
```

In this usage the value of `directive1` would be `foo bar baz` (and `{env:SEP_VAR}` would be `foo,bar,baz`)

## Snippets & imports

You can reuse blocks of configuration by defining them as "snippets". Snippet
Expand Down Expand Up @@ -196,5 +206,3 @@ No-op module. It doesn't need to be configured explicitly and can be referenced
using "dummy" name. It can act as a delivery target or auth.
provider. In the latter case, it will accept any credentials, allowing any
client to authenticate using any username and password (use with care!).


39 changes: 33 additions & 6 deletions framework/cfgparser/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ func expandEnvironment(nodes []Node) []Node {
return nil
}

replacer := buildEnvReplacer()
envMap := buildEnvMap()
replacer := buildEnvReplacerFromMap(envMap)
newNodes := make([]Node, 0, len(nodes))
for _, node := range nodes {
node.Name = removeUnexpandedEnvvars(replacer.Replace(node.Name))
newArgs := make([]string, 0, len(node.Args))
for _, arg := range node.Args {
newArgs = append(newArgs, removeUnexpandedEnvvars(replacer.Replace(arg)))
expArg := replacer.Replace(arg)
if splitArgs, ok := handleEnvSplitWithMap(expArg, envMap); ok {
newArgs = append(newArgs, splitArgs...)
} else {
newArgs = append(newArgs, removeUnexpandedEnvvars(expArg))
}
}
node.Args = newArgs
node.Children = expandEnvironment(node.Children)
Expand All @@ -47,21 +53,42 @@ func expandEnvironment(nodes []Node) []Node {
}

var unixEnvvarRe = regexp.MustCompile(`{env:([^\$]+)}`)
var unixEnvSplitRe = regexp.MustCompile(`{env_split:([^\$]+)}`)

func removeUnexpandedEnvvars(s string) string {
s = unixEnvvarRe.ReplaceAllString(s, "")
return s
}

func buildEnvReplacer() *strings.Replacer {
func buildEnvMap() map[string]string {
env := os.Environ()
pairs := make([]string, 0, len(env)*4)
envMap := make(map[string]string, len(env))
for _, entry := range env {
parts := strings.SplitN(entry, "=", 2)
key := parts[0]
value := parts[1]
if len(parts) == 2 {
envMap[parts[0]] = parts[1]
}
}
return envMap
}

func buildEnvReplacerFromMap(envMap map[string]string) *strings.Replacer {
pairs := make([]string, 0, len(envMap)*4)
for key, value := range envMap {
pairs = append(pairs, "{env:"+key+"}", value)
}
return strings.NewReplacer(pairs...)
}

func handleEnvSplitWithMap(arg string, envMap map[string]string) ([]string, bool) {
matches := unixEnvSplitRe.FindStringSubmatch(arg)
if len(matches) == 2 {
value, exists := envMap[matches[1]]
if !exists {
return nil, false
}
splitValues := strings.Split(value, ",")
return splitValues, true
}
return nil, false
}
42 changes: 42 additions & 0 deletions framework/cfgparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,47 @@ var cases = []struct {
},
false,
},
{
"environment variable split at comma expansion",
`a {env_split:TESTING_VARIABLE_SPLIT}`,
[]Node{
{
Name: "a",
Args: []string{"value1", "value2", "value3"},
Children: nil,
File: "test",
Line: 1,
},
},
false,
},
{
"environment variable mixed usage",
`mixed {env:TESTING_VARIABLE} {env_split:TESTING_VARIABLE_SPLIT}`,
[]Node{
{
Name: "mixed",
Args: []string{"ABCDEF", "value1", "value2", "value3"},
Children: nil,
File: "test",
Line: 1,
},
},
false,
},
{
"environment variable not split on comma",
`not_split {env:TESTING_VARIABLE_SPLIT}`,
[]Node{
{
Name: "not_split",
Args: []string{"value1,value2,value3"},
File: "test",
Line: 1,
},
},
false,
},
{
"snippet expansion",
`(foo) { a }
Expand Down Expand Up @@ -581,6 +622,7 @@ func printTree(t *testing.T, root Node, indent int) {
func TestRead(t *testing.T) {
os.Setenv("TESTING_VARIABLE", "ABCDEF")
os.Setenv("TESTING_VARIABLE2", "ABC2 DEF2")
os.Setenv("TESTING_VARIABLE_SPLIT", "value1,value2,value3")

for _, case_ := range cases {
case_ := case_
Expand Down