From fdf2a77feb53cb9dd6de772f811e96a5f6b2d2c4 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Mon, 1 Jun 2020 12:43:06 -0400 Subject: [PATCH] caddyfile: Add args on imports (#3423) * caddyfile: Add support for args on imports * caddyfile: Add more import args tests --- caddyconfig/caddyfile/parse.go | 33 ++++++-- caddyconfig/caddyfile/parse_test.go | 8 +- .../caddyfile/testdata/import_args0.txt | 1 + .../caddyfile/testdata/import_args1.txt | 1 + .../caddyfile_adapt/import_args_file.txt | 49 +++++++++++ .../caddyfile_adapt/import_args_snippet.txt | 83 +++++++++++++++++++ .../integration/testdata/import_respond.txt | 1 + 7 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 caddyconfig/caddyfile/testdata/import_args0.txt create mode 100644 caddyconfig/caddyfile/testdata/import_args1.txt create mode 100644 caddytest/integration/caddyfile_adapt/import_args_file.txt create mode 100644 caddytest/integration/caddyfile_adapt/import_args_snippet.txt create mode 100644 caddytest/integration/testdata/import_respond.txt diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go index cdcac268..5cb5b686 100755 --- a/caddyconfig/caddyfile/parse.go +++ b/caddyconfig/caddyfile/parse.go @@ -20,7 +20,10 @@ import ( "log" "os" "path/filepath" + "strconv" "strings" + + "github.com/caddyserver/caddy/v2" ) // Parse parses the input just enough to group tokens, in @@ -292,11 +295,19 @@ func (p *parser) doImport() error { if importPattern == "" { return p.Err("Import requires a non-empty filepath") } - if p.NextArg() { - return p.Err("Import takes only one argument (glob pattern or file)") + + // grab remaining args as placeholder replacements + args := p.RemainingArgs() + + // add args to the replacer + repl := caddy.NewReplacer() + for index, arg := range args { + repl.Set("args."+strconv.Itoa(index), arg) } - // splice out the import directive and its argument (2 tokens total) - tokensBefore := p.tokens[:p.cursor-1] + + // splice out the import directive and its arguments + // (2 tokens, plus the length of args) + tokensBefore := p.tokens[:p.cursor-1-len(args)] tokensAfter := p.tokens[p.cursor+1:] var importedTokens []Token @@ -348,10 +359,20 @@ func (p *parser) doImport() error { } } + // copy the tokens so we don't overwrite p.definedSnippets + tokensCopy := make([]Token, len(importedTokens)) + copy(tokensCopy, importedTokens) + + // run the argument replacer on the tokens + for index, token := range tokensCopy { + token.Text = repl.ReplaceKnown(token.Text, "") + tokensCopy[index] = token + } + // splice the imported tokens in the place of the import statement // and rewind cursor so Next() will land on first imported token - p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...) - p.cursor-- + p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...) + p.cursor -= len(args) + 1 return nil } diff --git a/caddyconfig/caddyfile/parse_test.go b/caddyconfig/caddyfile/parse_test.go index ed153674..12c30c86 100755 --- a/caddyconfig/caddyfile/parse_test.go +++ b/caddyconfig/caddyfile/parse_test.go @@ -182,14 +182,17 @@ func TestParseOneAndImport(t *testing.T) { "host1", }, []int{1, 2}}, - {`import testdata/import_test1.txt testdata/import_test2.txt`, true, []string{}, []int{}}, - {`import testdata/not_found.txt`, true, []string{}, []int{}}, {`""`, false, []string{}, []int{}}, {``, false, []string{}, []int{}}, + // import with args + {`import testdata/import_args0.txt a`, false, []string{"a"}, []int{}}, + {`import testdata/import_args1.txt a b`, false, []string{"a", "b"}, []int{}}, + {`import testdata/import_args*.txt a b`, false, []string{"a"}, []int{2}}, + // test cases found by fuzzing! {`import }{$"`, true, []string{}, []int{}}, {`import /*/*.txt`, true, []string{}, []int{}}, @@ -210,6 +213,7 @@ func TestParseOneAndImport(t *testing.T) { t.Errorf("Test %d: Expected no error, but got: %v", i, err) } + // t.Logf("%+v\n", result) if len(result.Keys) != len(test.keys) { t.Errorf("Test %d: Expected %d keys, got %d", i, len(test.keys), len(result.Keys)) diff --git a/caddyconfig/caddyfile/testdata/import_args0.txt b/caddyconfig/caddyfile/testdata/import_args0.txt new file mode 100644 index 00000000..af946fef --- /dev/null +++ b/caddyconfig/caddyfile/testdata/import_args0.txt @@ -0,0 +1 @@ +{args.0} \ No newline at end of file diff --git a/caddyconfig/caddyfile/testdata/import_args1.txt b/caddyconfig/caddyfile/testdata/import_args1.txt new file mode 100644 index 00000000..519a92d6 --- /dev/null +++ b/caddyconfig/caddyfile/testdata/import_args1.txt @@ -0,0 +1 @@ +{args.0} {args.1} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/import_args_file.txt b/caddytest/integration/caddyfile_adapt/import_args_file.txt new file mode 100644 index 00000000..6947f681 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/import_args_file.txt @@ -0,0 +1,49 @@ +example.com + +import testdata/import_respond.txt Groot Rocket +import testdata/import_respond.txt you "the confused man" +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "example.com" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "'I am Groot', hears Rocket", + "handler": "static_response" + }, + { + "body": "'I am you', hears the confused man", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/import_args_snippet.txt b/caddytest/integration/caddyfile_adapt/import_args_snippet.txt new file mode 100644 index 00000000..8d2ff34a --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/import_args_snippet.txt @@ -0,0 +1,83 @@ +(logging) { + log { + output file /var/log/caddy/{args.0}.access.log + } +} + +a.example.com { + import logging a.example.com +} + +b.example.com { + import logging b.example.com +} +---------- +{ + "logging": { + "logs": { + "default": { + "exclude": [ + "http.log.access.log0", + "http.log.access.log1" + ] + }, + "log0": { + "writer": { + "filename": "/var/log/caddy/a.example.com.access.log", + "output": "file" + }, + "include": [ + "http.log.access.log0" + ] + }, + "log1": { + "writer": { + "filename": "/var/log/caddy/b.example.com.access.log", + "output": "file" + }, + "include": [ + "http.log.access.log1" + ] + } + } + }, + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "a.example.com" + ] + } + ], + "terminal": true + }, + { + "match": [ + { + "host": [ + "b.example.com" + ] + } + ], + "terminal": true + } + ], + "logs": { + "logger_names": { + "a.example.com": "log0", + "b.example.com": "log1" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/testdata/import_respond.txt b/caddytest/integration/testdata/import_respond.txt new file mode 100644 index 00000000..0907fbeb --- /dev/null +++ b/caddytest/integration/testdata/import_respond.txt @@ -0,0 +1 @@ +respond "'I am {args.0}', hears {args.1}" \ No newline at end of file