| package dockerfile |
| |
| import ( |
| "fmt" |
| "runtime" |
| "strings" |
| "testing" |
| |
| "github.com/docker/docker/api/types" |
| "github.com/docker/docker/api/types/container" |
| "github.com/docker/docker/api/types/strslice" |
| "github.com/docker/go-connections/nat" |
| ) |
| |
| type commandWithFunction struct { |
| name string |
| function func(args []string) error |
| } |
| |
| func TestCommandsExactlyOneArgument(t *testing.T) { |
| commands := []commandWithFunction{ |
| {"MAINTAINER", func(args []string) error { return maintainer(nil, args, nil, "") }}, |
| {"FROM", func(args []string) error { return from(nil, args, nil, "") }}, |
| {"WORKDIR", func(args []string) error { return workdir(nil, args, nil, "") }}, |
| {"USER", func(args []string) error { return user(nil, args, nil, "") }}, |
| {"STOPSIGNAL", func(args []string) error { return stopSignal(nil, args, nil, "") }}} |
| |
| for _, command := range commands { |
| err := command.function([]string{}) |
| |
| if err == nil { |
| t.Fatalf("Error should be present for %s command", command.name) |
| } |
| |
| expectedError := errExactlyOneArgument(command.name) |
| |
| if err.Error() != expectedError.Error() { |
| t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError) |
| } |
| } |
| } |
| |
| func TestCommandsAtLeastOneArgument(t *testing.T) { |
| commands := []commandWithFunction{ |
| {"ENV", func(args []string) error { return env(nil, args, nil, "") }}, |
| {"LABEL", func(args []string) error { return label(nil, args, nil, "") }}, |
| {"ONBUILD", func(args []string) error { return onbuild(nil, args, nil, "") }}, |
| {"HEALTHCHECK", func(args []string) error { return healthcheck(nil, args, nil, "") }}, |
| {"EXPOSE", func(args []string) error { return expose(nil, args, nil, "") }}, |
| {"VOLUME", func(args []string) error { return volume(nil, args, nil, "") }}} |
| |
| for _, command := range commands { |
| err := command.function([]string{}) |
| |
| if err == nil { |
| t.Fatalf("Error should be present for %s command", command.name) |
| } |
| |
| expectedError := errAtLeastOneArgument(command.name) |
| |
| if err.Error() != expectedError.Error() { |
| t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError) |
| } |
| } |
| } |
| |
| func TestCommandsAtLeastTwoArguments(t *testing.T) { |
| commands := []commandWithFunction{ |
| {"ADD", func(args []string) error { return add(nil, args, nil, "") }}, |
| {"COPY", func(args []string) error { return dispatchCopy(nil, args, nil, "") }}} |
| |
| for _, command := range commands { |
| err := command.function([]string{"arg1"}) |
| |
| if err == nil { |
| t.Fatalf("Error should be present for %s command", command.name) |
| } |
| |
| expectedError := errAtLeastTwoArguments(command.name) |
| |
| if err.Error() != expectedError.Error() { |
| t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError) |
| } |
| } |
| } |
| |
| func TestCommandsTooManyArguments(t *testing.T) { |
| commands := []commandWithFunction{ |
| {"ENV", func(args []string) error { return env(nil, args, nil, "") }}, |
| {"LABEL", func(args []string) error { return label(nil, args, nil, "") }}} |
| |
| for _, command := range commands { |
| err := command.function([]string{"arg1", "arg2", "arg3"}) |
| |
| if err == nil { |
| t.Fatalf("Error should be present for %s command", command.name) |
| } |
| |
| expectedError := errTooManyArguments(command.name) |
| |
| if err.Error() != expectedError.Error() { |
| t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError) |
| } |
| } |
| } |
| |
| func TestCommandseBlankNames(t *testing.T) { |
| bflags := &BFlags{} |
| config := &container.Config{} |
| |
| b := &Builder{flags: bflags, runConfig: config, disableCommit: true} |
| |
| commands := []commandWithFunction{ |
| {"ENV", func(args []string) error { return env(b, args, nil, "") }}, |
| {"LABEL", func(args []string) error { return label(b, args, nil, "") }}, |
| } |
| |
| for _, command := range commands { |
| err := command.function([]string{"", ""}) |
| |
| if err == nil { |
| t.Fatalf("Error should be present for %s command", command.name) |
| } |
| |
| expectedError := errBlankCommandNames(command.name) |
| |
| if err.Error() != expectedError.Error() { |
| t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError) |
| } |
| } |
| } |
| |
| func TestEnv2Variables(t *testing.T) { |
| variables := []string{"var1", "val1", "var2", "val2"} |
| |
| bflags := &BFlags{} |
| config := &container.Config{} |
| |
| b := &Builder{flags: bflags, runConfig: config, disableCommit: true} |
| |
| if err := env(b, variables, nil, ""); err != nil { |
| t.Fatalf("Error when executing env: %s", err.Error()) |
| } |
| |
| expectedVar1 := fmt.Sprintf("%s=%s", variables[0], variables[1]) |
| expectedVar2 := fmt.Sprintf("%s=%s", variables[2], variables[3]) |
| |
| if b.runConfig.Env[0] != expectedVar1 { |
| t.Fatalf("Wrong env output for first variable. Got: %s. Should be: %s", b.runConfig.Env[0], expectedVar1) |
| } |
| |
| if b.runConfig.Env[1] != expectedVar2 { |
| t.Fatalf("Wrong env output for second variable. Got: %s, Should be: %s", b.runConfig.Env[1], expectedVar2) |
| } |
| } |
| |
| func TestMaintainer(t *testing.T) { |
| maintainerEntry := "Some Maintainer <maintainer@example.com>" |
| |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| if err := maintainer(b, []string{maintainerEntry}, nil, ""); err != nil { |
| t.Fatalf("Error when executing maintainer: %s", err.Error()) |
| } |
| |
| if b.maintainer != maintainerEntry { |
| t.Fatalf("Maintainer in builder should be set to %s. Got: %s", maintainerEntry, b.maintainer) |
| } |
| } |
| |
| func TestLabel(t *testing.T) { |
| labelName := "label" |
| labelValue := "value" |
| |
| labelEntry := []string{labelName, labelValue} |
| |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| if err := label(b, labelEntry, nil, ""); err != nil { |
| t.Fatalf("Error when executing label: %s", err.Error()) |
| } |
| |
| if val, ok := b.runConfig.Labels[labelName]; ok { |
| if val != labelValue { |
| t.Fatalf("Label %s should have value %s, had %s instead", labelName, labelValue, val) |
| } |
| } else { |
| t.Fatalf("Label %s should be present but it is not", labelName) |
| } |
| } |
| |
| func TestFrom(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| err := from(b, []string{"scratch"}, nil, "") |
| |
| if runtime.GOOS == "windows" { |
| if err == nil { |
| t.Fatalf("Error not set on Windows") |
| } |
| |
| expectedError := "Windows does not support FROM scratch" |
| |
| if !strings.Contains(err.Error(), expectedError) { |
| t.Fatalf("Error message not correct on Windows. Should be: %s, got: %s", expectedError, err.Error()) |
| } |
| } else { |
| if err != nil { |
| t.Fatalf("Error when executing from: %s", err.Error()) |
| } |
| |
| if b.image != "" { |
| t.Fatalf("Image shoule be empty, got: %s", b.image) |
| } |
| |
| if b.noBaseImage != true { |
| t.Fatalf("Image should not have any base image, got: %v", b.noBaseImage) |
| } |
| } |
| } |
| |
| func TestOnbuildIllegalTriggers(t *testing.T) { |
| triggers := []struct{ command, expectedError string }{ |
| {"ONBUILD", "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed"}, |
| {"MAINTAINER", "MAINTAINER isn't allowed as an ONBUILD trigger"}, |
| {"FROM", "FROM isn't allowed as an ONBUILD trigger"}} |
| |
| for _, trigger := range triggers { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| err := onbuild(b, []string{trigger.command}, nil, "") |
| |
| if err == nil { |
| t.Fatalf("Error should not be nil") |
| } |
| |
| if !strings.Contains(err.Error(), trigger.expectedError) { |
| t.Fatalf("Error message not correct. Should be: %s, got: %s", trigger.expectedError, err.Error()) |
| } |
| } |
| } |
| |
| func TestOnbuild(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| err := onbuild(b, []string{"ADD", ".", "/app/src"}, nil, "ONBUILD ADD . /app/src") |
| |
| if err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| expectedOnbuild := "ADD . /app/src" |
| |
| if b.runConfig.OnBuild[0] != expectedOnbuild { |
| t.Fatalf("Wrong ONBUILD command. Expected: %s, got: %s", expectedOnbuild, b.runConfig.OnBuild[0]) |
| } |
| } |
| |
| func TestWorkdir(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| workingDir := "/app" |
| |
| if runtime.GOOS == "windows" { |
| workingDir = "C:\app" |
| } |
| |
| err := workdir(b, []string{workingDir}, nil, "") |
| |
| if err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.WorkingDir != workingDir { |
| t.Fatalf("WorkingDir should be set to %s, got %s", workingDir, b.runConfig.WorkingDir) |
| } |
| |
| } |
| |
| func TestCmd(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| command := "./executable" |
| |
| err := cmd(b, []string{command}, nil, "") |
| |
| if err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| var expectedCommand strslice.StrSlice |
| |
| if runtime.GOOS == "windows" { |
| expectedCommand = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", command)) |
| } else { |
| expectedCommand = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", command)) |
| } |
| |
| if !compareStrSlice(b.runConfig.Cmd, expectedCommand) { |
| t.Fatalf("Command should be set to %s, got %s", command, b.runConfig.Cmd) |
| } |
| |
| if !b.cmdSet { |
| t.Fatalf("Command should be marked as set") |
| } |
| } |
| |
| func compareStrSlice(slice1, slice2 strslice.StrSlice) bool { |
| if len(slice1) != len(slice2) { |
| return false |
| } |
| |
| for i := range slice1 { |
| if slice1[i] != slice2[i] { |
| return false |
| } |
| } |
| |
| return true |
| } |
| |
| func TestHealthcheckNone(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| if err := healthcheck(b, []string{"NONE"}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.Healthcheck == nil { |
| t.Fatal("Healthcheck should be set, got nil") |
| } |
| |
| expectedTest := strslice.StrSlice(append([]string{"NONE"})) |
| |
| if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) { |
| t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test) |
| } |
| } |
| |
| func TestHealthcheckCmd(t *testing.T) { |
| b := &Builder{flags: &BFlags{flags: make(map[string]*Flag)}, runConfig: &container.Config{}, disableCommit: true} |
| |
| if err := healthcheck(b, []string{"CMD", "curl", "-f", "http://localhost/", "||", "exit", "1"}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.Healthcheck == nil { |
| t.Fatal("Healthcheck should be set, got nil") |
| } |
| |
| expectedTest := strslice.StrSlice(append([]string{"CMD-SHELL"}, "curl -f http://localhost/ || exit 1")) |
| |
| if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) { |
| t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test) |
| } |
| } |
| |
| func TestEntrypoint(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| entrypointCmd := "/usr/sbin/nginx" |
| |
| if err := entrypoint(b, []string{entrypointCmd}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.Entrypoint == nil { |
| t.Fatalf("Entrypoint should be set") |
| } |
| |
| var expectedEntrypoint strslice.StrSlice |
| |
| if runtime.GOOS == "windows" { |
| expectedEntrypoint = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", entrypointCmd)) |
| } else { |
| expectedEntrypoint = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", entrypointCmd)) |
| } |
| |
| if !compareStrSlice(expectedEntrypoint, b.runConfig.Entrypoint) { |
| t.Fatalf("Entrypoint command should be set to %s, got %s", expectedEntrypoint, b.runConfig.Entrypoint) |
| } |
| } |
| |
| func TestExpose(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| exposedPort := "80" |
| |
| if err := expose(b, []string{exposedPort}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.ExposedPorts == nil { |
| t.Fatalf("ExposedPorts should be set") |
| } |
| |
| if len(b.runConfig.ExposedPorts) != 1 { |
| t.Fatalf("ExposedPorts should contain only 1 element. Got %s", b.runConfig.ExposedPorts) |
| } |
| |
| portsMapping, err := nat.ParsePortSpec(exposedPort) |
| |
| if err != nil { |
| t.Fatalf("Error when parsing port spec: %s", err.Error()) |
| } |
| |
| if _, ok := b.runConfig.ExposedPorts[portsMapping[0].Port]; !ok { |
| t.Fatalf("Port %s should be present. Got %s", exposedPort, b.runConfig.ExposedPorts) |
| } |
| } |
| |
| func TestUser(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| userCommand := "foo" |
| |
| if err := user(b, []string{userCommand}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.User != userCommand { |
| t.Fatalf("User should be set to %s, got %s", userCommand, b.runConfig.User) |
| } |
| } |
| |
| func TestVolume(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| exposedVolume := "/foo" |
| |
| if err := volume(b, []string{exposedVolume}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.Volumes == nil { |
| t.Fatalf("Volumes should be set") |
| } |
| |
| if len(b.runConfig.Volumes) != 1 { |
| t.Fatalf("Volumes should contain only 1 element. Got %s", b.runConfig.Volumes) |
| } |
| |
| if _, ok := b.runConfig.Volumes[exposedVolume]; !ok { |
| t.Fatalf("Volume %s should be present. Got %s", exposedVolume, b.runConfig.Volumes) |
| } |
| } |
| |
| func TestStopSignal(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| signal := "SIGKILL" |
| |
| if err := stopSignal(b, []string{signal}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.StopSignal != signal { |
| t.Fatalf("StopSignal should be set to %s, got %s", signal, b.runConfig.StopSignal) |
| } |
| } |
| |
| func TestArg(t *testing.T) { |
| buildOptions := &types.ImageBuildOptions{BuildArgs: make(map[string]*string)} |
| |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true, allowedBuildArgs: make(map[string]bool), options: buildOptions} |
| |
| argName := "foo" |
| argVal := "bar" |
| argDef := fmt.Sprintf("%s=%s", argName, argVal) |
| |
| if err := arg(b, []string{argDef}, nil, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| allowed, ok := b.allowedBuildArgs[argName] |
| |
| if !ok { |
| t.Fatalf("%s argument should be allowed as a build arg", argName) |
| } |
| |
| if !allowed { |
| t.Fatalf("%s argument was present in map but disallowed as a build arg", argName) |
| } |
| |
| val, ok := b.options.BuildArgs[argName] |
| |
| if !ok { |
| t.Fatalf("%s argument should be a build arg", argName) |
| } |
| |
| if *val != "bar" { |
| t.Fatalf("%s argument should have default value 'bar', got %s", argName, val) |
| } |
| } |
| |
| func TestShell(t *testing.T) { |
| b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true} |
| |
| shellCmd := "powershell" |
| |
| attrs := make(map[string]bool) |
| attrs["json"] = true |
| |
| if err := shell(b, []string{shellCmd}, attrs, ""); err != nil { |
| t.Fatalf("Error should be empty, got: %s", err.Error()) |
| } |
| |
| if b.runConfig.Shell == nil { |
| t.Fatalf("Shell should be set") |
| } |
| |
| expectedShell := strslice.StrSlice([]string{shellCmd}) |
| |
| if !compareStrSlice(expectedShell, b.runConfig.Shell) { |
| t.Fatalf("Shell should be set to %s, got %s", expectedShell, b.runConfig.Shell) |
| } |
| } |