2018-06-02 00:30:18 +00:00
|
|
|
package instructions
|
2017-05-22 15:21:17 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2018-06-02 00:30:18 +00:00
|
|
|
"github.com/moby/buildkit/frontend/dockerfile/command"
|
|
|
|
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
2020-04-15 04:49:48 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-05-22 15:21:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestCommandsExactlyOneArgument(t *testing.T) {
|
|
|
|
commands := []string{
|
|
|
|
"MAINTAINER",
|
|
|
|
"WORKDIR",
|
|
|
|
"USER",
|
|
|
|
"STOPSIGNAL",
|
|
|
|
}
|
|
|
|
|
2018-05-20 22:06:50 +00:00
|
|
|
for _, cmd := range commands {
|
|
|
|
ast, err := parser.Parse(strings.NewReader(cmd))
|
2020-04-15 04:49:48 +00:00
|
|
|
require.NoError(t, err)
|
2017-05-22 15:21:17 +00:00
|
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
2020-04-15 04:49:48 +00:00
|
|
|
require.EqualError(t, err, errExactlyOneArgument(cmd).Error())
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCommandsAtLeastOneArgument(t *testing.T) {
|
|
|
|
commands := []string{
|
|
|
|
"ENV",
|
|
|
|
"LABEL",
|
|
|
|
"ONBUILD",
|
|
|
|
"HEALTHCHECK",
|
|
|
|
"EXPOSE",
|
|
|
|
"VOLUME",
|
|
|
|
}
|
|
|
|
|
2018-05-20 22:06:50 +00:00
|
|
|
for _, cmd := range commands {
|
|
|
|
ast, err := parser.Parse(strings.NewReader(cmd))
|
2020-04-15 04:49:48 +00:00
|
|
|
require.NoError(t, err)
|
2017-05-22 15:21:17 +00:00
|
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
2020-04-15 04:49:48 +00:00
|
|
|
require.EqualError(t, err, errAtLeastOneArgument(cmd).Error())
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-21 14:03:59 +00:00
|
|
|
func TestCommandsNoDestinationArgument(t *testing.T) {
|
2017-05-22 15:21:17 +00:00
|
|
|
commands := []string{
|
|
|
|
"ADD",
|
|
|
|
"COPY",
|
|
|
|
}
|
|
|
|
|
2018-05-20 22:06:50 +00:00
|
|
|
for _, cmd := range commands {
|
|
|
|
ast, err := parser.Parse(strings.NewReader(cmd + " arg1"))
|
2020-04-15 04:49:48 +00:00
|
|
|
require.NoError(t, err)
|
2017-05-22 15:21:17 +00:00
|
|
|
_, err = ParseInstruction(ast.AST.Children[0])
|
2020-04-15 04:49:48 +00:00
|
|
|
require.EqualError(t, err, errNoDestinationArgument(cmd).Error())
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.EqualError(t, err, errTooManyArguments(command).Error())
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCommandsBlankNames(t *testing.T) {
|
|
|
|
commands := []string{
|
|
|
|
"ENV",
|
|
|
|
"LABEL",
|
|
|
|
}
|
|
|
|
|
2018-05-20 22:06:50 +00:00
|
|
|
for _, cmd := range commands {
|
2017-05-22 15:21:17 +00:00
|
|
|
node := &parser.Node{
|
2018-05-20 22:06:50 +00:00
|
|
|
Original: cmd + " =arg2",
|
|
|
|
Value: strings.ToLower(cmd),
|
2017-05-22 15:21:17 +00:00
|
|
|
Next: &parser.Node{
|
|
|
|
Value: "",
|
|
|
|
Next: &parser.Node{
|
|
|
|
Value: "arg2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
_, err := ParseInstruction(node)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.EqualError(t, err, errBlankCommandNames(cmd).Error())
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.NoError(t, err)
|
2017-05-22 15:21:17 +00:00
|
|
|
hc, ok := cmd.(*HealthCheckCommand)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.Equal(t, true, ok)
|
2017-05-22 15:21:17 +00:00
|
|
|
expected := []string{"CMD-SHELL", "hello world"}
|
2020-04-15 04:49:48 +00:00
|
|
|
require.Equal(t, expected, hc.Health.Test)
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseOptInterval(t *testing.T) {
|
|
|
|
flInterval := &Flag{
|
|
|
|
name: "interval",
|
|
|
|
flagType: stringType,
|
|
|
|
Value: "50ns",
|
|
|
|
}
|
|
|
|
_, err := parseOptInterval(flInterval)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Contains(t, err.Error(), "cannot be less than 1ms")
|
2017-05-22 15:21:17 +00:00
|
|
|
|
|
|
|
flInterval.Value = "1ms"
|
|
|
|
_, err = parseOptInterval(flInterval)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.NoError(t, err)
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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: "ONBUILD forbidden MAINTAINER",
|
|
|
|
dockerfile: "ONBUILD MAINTAINER docker.io",
|
|
|
|
expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "ARG two arguments",
|
|
|
|
dockerfile: "ARG foo bar",
|
|
|
|
expectedError: "ARG requires exactly one argument",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
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",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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)
|
2020-04-15 04:49:48 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
require.Contains(t, err.Error(), c.expectedError)
|
2017-05-22 15:21:17 +00:00
|
|
|
}
|
|
|
|
}
|