From 72c0527b7d0ae2423c1113e1964bd66f46516e37 Mon Sep 17 00:00:00 2001 From: Michael Banzon Date: Thu, 5 Nov 2015 08:26:10 +0100 Subject: [PATCH 1/5] Added support for env vars in Caddyfile This is work in progress for #304 --- caddy/parse/parsing.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index b24b46ab..df8feb54 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -4,6 +4,7 @@ import ( "net" "os" "path/filepath" + "regexp" "strings" ) @@ -71,6 +72,8 @@ func (p *parser) addresses() error { for { tkn := p.Val() + tkn = getValFromEnv(tkn) + // special case: import directive replaces tokens during parse-time if tkn == "import" && p.isNewLine() { err := p.doImport() @@ -241,6 +244,7 @@ func (p *parser) directive() error { } else if p.Val() == "}" && nesting == 0 { return p.Err("Unexpected '}' because no matching opening brace") } + p.tokens[p.cursor].text = getValFromEnv(p.tokens[p.cursor].text) p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) } @@ -327,3 +331,14 @@ func (sb serverBlock) HostList() []string { } return sbHosts } + +func getValFromEnv(s string) string { + re := regexp.MustCompile("{\\$[^}]+}") + envRefs := re.FindAllString(s, -1) + + for _, ref := range envRefs { + s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-1]), -1) + } + + return s +} From 01465932e7a8abf3ebfdb281da6c3edf11c01293 Mon Sep 17 00:00:00 2001 From: Michael Banzon Date: Thu, 5 Nov 2015 18:01:44 +0100 Subject: [PATCH 2/5] Environment variables Windows style Added the Windows style ({%%}) for environment variables in Caddyfiles (for issue #304). --- caddy/parse/parsing.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index df8feb54..96005881 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -8,6 +8,11 @@ import ( "strings" ) +var ( + unixEnvRegEx = regexp.MustCompile("{\\$[^}]+}") + windowsEnvRegEx = regexp.MustCompile("{%[^}]+%}") +) + type parser struct { Dispenser block serverBlock // current server block being parsed @@ -333,12 +338,18 @@ func (sb serverBlock) HostList() []string { } func getValFromEnv(s string) string { - re := regexp.MustCompile("{\\$[^}]+}") - envRefs := re.FindAllString(s, -1) + envRefsUnix := unixEnvRegEx.FindAllString(s, -1) - for _, ref := range envRefs { + for _, ref := range envRefsUnix { s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-1]), -1) } + envRefsWin := unixEnvRegEx.FindAllString(s, -1) + + for _, ref := range envRefsWin { + s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-2]), -1) + } + return s + } From 8774c9070944b91828c0c18bcb1879ee162b51d9 Mon Sep 17 00:00:00 2001 From: Michael Banzon Date: Thu, 5 Nov 2015 18:12:06 +0100 Subject: [PATCH 3/5] Removed the Windows part. It wasn't working properly. For issue #304. --- caddy/parse/parsing.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index 96005881..2df2dfdd 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -9,8 +9,7 @@ import ( ) var ( - unixEnvRegEx = regexp.MustCompile("{\\$[^}]+}") - windowsEnvRegEx = regexp.MustCompile("{%[^}]+%}") + envRegEx = regexp.MustCompile("{\\$[^}]+}") ) type parser struct { @@ -338,18 +337,11 @@ func (sb serverBlock) HostList() []string { } func getValFromEnv(s string) string { - envRefsUnix := unixEnvRegEx.FindAllString(s, -1) + envRefs := envRegEx.FindAllString(s, -1) - for _, ref := range envRefsUnix { + for _, ref := range envRefs { s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-1]), -1) } - envRefsWin := unixEnvRegEx.FindAllString(s, -1) - - for _, ref := range envRefsWin { - s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-2]), -1) - } - return s - } From e166ebf68b5375e2dae513a98eafacba431a938c Mon Sep 17 00:00:00 2001 From: Michael Banzon Date: Sat, 14 Nov 2015 18:54:29 +0100 Subject: [PATCH 4/5] Added test for environment replacement. Added test for the fix of issue #304 --- caddy/parse/parsing_test.go | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/caddy/parse/parsing_test.go b/caddy/parse/parsing_test.go index afd5870f..4bf9d88f 100644 --- a/caddy/parse/parsing_test.go +++ b/caddy/parse/parsing_test.go @@ -1,6 +1,7 @@ package parse import ( + "os" "strings" "testing" ) @@ -364,6 +365,65 @@ func TestParseAll(t *testing.T) { } } +func TestEnvironmentReplacement(t *testing.T) { + setupParseTests() + + os.Setenv("MY_PORT", "8080") + os.Setenv("MY_ADDRESS", "servername.com") + os.Setenv("MY_ADDRESS2", "127.0.0.1") + + for i, test := range []struct { + input string + addresses [][]address // addresses per server block, in order + }{ + {`{$MY_ADDRESS}`, [][]address{ + {{"servername.com", ""}}, + }}, + + {`{$MY_ADDRESS}:{$MY_PORT}`, [][]address{ + []address{{"servername.com", "8080"}}, + }}, + + {`{$MY_ADDRESS2}:1234 { + } + localhost:{$MY_PORT} { + }`, [][]address{ + []address{{"127.0.0.1", "1234"}}, + []address{{"localhost", "8080"}}, + }}, + } { + p := testParser(test.input) + blocks, err := p.parseAll() + + if err != nil { + t.Errorf("Test %d: Expected no error, but got: %v", i, err) + } + + if len(blocks) != len(test.addresses) { + t.Errorf("Test %d: Expected %d server blocks, got %d", + i, len(test.addresses), len(blocks)) + continue + } + for j, block := range blocks { + if len(block.Addresses) != len(test.addresses[j]) { + t.Errorf("Test %d: Expected %d addresses in block %d, got %d", + i, len(test.addresses[j]), j, len(block.Addresses)) + continue + } + for k, addr := range block.Addresses { + if addr.Host != test.addresses[j][k].Host { + t.Errorf("Test %d, block %d, address %d: Expected host to be '%s', but was '%s'", + i, j, k, test.addresses[j][k].Host, addr.Host) + } + if addr.Port != test.addresses[j][k].Port { + t.Errorf("Test %d, block %d, address %d: Expected port to be '%s', but was '%s'", + i, j, k, test.addresses[j][k].Port, addr.Port) + } + } + } + } +} + func setupParseTests() { // Set up some bogus directives for testing ValidDirectives = map[string]struct{}{ From d448c919e895149097c2b175fcfd8f7046be610c Mon Sep 17 00:00:00 2001 From: Michael Banzon Date: Sun, 15 Nov 2015 11:16:37 +0100 Subject: [PATCH 5/5] Changed implementation of issue #304 fix It no longer uses regular expressions. It supports both the Unix `{$ENV_VAR}` _and_ the Windows `{%ENV_VAR%}` syntax. Added test for both Unix and Windows env. syntax. --- caddy/parse/parsing.go | 24 ++++++++++++++++-------- caddy/parse/parsing_test.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index 2df2dfdd..2994159b 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -4,14 +4,9 @@ import ( "net" "os" "path/filepath" - "regexp" "strings" ) -var ( - envRegEx = regexp.MustCompile("{\\$[^}]+}") -) - type parser struct { Dispenser block serverBlock // current server block being parsed @@ -337,10 +332,23 @@ func (sb serverBlock) HostList() []string { } func getValFromEnv(s string) string { - envRefs := envRegEx.FindAllString(s, -1) + s = replaceEnvReferences(s, "{$", "}") + s = replaceEnvReferences(s, "{%", "%}") + return s +} - for _, ref := range envRefs { - s = strings.Replace(s, ref, os.Getenv(ref[2:len(ref)-1]), -1) +func replaceEnvReferences(s, refStart, refEnd string) string { + index := strings.Index(s, refStart) + for index != -1 { + endIndex := strings.Index(s, refEnd) + if endIndex != -1 { + ref := s[index : endIndex+len(refEnd)] + s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1) + } else { + return s + } + + index = strings.Index(s, refStart) } return s diff --git a/caddy/parse/parsing_test.go b/caddy/parse/parsing_test.go index 4bf9d88f..b9184684 100644 --- a/caddy/parse/parsing_test.go +++ b/caddy/parse/parsing_test.go @@ -391,6 +391,22 @@ func TestEnvironmentReplacement(t *testing.T) { []address{{"127.0.0.1", "1234"}}, []address{{"localhost", "8080"}}, }}, + + {`{%MY_ADDRESS%}`, [][]address{ + {{"servername.com", ""}}, + }}, + + {`{%MY_ADDRESS%}:{%MY_PORT%}`, [][]address{ + []address{{"servername.com", "8080"}}, + }}, + + {`{%MY_ADDRESS2%}:1234 { + } + localhost:{%MY_PORT%} { + }`, [][]address{ + []address{{"127.0.0.1", "1234"}}, + []address{{"localhost", "8080"}}, + }}, } { p := testParser(test.input) blocks, err := p.parseAll()