blob: 6ac3dfda13a8f248e6873a3b4560320c94f87dbf [file] [log] [blame]
package instructions
import (
"strings"
"testing"
"github.com/docker/docker/builder/dockerfile/command"
"github.com/docker/docker/builder/dockerfile/parser"
"github.com/docker/docker/internal/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCommandsExactlyOneArgument(t *testing.T) {
commands := []string{
"MAINTAINER",
"WORKDIR",
"USER",
"STOPSIGNAL",
}
for _, command := range commands {
ast, err := parser.Parse(strings.NewReader(command))
require.NoError(t, err)
_, err = ParseInstruction(ast.AST.Children[0])
assert.EqualError(t, err, errExactlyOneArgument(command).Error())
}
}
func TestCommandsAtLeastOneArgument(t *testing.T) {
commands := []string{
"ENV",
"LABEL",
"ONBUILD",
"HEALTHCHECK",
"EXPOSE",
"VOLUME",
}
for _, command := range commands {
ast, err := parser.Parse(strings.NewReader(command))
require.NoError(t, err)
_, err = ParseInstruction(ast.AST.Children[0])
assert.EqualError(t, err, errAtLeastOneArgument(command).Error())
}
}
func TestCommandsNoDestinationArgument(t *testing.T) {
commands := []string{
"ADD",
"COPY",
}
for _, command := range commands {
ast, err := parser.Parse(strings.NewReader(command + " arg1"))
require.NoError(t, err)
_, err = ParseInstruction(ast.AST.Children[0])
assert.EqualError(t, err, errNoDestinationArgument(command).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)
assert.EqualError(t, err, errTooManyArguments(command).Error())
}
}
func TestCommandsBlankNames(t *testing.T) {
commands := []string{
"ENV",
"LABEL",
}
for _, command := range commands {
node := &parser.Node{
Original: command + " =arg2",
Value: strings.ToLower(command),
Next: &parser.Node{
Value: "",
Next: &parser.Node{
Value: "arg2",
},
},
}
_, err := ParseInstruction(node)
assert.EqualError(t, err, errBlankCommandNames(command).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)
assert.NoError(t, err)
hc, ok := cmd.(*HealthCheckCommand)
assert.True(t, ok)
expected := []string{"CMD-SHELL", "hello world"}
assert.Equal(t, expected, hc.Health.Test)
}
func TestParseOptInterval(t *testing.T) {
flInterval := &Flag{
name: "interval",
flagType: stringType,
Value: "50ns",
}
_, err := parseOptInterval(flInterval)
testutil.ErrorContains(t, err, "cannot be less than 1ms")
flInterval.Value = "1ms"
_, err = parseOptInterval(flInterval)
require.NoError(t, err)
}
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)
testutil.ErrorContains(t, err, c.expectedError)
}
}