242 lines
5.6 KiB
Go
242 lines
5.6 KiB
Go
package instructions
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/moby/buildkit/frontend/dockerfile/command"
|
|
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCommandsExactlyOneArgument(t *testing.T) {
|
|
commands := []string{
|
|
"MAINTAINER",
|
|
"WORKDIR",
|
|
"USER",
|
|
"STOPSIGNAL",
|
|
}
|
|
|
|
for _, cmd := range commands {
|
|
ast, err := parser.Parse(strings.NewReader(cmd))
|
|
require.NoError(t, err)
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
|
require.EqualError(t, err, errExactlyOneArgument(cmd).Error())
|
|
}
|
|
}
|
|
|
|
func TestCommandsAtLeastOneArgument(t *testing.T) {
|
|
commands := []string{
|
|
"ENV",
|
|
"LABEL",
|
|
"ONBUILD",
|
|
"HEALTHCHECK",
|
|
"EXPOSE",
|
|
"VOLUME",
|
|
}
|
|
|
|
for _, cmd := range commands {
|
|
ast, err := parser.Parse(strings.NewReader(cmd))
|
|
require.NoError(t, err)
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
|
require.EqualError(t, err, errAtLeastOneArgument(cmd).Error())
|
|
}
|
|
}
|
|
|
|
func TestCommandsNoDestinationArgument(t *testing.T) {
|
|
commands := []string{
|
|
"ADD",
|
|
"COPY",
|
|
}
|
|
|
|
for _, cmd := range commands {
|
|
ast, err := parser.Parse(strings.NewReader(cmd + " arg1"))
|
|
require.NoError(t, err)
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
|
require.EqualError(t, err, errNoDestinationArgument(cmd).Error())
|
|
}
|
|
}
|
|
|
|
func TestCommandsTooManyArguments(t *testing.T) {
|
|
commands := []string{
|
|
"ENV",
|
|
"LABEL",
|
|
}
|
|
|
|
for _, command := range commands {
|
|
node := &parser.Node{
|
|
Original: command + "arg1 arg2 arg3",
|
|
Value: strings.ToLower(command),
|
|
Next: &parser.Node{
|
|
Value: "arg1",
|
|
Next: &parser.Node{
|
|
Value: "arg2",
|
|
Next: &parser.Node{
|
|
Value: "arg3",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
_, err := ParseInstruction(node)
|
|
require.EqualError(t, err, errTooManyArguments(command).Error())
|
|
}
|
|
}
|
|
|
|
func TestCommandsBlankNames(t *testing.T) {
|
|
commands := []string{
|
|
"ENV",
|
|
"LABEL",
|
|
}
|
|
|
|
for _, cmd := range commands {
|
|
node := &parser.Node{
|
|
Original: cmd + " =arg2",
|
|
Value: strings.ToLower(cmd),
|
|
Next: &parser.Node{
|
|
Value: "",
|
|
Next: &parser.Node{
|
|
Value: "arg2",
|
|
},
|
|
},
|
|
}
|
|
_, err := ParseInstruction(node)
|
|
require.EqualError(t, err, errBlankCommandNames(cmd).Error())
|
|
}
|
|
}
|
|
|
|
func TestHealthCheckCmd(t *testing.T) {
|
|
node := &parser.Node{
|
|
Value: command.Healthcheck,
|
|
Next: &parser.Node{
|
|
Value: "CMD",
|
|
Next: &parser.Node{
|
|
Value: "hello",
|
|
Next: &parser.Node{
|
|
Value: "world",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
cmd, err := ParseInstruction(node)
|
|
require.NoError(t, err)
|
|
hc, ok := cmd.(*HealthCheckCommand)
|
|
require.Equal(t, true, ok)
|
|
expected := []string{"CMD-SHELL", "hello world"}
|
|
require.Equal(t, expected, hc.Health.Test)
|
|
}
|
|
|
|
func TestParseOptInterval(t *testing.T) {
|
|
flInterval := &Flag{
|
|
name: "interval",
|
|
flagType: stringType,
|
|
Value: "50ns",
|
|
}
|
|
_, err := parseOptInterval(flInterval)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "cannot be less than 1ms")
|
|
|
|
flInterval.Value = "1ms"
|
|
_, err = parseOptInterval(flInterval)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestCommentsDetection(t *testing.T) {
|
|
dt := `# foo sets foo
|
|
ARG foo=bar
|
|
|
|
# base defines first stage
|
|
FROM busybox AS base
|
|
# this is irrelevant
|
|
ARG foo
|
|
# bar defines bar
|
|
# baz is something else
|
|
ARG bar baz=123
|
|
`
|
|
|
|
ast, err := parser.Parse(bytes.NewBuffer([]byte(dt)))
|
|
require.NoError(t, err)
|
|
|
|
stages, meta, err := Parse(ast.AST)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "defines first stage", stages[0].Comment)
|
|
require.Equal(t, "foo", meta[0].Args[0].Key)
|
|
require.Equal(t, "sets foo", meta[0].Args[0].Comment)
|
|
|
|
st := stages[0]
|
|
|
|
require.Equal(t, "foo", st.Commands[0].(*ArgCommand).Args[0].Key)
|
|
require.Equal(t, "", st.Commands[0].(*ArgCommand).Args[0].Comment)
|
|
require.Equal(t, "bar", st.Commands[1].(*ArgCommand).Args[0].Key)
|
|
require.Equal(t, "defines bar", st.Commands[1].(*ArgCommand).Args[0].Comment)
|
|
require.Equal(t, "baz", st.Commands[1].(*ArgCommand).Args[1].Key)
|
|
require.Equal(t, "is something else", st.Commands[1].(*ArgCommand).Args[1].Comment)
|
|
}
|
|
|
|
func TestErrorCases(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
dockerfile string
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "copyEmptyWhitespace",
|
|
dockerfile: `COPY
|
|
quux \
|
|
bar`,
|
|
expectedError: "COPY requires at least two arguments",
|
|
},
|
|
{
|
|
name: "ONBUILD forbidden FROM",
|
|
dockerfile: "ONBUILD FROM scratch",
|
|
expectedError: "FROM isn't allowed as an ONBUILD trigger",
|
|
},
|
|
{
|
|
name: "MAINTAINER unknown flag",
|
|
dockerfile: "MAINTAINER --boo joe@example.com",
|
|
expectedError: "unknown flag: boo",
|
|
},
|
|
{
|
|
name: "Chaining ONBUILD",
|
|
dockerfile: `ONBUILD ONBUILD RUN touch foobar`,
|
|
expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
|
|
},
|
|
{
|
|
name: "Invalid instruction",
|
|
dockerfile: `FOO bar`,
|
|
expectedError: "unknown instruction: FOO",
|
|
},
|
|
{
|
|
name: "Invalid instruction",
|
|
dockerfile: `foo bar`,
|
|
expectedError: "unknown instruction: foo",
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
r := strings.NewReader(c.dockerfile)
|
|
ast, err := parser.Parse(r)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
|
}
|
|
n := ast.AST.Children[0]
|
|
_, err = ParseInstruction(n)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), c.expectedError)
|
|
}
|
|
}
|
|
|
|
func TestRunCmdFlagsUsed(t *testing.T) {
|
|
dockerfile := "RUN --mount=type=tmpfs,target=/foo/ echo hello"
|
|
r := strings.NewReader(dockerfile)
|
|
ast, err := parser.Parse(r)
|
|
require.NoError(t, err)
|
|
|
|
n := ast.AST.Children[0]
|
|
c, err := ParseInstruction(n)
|
|
require.NoError(t, err)
|
|
require.IsType(t, c, &RunCommand{})
|
|
require.Equal(t, []string{"mount"}, c.(*RunCommand).FlagsUsed)
|
|
}
|