diff --git a/builder/dockerfile/dispatchers_test.go b/builder/dockerfile/dispatchers_test.go index 60c40da4f0..75d1185a0f 100644 --- a/builder/dockerfile/dispatchers_test.go +++ b/builder/dockerfile/dispatchers_test.go @@ -6,7 +6,10 @@ import ( "strings" "testing" + "github.com/docker/engine-api/types" "github.com/docker/engine-api/types/container" + "github.com/docker/engine-api/types/strslice" + "github.com/docker/go-connections/nat" ) type commandWithFunction struct { @@ -206,3 +209,263 @@ func TestOnbuild(t *testing.T) { 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) + } +} diff --git a/builder/dockerfile/dispatchers_unix_test.go b/builder/dockerfile/dispatchers_unix_test.go new file mode 100644 index 0000000000..4aae6b460e --- /dev/null +++ b/builder/dockerfile/dispatchers_unix_test.go @@ -0,0 +1,33 @@ +// +build !windows + +package dockerfile + +import ( + "testing" +) + +func TestNormaliseWorkdir(t *testing.T) { + testCases := []struct{ current, requested, expected, expectedError string }{ + {``, ``, ``, `cannot normalise nothing`}, + {``, `foo`, `/foo`, ``}, + {``, `/foo`, `/foo`, ``}, + {`/foo`, `bar`, `/foo/bar`, ``}, + {`/foo`, `/bar`, `/bar`, ``}, + } + + for _, test := range testCases { + normalised, err := normaliseWorkdir(test.current, test.requested) + + if test.expectedError != "" && err == nil { + t.Fatalf("NormaliseWorkdir should return an error %s, got nil", test.expectedError) + } + + if test.expectedError != "" && err.Error() != test.expectedError { + t.Fatalf("NormaliseWorkdir returned wrong error. Expected %s, got %s", test.expectedError, err.Error()) + } + + if normalised != test.expected { + t.Fatalf("NormaliseWorkdir error. Expected %s for current %s and requested %s, got %s", test.expected, test.current, test.requested, normalised) + } + } +} diff --git a/builder/dockerfile/internals_test.go b/builder/dockerfile/internals_test.go index 7ab22156e4..279a6f8912 100644 --- a/builder/dockerfile/internals_test.go +++ b/builder/dockerfile/internals_test.go @@ -34,6 +34,24 @@ func TestSymlinkDockerfile(t *testing.T) { readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError) } +func TestDockerfileOutsideTheBuildContext(t *testing.T) { + contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") + defer cleanup() + + expectedError := "Forbidden path outside the build context" + + readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError) +} + +func TestNonExistingDockerfile(t *testing.T) { + contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") + defer cleanup() + + expectedError := "Cannot locate specified Dockerfile: Dockerfile" + + readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError) +} + func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) { tarStream, err := archive.Tar(contextDir, archive.Uncompressed) @@ -75,50 +93,3 @@ func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", expectedError, err.Error()) } } - -func TestDockerfileOutsideTheBuildContext(t *testing.T) { - contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") - defer cleanup() - - tarStream, err := archive.Tar(contextDir, archive.Uncompressed) - - if err != nil { - t.Fatalf("Error when creating tar stream: %s", err) - } - - defer func() { - if err = tarStream.Close(); err != nil { - t.Fatalf("Error when closing tar stream: %s", err) - } - }() - - context, err := builder.MakeTarSumContext(tarStream) - - if err != nil { - t.Fatalf("Error when creating tar context: %s", err) - } - - defer func() { - if err = context.Close(); err != nil { - t.Fatalf("Error when closing tar context: %s", err) - } - }() - - options := &types.ImageBuildOptions{ - Dockerfile: "../../Dockerfile", - } - - b := &Builder{options: options, context: context} - - err = b.readDockerfile() - - if err == nil { - t.Fatalf("No error when executing test for Dockerfile outside the build context") - } - - expectedError := "Forbidden path outside the build context" - - if !strings.Contains(err.Error(), expectedError) { - t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", expectedError, err.Error()) - } -}