github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/builder/dockerfile/internals_test.go (about) 1 package dockerfile // import "github.com/docker/docker/builder/dockerfile" 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "testing" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/backend" 11 "github.com/docker/docker/api/types/container" 12 "github.com/docker/docker/builder" 13 "github.com/docker/docker/builder/remotecontext" 14 "github.com/docker/docker/pkg/archive" 15 "github.com/docker/go-connections/nat" 16 "gotest.tools/assert" 17 is "gotest.tools/assert/cmp" 18 "gotest.tools/skip" 19 ) 20 21 func TestEmptyDockerfile(t *testing.T) { 22 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 23 defer cleanup() 24 25 createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777) 26 27 readAndCheckDockerfile(t, "emptyDockerfile", contextDir, "", "the Dockerfile (Dockerfile) cannot be empty") 28 } 29 30 func TestSymlinkDockerfile(t *testing.T) { 31 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 32 defer cleanup() 33 34 createTestSymlink(t, contextDir, builder.DefaultDockerfileName, "/etc/passwd") 35 36 // The reason the error is "Cannot locate specified Dockerfile" is because 37 // in the builder, the symlink is resolved within the context, therefore 38 // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is 39 // a nonexistent file. 40 expectedError := fmt.Sprintf("Cannot locate specified Dockerfile: %s", builder.DefaultDockerfileName) 41 42 readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError) 43 } 44 45 func TestDockerfileOutsideTheBuildContext(t *testing.T) { 46 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 47 defer cleanup() 48 49 expectedError := "Forbidden path outside the build context: ../../Dockerfile ()" 50 if runtime.GOOS == "windows" { 51 expectedError = "failed to resolve scoped path ../../Dockerfile ()" 52 } 53 54 readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError) 55 } 56 57 func TestNonExistingDockerfile(t *testing.T) { 58 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 59 defer cleanup() 60 61 expectedError := "Cannot locate specified Dockerfile: Dockerfile" 62 63 readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError) 64 } 65 66 func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) { 67 if runtime.GOOS != "windows" { 68 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 69 } 70 tarStream, err := archive.Tar(contextDir, archive.Uncompressed) 71 assert.NilError(t, err) 72 73 defer func() { 74 if err = tarStream.Close(); err != nil { 75 t.Fatalf("Error when closing tar stream: %s", err) 76 } 77 }() 78 79 if dockerfilePath == "" { // handled in BuildWithContext 80 dockerfilePath = builder.DefaultDockerfileName 81 } 82 83 config := backend.BuildConfig{ 84 Options: &types.ImageBuildOptions{Dockerfile: dockerfilePath}, 85 Source: tarStream, 86 } 87 _, _, err = remotecontext.Detect(config) 88 assert.Check(t, is.ErrorContains(err, expectedError)) 89 } 90 91 func TestCopyRunConfig(t *testing.T) { 92 defaultEnv := []string{"foo=1"} 93 defaultCmd := []string{"old"} 94 95 var testcases = []struct { 96 doc string 97 modifiers []runConfigModifier 98 expected *container.Config 99 }{ 100 { 101 doc: "Set the command", 102 modifiers: []runConfigModifier{withCmd([]string{"new"})}, 103 expected: &container.Config{ 104 Cmd: []string{"new"}, 105 Env: defaultEnv, 106 }, 107 }, 108 { 109 doc: "Set the command to a comment", 110 modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)}, 111 expected: &container.Config{ 112 Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"), 113 Env: defaultEnv, 114 }, 115 }, 116 { 117 doc: "Set the command and env", 118 modifiers: []runConfigModifier{ 119 withCmd([]string{"new"}), 120 withEnv([]string{"one", "two"}), 121 }, 122 expected: &container.Config{ 123 Cmd: []string{"new"}, 124 Env: []string{"one", "two"}, 125 }, 126 }, 127 } 128 129 for _, testcase := range testcases { 130 runConfig := &container.Config{ 131 Cmd: defaultCmd, 132 Env: defaultEnv, 133 } 134 runConfigCopy := copyRunConfig(runConfig, testcase.modifiers...) 135 assert.Check(t, is.DeepEqual(testcase.expected, runConfigCopy), testcase.doc) 136 // Assert the original was not modified 137 assert.Check(t, runConfig != runConfigCopy, testcase.doc) 138 } 139 140 } 141 142 func fullMutableRunConfig() *container.Config { 143 return &container.Config{ 144 Cmd: []string{"command", "arg1"}, 145 Env: []string{"env1=foo", "env2=bar"}, 146 ExposedPorts: nat.PortSet{ 147 "1000/tcp": {}, 148 "1001/tcp": {}, 149 }, 150 Volumes: map[string]struct{}{ 151 "one": {}, 152 "two": {}, 153 }, 154 Entrypoint: []string{"entry", "arg1"}, 155 OnBuild: []string{"first", "next"}, 156 Labels: map[string]string{ 157 "label1": "value1", 158 "label2": "value2", 159 }, 160 Shell: []string{"shell", "-c"}, 161 } 162 } 163 164 func TestDeepCopyRunConfig(t *testing.T) { 165 runConfig := fullMutableRunConfig() 166 copy := copyRunConfig(runConfig) 167 assert.Check(t, is.DeepEqual(fullMutableRunConfig(), copy)) 168 169 copy.Cmd[1] = "arg2" 170 copy.Env[1] = "env2=new" 171 copy.ExposedPorts["10002"] = struct{}{} 172 copy.Volumes["three"] = struct{}{} 173 copy.Entrypoint[1] = "arg2" 174 copy.OnBuild[0] = "start" 175 copy.Labels["label3"] = "value3" 176 copy.Shell[0] = "sh" 177 assert.Check(t, is.DeepEqual(fullMutableRunConfig(), runConfig)) 178 }