caddyfile: Add args on imports (#3423)

* caddyfile: Add support for args on imports

* caddyfile: Add more import args tests
This commit is contained in:
Francis Lavoie 2020-06-01 12:43:06 -04:00 committed by GitHub
parent a496308f6e
commit fdf2a77feb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 8 deletions

View file

@ -20,7 +20,10 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/caddyserver/caddy/v2"
) )
// Parse parses the input just enough to group tokens, in // Parse parses the input just enough to group tokens, in
@ -292,11 +295,19 @@ func (p *parser) doImport() error {
if importPattern == "" { if importPattern == "" {
return p.Err("Import requires a non-empty filepath") 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:] tokensAfter := p.tokens[p.cursor+1:]
var importedTokens []Token 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 // splice the imported tokens in the place of the import statement
// and rewind cursor so Next() will land on first imported token // and rewind cursor so Next() will land on first imported token
p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...) p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...)
p.cursor-- p.cursor -= len(args) + 1
return nil return nil
} }

View file

@ -182,14 +182,17 @@ func TestParseOneAndImport(t *testing.T) {
"host1", "host1",
}, []int{1, 2}}, }, []int{1, 2}},
{`import testdata/import_test1.txt testdata/import_test2.txt`, true, []string{}, []int{}},
{`import testdata/not_found.txt`, true, []string{}, []int{}}, {`import testdata/not_found.txt`, true, []string{}, []int{}},
{`""`, false, []string{}, []int{}}, {`""`, false, []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! // test cases found by fuzzing!
{`import }{$"`, true, []string{}, []int{}}, {`import }{$"`, true, []string{}, []int{}},
{`import /*/*.txt`, 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.Errorf("Test %d: Expected no error, but got: %v", i, err)
} }
// t.Logf("%+v\n", result)
if len(result.Keys) != len(test.keys) { if len(result.Keys) != len(test.keys) {
t.Errorf("Test %d: Expected %d keys, got %d", t.Errorf("Test %d: Expected %d keys, got %d",
i, len(test.keys), len(result.Keys)) i, len(test.keys), len(result.Keys))

View file

@ -0,0 +1 @@
{args.0}

View file

@ -0,0 +1 @@
{args.0} {args.1}

View file

@ -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
}
]
}
}
}
}
}

View file

@ -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"
}
}
}
}
}
}
}

View file

@ -0,0 +1 @@
respond "'I am {args.0}', hears {args.1}"