2018-02-05 16:05:59 -05:00
|
|
|
package parser // import "github.com/docker/docker/builder/dockerfile/parser"
|
2014-08-05 16:17:40 -04:00
|
|
|
|
|
|
|
import (
|
2017-11-07 18:27:49 -05:00
|
|
|
"bufio"
|
2015-11-01 23:08:37 -05:00
|
|
|
"bytes"
|
2014-08-29 16:04:34 -04:00
|
|
|
"fmt"
|
2014-08-05 16:17:40 -04:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2015-11-01 23:08:37 -05:00
|
|
|
"runtime"
|
2017-11-07 18:27:49 -05:00
|
|
|
"strings"
|
2014-08-05 16:17:40 -04:00
|
|
|
"testing"
|
2017-04-12 13:47:19 -04:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
"github.com/gotestyourself/gotestyourself/assert"
|
|
|
|
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
2014-08-05 16:17:40 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const testDir = "testfiles"
|
2014-08-07 03:42:10 -04:00
|
|
|
const negativeTestDir = "testfiles-negative"
|
2015-11-01 16:28:30 -05:00
|
|
|
const testFileLineInfo = "testfile-line/Dockerfile"
|
2014-08-05 16:17:40 -04:00
|
|
|
|
2015-03-09 19:55:00 -04:00
|
|
|
func getDirs(t *testing.T, dir string) []string {
|
2014-08-07 03:42:10 -04:00
|
|
|
f, err := os.Open(dir)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2014-08-05 16:17:40 -04:00
|
|
|
defer f.Close()
|
|
|
|
|
2015-03-09 19:55:00 -04:00
|
|
|
dirs, err := f.Readdirnames(0)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2014-08-07 03:42:10 -04:00
|
|
|
return dirs
|
|
|
|
}
|
|
|
|
|
2017-06-16 18:05:30 -04:00
|
|
|
func TestParseErrorCases(t *testing.T) {
|
2014-08-07 03:42:10 -04:00
|
|
|
for _, dir := range getDirs(t, negativeTestDir) {
|
2015-03-09 19:55:00 -04:00
|
|
|
dockerfile := filepath.Join(negativeTestDir, dir, "Dockerfile")
|
2014-08-07 03:42:10 -04:00
|
|
|
|
|
|
|
df, err := os.Open(dockerfile)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, dockerfile)
|
2016-06-24 23:57:21 -04:00
|
|
|
defer df.Close()
|
2014-08-07 03:42:10 -04:00
|
|
|
|
2017-04-12 13:47:19 -04:00
|
|
|
_, err = Parse(df)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.ErrorContains(err, ""), dockerfile)
|
2014-08-07 03:42:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 18:05:30 -04:00
|
|
|
func TestParseCases(t *testing.T) {
|
2014-08-07 03:42:10 -04:00
|
|
|
for _, dir := range getDirs(t, testDir) {
|
2015-03-09 19:55:00 -04:00
|
|
|
dockerfile := filepath.Join(testDir, dir, "Dockerfile")
|
|
|
|
resultfile := filepath.Join(testDir, dir, "result")
|
2014-08-05 16:17:40 -04:00
|
|
|
|
|
|
|
df, err := os.Open(dockerfile)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, dockerfile)
|
2015-01-03 00:38:52 -05:00
|
|
|
defer df.Close()
|
2014-08-05 16:17:40 -04:00
|
|
|
|
2017-04-12 13:47:19 -04:00
|
|
|
result, err := Parse(df)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, dockerfile)
|
2014-08-05 16:17:40 -04:00
|
|
|
|
2015-01-03 00:38:52 -05:00
|
|
|
content, err := ioutil.ReadFile(resultfile)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, resultfile)
|
2014-08-05 16:17:40 -04:00
|
|
|
|
2015-11-01 23:08:37 -05:00
|
|
|
if runtime.GOOS == "windows" {
|
2015-12-13 11:00:39 -05:00
|
|
|
// CRLF --> CR to match Unix behavior
|
2015-11-01 23:08:37 -05:00
|
|
|
content = bytes.Replace(content, []byte{'\x0d', '\x0a'}, []byte{'\x0a'}, -1)
|
|
|
|
}
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.Equal(result.AST.Dump()+"\n", string(content)), "In "+dockerfile)
|
2014-08-05 16:17:40 -04:00
|
|
|
}
|
|
|
|
}
|
2014-11-14 13:59:14 -05:00
|
|
|
|
|
|
|
func TestParseWords(t *testing.T) {
|
|
|
|
tests := []map[string][]string{
|
|
|
|
{
|
|
|
|
"input": {"foo"},
|
|
|
|
"expect": {"foo"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"input": {"foo bar"},
|
|
|
|
"expect": {"foo", "bar"},
|
|
|
|
},
|
2016-06-08 07:55:26 -04:00
|
|
|
{
|
|
|
|
"input": {"foo\\ bar"},
|
|
|
|
"expect": {"foo\\ bar"},
|
|
|
|
},
|
2014-11-14 13:59:14 -05:00
|
|
|
{
|
|
|
|
"input": {"foo=bar"},
|
|
|
|
"expect": {"foo=bar"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"input": {"foo bar 'abc xyz'"},
|
|
|
|
"expect": {"foo", "bar", "'abc xyz'"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"input": {`foo bar "abc xyz"`},
|
|
|
|
"expect": {"foo", "bar", `"abc xyz"`},
|
|
|
|
},
|
2016-06-08 07:55:26 -04:00
|
|
|
{
|
|
|
|
"input": {"àöû"},
|
|
|
|
"expect": {"àöû"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"input": {`föo bàr "âbc xÿz"`},
|
|
|
|
"expect": {"föo", "bàr", `"âbc xÿz"`},
|
|
|
|
},
|
2014-11-14 13:59:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
2017-04-11 15:07:02 -04:00
|
|
|
words := parseWords(test["input"][0], NewDefaultDirective())
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.DeepEqual(test["expect"], words))
|
2014-11-14 13:59:14 -05:00
|
|
|
}
|
|
|
|
}
|
2015-11-01 16:28:30 -05:00
|
|
|
|
2017-06-16 18:05:30 -04:00
|
|
|
func TestParseIncludesLineNumbers(t *testing.T) {
|
2015-11-01 16:28:30 -05:00
|
|
|
df, err := os.Open(testFileLineInfo)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2015-11-01 16:28:30 -05:00
|
|
|
defer df.Close()
|
|
|
|
|
2017-04-12 13:47:19 -04:00
|
|
|
result, err := Parse(df)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2015-11-01 16:28:30 -05:00
|
|
|
|
2017-04-12 13:47:19 -04:00
|
|
|
ast := result.AST
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.Equal(5, ast.StartLine))
|
|
|
|
assert.Check(t, is.Equal(31, ast.endLine))
|
|
|
|
assert.Check(t, is.Len(ast.Children, 3))
|
2015-11-01 16:28:30 -05:00
|
|
|
expected := [][]int{
|
2016-04-22 18:04:46 -04:00
|
|
|
{5, 5},
|
|
|
|
{11, 12},
|
2016-12-02 08:15:55 -05:00
|
|
|
{17, 31},
|
2015-11-01 16:28:30 -05:00
|
|
|
}
|
|
|
|
for i, child := range ast.Children {
|
2017-06-16 18:05:30 -04:00
|
|
|
msg := fmt.Sprintf("Child %d", i)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.DeepEqual(expected[i], []int{child.StartLine, child.endLine}), msg)
|
2015-11-01 16:28:30 -05:00
|
|
|
}
|
|
|
|
}
|
2017-06-16 18:05:30 -04:00
|
|
|
|
|
|
|
func TestParseWarnsOnEmptyContinutationLine(t *testing.T) {
|
|
|
|
dockerfile := bytes.NewBufferString(`
|
|
|
|
FROM alpine:3.6
|
|
|
|
|
|
|
|
RUN something \
|
|
|
|
|
|
|
|
following \
|
|
|
|
|
|
|
|
more
|
|
|
|
|
|
|
|
RUN another \
|
|
|
|
|
|
|
|
thing
|
2017-09-27 16:18:24 -04:00
|
|
|
RUN non-indented \
|
|
|
|
# this is a comment
|
|
|
|
after-comment
|
|
|
|
|
|
|
|
RUN indented \
|
|
|
|
# this is an indented comment
|
|
|
|
comment
|
2017-06-16 18:05:30 -04:00
|
|
|
`)
|
|
|
|
|
|
|
|
result, err := Parse(dockerfile)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-06-16 18:05:30 -04:00
|
|
|
warnings := result.Warnings
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.Len(warnings, 3))
|
|
|
|
assert.Check(t, is.Contains(warnings[0], "Empty continuation line found in"))
|
|
|
|
assert.Check(t, is.Contains(warnings[0], "RUN something following more"))
|
|
|
|
assert.Check(t, is.Contains(warnings[1], "RUN another thing"))
|
|
|
|
assert.Check(t, is.Contains(warnings[2], "will become errors in a future release"))
|
2017-06-16 18:05:30 -04:00
|
|
|
}
|
2017-11-07 18:27:49 -05:00
|
|
|
|
|
|
|
func TestParseReturnsScannerErrors(t *testing.T) {
|
|
|
|
label := strings.Repeat("a", bufio.MaxScanTokenSize)
|
|
|
|
|
|
|
|
dockerfile := strings.NewReader(fmt.Sprintf(`
|
|
|
|
FROM image
|
|
|
|
LABEL test=%s
|
|
|
|
`, label))
|
|
|
|
_, err := Parse(dockerfile)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.Error(err, "dockerfile line greater than max allowed size of 65535"))
|
2017-11-07 18:27:49 -05:00
|
|
|
}
|