github.com/shishir-a412ed/docker@v1.3.2-0.20180103180333-fda904911d87/builder/dockerfile/internals_test.go (about) 1 package dockerfile 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "runtime" 8 "testing" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/backend" 12 "github.com/docker/docker/api/types/container" 13 "github.com/docker/docker/builder" 14 "github.com/docker/docker/builder/remotecontext" 15 "github.com/docker/docker/pkg/archive" 16 "github.com/docker/docker/pkg/idtools" 17 "github.com/docker/go-connections/nat" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestEmptyDockerfile(t *testing.T) { 23 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 24 defer cleanup() 25 26 createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777) 27 28 readAndCheckDockerfile(t, "emptyDockerfile", contextDir, "", "the Dockerfile (Dockerfile) cannot be empty") 29 } 30 31 func TestSymlinkDockerfile(t *testing.T) { 32 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 33 defer cleanup() 34 35 createTestSymlink(t, contextDir, builder.DefaultDockerfileName, "/etc/passwd") 36 37 // The reason the error is "Cannot locate specified Dockerfile" is because 38 // in the builder, the symlink is resolved within the context, therefore 39 // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is 40 // a nonexistent file. 41 expectedError := fmt.Sprintf("Cannot locate specified Dockerfile: %s", builder.DefaultDockerfileName) 42 43 readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError) 44 } 45 46 func TestDockerfileOutsideTheBuildContext(t *testing.T) { 47 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 48 defer cleanup() 49 50 expectedError := "Forbidden path outside the build context: ../../Dockerfile ()" 51 52 readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError) 53 } 54 55 func TestNonExistingDockerfile(t *testing.T) { 56 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 57 defer cleanup() 58 59 expectedError := "Cannot locate specified Dockerfile: Dockerfile" 60 61 readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError) 62 } 63 64 func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) { 65 tarStream, err := archive.Tar(contextDir, archive.Uncompressed) 66 require.NoError(t, err) 67 68 defer func() { 69 if err = tarStream.Close(); err != nil { 70 t.Fatalf("Error when closing tar stream: %s", err) 71 } 72 }() 73 74 if dockerfilePath == "" { // handled in BuildWithContext 75 dockerfilePath = builder.DefaultDockerfileName 76 } 77 78 config := backend.BuildConfig{ 79 Options: &types.ImageBuildOptions{Dockerfile: dockerfilePath}, 80 Source: tarStream, 81 } 82 _, _, err = remotecontext.Detect(config) 83 assert.EqualError(t, err, expectedError) 84 } 85 86 func TestCopyRunConfig(t *testing.T) { 87 defaultEnv := []string{"foo=1"} 88 defaultCmd := []string{"old"} 89 90 var testcases = []struct { 91 doc string 92 modifiers []runConfigModifier 93 expected *container.Config 94 }{ 95 { 96 doc: "Set the command", 97 modifiers: []runConfigModifier{withCmd([]string{"new"})}, 98 expected: &container.Config{ 99 Cmd: []string{"new"}, 100 Env: defaultEnv, 101 }, 102 }, 103 { 104 doc: "Set the command to a comment", 105 modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)}, 106 expected: &container.Config{ 107 Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"), 108 Env: defaultEnv, 109 }, 110 }, 111 { 112 doc: "Set the command and env", 113 modifiers: []runConfigModifier{ 114 withCmd([]string{"new"}), 115 withEnv([]string{"one", "two"}), 116 }, 117 expected: &container.Config{ 118 Cmd: []string{"new"}, 119 Env: []string{"one", "two"}, 120 }, 121 }, 122 } 123 124 for _, testcase := range testcases { 125 runConfig := &container.Config{ 126 Cmd: defaultCmd, 127 Env: defaultEnv, 128 } 129 runConfigCopy := copyRunConfig(runConfig, testcase.modifiers...) 130 assert.Equal(t, testcase.expected, runConfigCopy, testcase.doc) 131 // Assert the original was not modified 132 assert.NotEqual(t, runConfig, runConfigCopy, testcase.doc) 133 } 134 135 } 136 137 func fullMutableRunConfig() *container.Config { 138 return &container.Config{ 139 Cmd: []string{"command", "arg1"}, 140 Env: []string{"env1=foo", "env2=bar"}, 141 ExposedPorts: nat.PortSet{ 142 "1000/tcp": {}, 143 "1001/tcp": {}, 144 }, 145 Volumes: map[string]struct{}{ 146 "one": {}, 147 "two": {}, 148 }, 149 Entrypoint: []string{"entry", "arg1"}, 150 OnBuild: []string{"first", "next"}, 151 Labels: map[string]string{ 152 "label1": "value1", 153 "label2": "value2", 154 }, 155 Shell: []string{"shell", "-c"}, 156 } 157 } 158 159 func TestDeepCopyRunConfig(t *testing.T) { 160 runConfig := fullMutableRunConfig() 161 copy := copyRunConfig(runConfig) 162 assert.Equal(t, fullMutableRunConfig(), copy) 163 164 copy.Cmd[1] = "arg2" 165 copy.Env[1] = "env2=new" 166 copy.ExposedPorts["10002"] = struct{}{} 167 copy.Volumes["three"] = struct{}{} 168 copy.Entrypoint[1] = "arg2" 169 copy.OnBuild[0] = "start" 170 copy.Labels["label3"] = "value3" 171 copy.Shell[0] = "sh" 172 assert.Equal(t, fullMutableRunConfig(), runConfig) 173 } 174 175 func TestChownFlagParsing(t *testing.T) { 176 testFiles := map[string]string{ 177 "passwd": `root:x:0:0::/bin:/bin/false 178 bin:x:1:1::/bin:/bin/false 179 wwwwww:x:21:33::/bin:/bin/false 180 unicorn:x:1001:1002::/bin:/bin/false 181 `, 182 "group": `root:x:0: 183 bin:x:1: 184 wwwwww:x:33: 185 unicorn:x:1002: 186 somegrp:x:5555: 187 othergrp:x:6666: 188 `, 189 } 190 // test mappings for validating use of maps 191 idMaps := []idtools.IDMap{ 192 { 193 ContainerID: 0, 194 HostID: 100000, 195 Size: 65536, 196 }, 197 } 198 remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps) 199 unmapped := &idtools.IDMappings{} 200 201 contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test") 202 defer cleanup() 203 204 if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil { 205 t.Fatalf("error creating test directory: %v", err) 206 } 207 208 for filename, content := range testFiles { 209 createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644) 210 } 211 212 // positive tests 213 for _, testcase := range []struct { 214 name string 215 chownStr string 216 idMapping *idtools.IDMappings 217 expected idtools.IDPair 218 }{ 219 { 220 name: "UIDNoMap", 221 chownStr: "1", 222 idMapping: unmapped, 223 expected: idtools.IDPair{UID: 1, GID: 1}, 224 }, 225 { 226 name: "UIDGIDNoMap", 227 chownStr: "0:1", 228 idMapping: unmapped, 229 expected: idtools.IDPair{UID: 0, GID: 1}, 230 }, 231 { 232 name: "UIDWithMap", 233 chownStr: "0", 234 idMapping: remapped, 235 expected: idtools.IDPair{UID: 100000, GID: 100000}, 236 }, 237 { 238 name: "UIDGIDWithMap", 239 chownStr: "1:33", 240 idMapping: remapped, 241 expected: idtools.IDPair{UID: 100001, GID: 100033}, 242 }, 243 { 244 name: "UserNoMap", 245 chownStr: "bin:5555", 246 idMapping: unmapped, 247 expected: idtools.IDPair{UID: 1, GID: 5555}, 248 }, 249 { 250 name: "GroupWithMap", 251 chownStr: "0:unicorn", 252 idMapping: remapped, 253 expected: idtools.IDPair{UID: 100000, GID: 101002}, 254 }, 255 { 256 name: "UserOnlyWithMap", 257 chownStr: "unicorn", 258 idMapping: remapped, 259 expected: idtools.IDPair{UID: 101001, GID: 101002}, 260 }, 261 } { 262 t.Run(testcase.name, func(t *testing.T) { 263 idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping) 264 require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr) 265 assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure") 266 }) 267 } 268 269 // error tests 270 for _, testcase := range []struct { 271 name string 272 chownStr string 273 idMapping *idtools.IDMappings 274 descr string 275 }{ 276 { 277 name: "BadChownFlagFormat", 278 chownStr: "bob:1:555", 279 idMapping: unmapped, 280 descr: "invalid chown string format: bob:1:555", 281 }, 282 { 283 name: "UserNoExist", 284 chownStr: "bob", 285 idMapping: unmapped, 286 descr: "can't find uid for user bob: no such user: bob", 287 }, 288 { 289 name: "GroupNoExist", 290 chownStr: "root:bob", 291 idMapping: unmapped, 292 descr: "can't find gid for group bob: no such group: bob", 293 }, 294 } { 295 t.Run(testcase.name, func(t *testing.T) { 296 _, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping) 297 assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match") 298 }) 299 } 300 }