github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/integration-cli/docker_cli_build_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "regexp" 13 "runtime" 14 "strconv" 15 "strings" 16 "text/template" 17 "time" 18 19 "github.com/docker/docker/builder/dockerfile/command" 20 "github.com/docker/docker/integration-cli/checker" 21 "github.com/docker/docker/pkg/archive" 22 "github.com/docker/docker/pkg/stringutils" 23 "github.com/docker/docker/pkg/testutil" 24 icmd "github.com/docker/docker/pkg/testutil/cmd" 25 "github.com/go-check/check" 26 ) 27 28 func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) { 29 buildImageSuccessfully(c, "testbuildjsonemptyrun", withDockerfile(` 30 FROM busybox 31 RUN [] 32 `)) 33 } 34 35 func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) { 36 name := "testbuildshcmdjsonentrypoint" 37 expected := "/bin/sh -c echo test" 38 if testEnv.DaemonPlatform() == "windows" { 39 expected = "cmd /S /C echo test" 40 } 41 42 buildImageSuccessfully(c, name, withDockerfile(` 43 FROM busybox 44 ENTRYPOINT ["echo"] 45 CMD echo test 46 `)) 47 out, _ := dockerCmd(c, "run", "--rm", name) 48 49 if strings.TrimSpace(out) != expected { 50 c.Fatalf("CMD did not contain %q : %q", expected, out) 51 } 52 } 53 54 func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *check.C) { 55 // Windows does not support FROM scratch or the USER command 56 testRequires(c, DaemonIsLinux) 57 name := "testbuildenvironmentreplacement" 58 59 buildImageSuccessfully(c, name, withDockerfile(` 60 FROM scratch 61 ENV user foo 62 USER ${user} 63 `)) 64 res := inspectFieldJSON(c, name, "Config.User") 65 66 if res != `"foo"` { 67 c.Fatal("User foo from environment not in Config.User on image") 68 } 69 } 70 71 func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *check.C) { 72 name := "testbuildenvironmentreplacement" 73 74 var volumePath string 75 76 if testEnv.DaemonPlatform() == "windows" { 77 volumePath = "c:/quux" 78 } else { 79 volumePath = "/quux" 80 } 81 82 buildImageSuccessfully(c, name, withDockerfile(` 83 FROM `+minimalBaseImage()+` 84 ENV volume `+volumePath+` 85 VOLUME ${volume} 86 `)) 87 88 var volumes map[string]interface{} 89 inspectFieldAndUnmarshall(c, name, "Config.Volumes", &volumes) 90 if _, ok := volumes[volumePath]; !ok { 91 c.Fatal("Volume " + volumePath + " from environment not in Config.Volumes on image") 92 } 93 94 } 95 96 func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) { 97 // Windows does not support FROM scratch or the EXPOSE command 98 testRequires(c, DaemonIsLinux) 99 name := "testbuildenvironmentreplacement" 100 101 buildImageSuccessfully(c, name, withDockerfile(` 102 FROM scratch 103 ENV port 80 104 EXPOSE ${port} 105 ENV ports " 99 100 " 106 EXPOSE ${ports} 107 `)) 108 109 var exposedPorts map[string]interface{} 110 inspectFieldAndUnmarshall(c, name, "Config.ExposedPorts", &exposedPorts) 111 exp := []int{80, 99, 100} 112 for _, p := range exp { 113 tmp := fmt.Sprintf("%d/tcp", p) 114 if _, ok := exposedPorts[tmp]; !ok { 115 c.Fatalf("Exposed port %d from environment not in Config.ExposedPorts on image", p) 116 } 117 } 118 119 } 120 121 func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *check.C) { 122 name := "testbuildenvironmentreplacement" 123 124 buildImageSuccessfully(c, name, withDockerfile(` 125 FROM busybox 126 ENV MYWORKDIR /work 127 RUN mkdir ${MYWORKDIR} 128 WORKDIR ${MYWORKDIR} 129 `)) 130 res := inspectFieldJSON(c, name, "Config.WorkingDir") 131 132 expected := `"/work"` 133 if testEnv.DaemonPlatform() == "windows" { 134 expected = `"C:\\work"` 135 } 136 if res != expected { 137 c.Fatalf("Workdir /workdir from environment not in Config.WorkingDir on image: %s", res) 138 } 139 } 140 141 func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *check.C) { 142 name := "testbuildenvironmentreplacement" 143 144 buildImageSuccessfully(c, name, withBuildContext(c, 145 withFile("Dockerfile", ` 146 FROM `+minimalBaseImage()+` 147 ENV baz foo 148 ENV quux bar 149 ENV dot . 150 ENV fee fff 151 ENV gee ggg 152 153 ADD ${baz} ${dot} 154 COPY ${quux} ${dot} 155 ADD ${zzz:-${fee}} ${dot} 156 COPY ${zzz:-${gee}} ${dot} 157 `), 158 withFile("foo", "test1"), 159 withFile("bar", "test2"), 160 withFile("fff", "test3"), 161 withFile("ggg", "test4"), 162 )) 163 } 164 165 func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) { 166 // ENV expansions work differently in Windows 167 testRequires(c, DaemonIsLinux) 168 name := "testbuildenvironmentreplacement" 169 170 buildImageSuccessfully(c, name, withDockerfile(` 171 FROM busybox 172 ENV foo zzz 173 ENV bar ${foo} 174 ENV abc1='$foo' 175 ENV env1=$foo env2=${foo} env3="$foo" env4="${foo}" 176 RUN [ "$abc1" = '$foo' ] && (echo "$abc1" | grep -q foo) 177 ENV abc2="\$foo" 178 RUN [ "$abc2" = '$foo' ] && (echo "$abc2" | grep -q foo) 179 ENV abc3 '$foo' 180 RUN [ "$abc3" = '$foo' ] && (echo "$abc3" | grep -q foo) 181 ENV abc4 "\$foo" 182 RUN [ "$abc4" = '$foo' ] && (echo "$abc4" | grep -q foo) 183 `)) 184 185 envResult := []string{} 186 inspectFieldAndUnmarshall(c, name, "Config.Env", &envResult) 187 found := false 188 envCount := 0 189 190 for _, env := range envResult { 191 parts := strings.SplitN(env, "=", 2) 192 if parts[0] == "bar" { 193 found = true 194 if parts[1] != "zzz" { 195 c.Fatalf("Could not find replaced var for env `bar`: got %q instead of `zzz`", parts[1]) 196 } 197 } else if strings.HasPrefix(parts[0], "env") { 198 envCount++ 199 if parts[1] != "zzz" { 200 c.Fatalf("%s should be 'foo' but instead its %q", parts[0], parts[1]) 201 } 202 } else if strings.HasPrefix(parts[0], "env") { 203 envCount++ 204 if parts[1] != "foo" { 205 c.Fatalf("%s should be 'foo' but instead its %q", parts[0], parts[1]) 206 } 207 } 208 } 209 210 if !found { 211 c.Fatal("Never found the `bar` env variable") 212 } 213 214 if envCount != 4 { 215 c.Fatalf("Didn't find all env vars - only saw %d\n%s", envCount, envResult) 216 } 217 218 } 219 220 func (s *DockerSuite) TestBuildHandleEscapesInVolume(c *check.C) { 221 // The volume paths used in this test are invalid on Windows 222 testRequires(c, DaemonIsLinux) 223 name := "testbuildhandleescapes" 224 225 testCases := []struct { 226 volumeValue string 227 expected string 228 }{ 229 { 230 volumeValue: "${FOO}", 231 expected: "bar", 232 }, 233 { 234 volumeValue: `\${FOO}`, 235 expected: "${FOO}", 236 }, 237 // this test in particular provides *7* backslashes and expects 6 to come back. 238 // Like above, the first escape is swallowed and the rest are treated as 239 // literals, this one is just less obvious because of all the character noise. 240 { 241 volumeValue: `\\\\\\\${FOO}`, 242 expected: `\\\${FOO}`, 243 }, 244 } 245 246 for _, tc := range testCases { 247 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(` 248 FROM scratch 249 ENV FOO bar 250 VOLUME %s 251 `, tc.volumeValue))) 252 253 var result map[string]map[string]struct{} 254 inspectFieldAndUnmarshall(c, name, "Config.Volumes", &result) 255 if _, ok := result[tc.expected]; !ok { 256 c.Fatalf("Could not find volume %s set from env foo in volumes table, got %q", tc.expected, result) 257 } 258 259 // Remove the image for the next iteration 260 dockerCmd(c, "rmi", name) 261 } 262 } 263 264 func (s *DockerSuite) TestBuildOnBuildLowercase(c *check.C) { 265 name := "testbuildonbuildlowercase" 266 name2 := "testbuildonbuildlowercase2" 267 268 buildImageSuccessfully(c, name, withDockerfile(` 269 FROM busybox 270 onbuild run echo quux 271 `)) 272 273 result := buildImage(name2, withDockerfile(fmt.Sprintf(` 274 FROM %s 275 `, name))) 276 result.Assert(c, icmd.Success) 277 278 if !strings.Contains(result.Combined(), "quux") { 279 c.Fatalf("Did not receive the expected echo text, got %s", result.Combined()) 280 } 281 282 if strings.Contains(result.Combined(), "ONBUILD ONBUILD") { 283 c.Fatalf("Got an ONBUILD ONBUILD error with no error: got %s", result.Combined()) 284 } 285 286 } 287 288 func (s *DockerSuite) TestBuildEnvEscapes(c *check.C) { 289 // ENV expansions work differently in Windows 290 testRequires(c, DaemonIsLinux) 291 name := "testbuildenvescapes" 292 buildImageSuccessfully(c, name, withDockerfile(` 293 FROM busybox 294 ENV TEST foo 295 CMD echo \$ 296 `)) 297 298 out, _ := dockerCmd(c, "run", "-t", name) 299 if strings.TrimSpace(out) != "$" { 300 c.Fatalf("Env TEST was not overwritten with bar when foo was supplied to dockerfile: was %q", strings.TrimSpace(out)) 301 } 302 303 } 304 305 func (s *DockerSuite) TestBuildEnvOverwrite(c *check.C) { 306 // ENV expansions work differently in Windows 307 testRequires(c, DaemonIsLinux) 308 name := "testbuildenvoverwrite" 309 buildImageSuccessfully(c, name, withDockerfile(` 310 FROM busybox 311 ENV TEST foo 312 CMD echo ${TEST} 313 `)) 314 315 out, _ := dockerCmd(c, "run", "-e", "TEST=bar", "-t", name) 316 if strings.TrimSpace(out) != "bar" { 317 c.Fatalf("Env TEST was not overwritten with bar when foo was supplied to dockerfile: was %q", strings.TrimSpace(out)) 318 } 319 320 } 321 322 // FIXME(vdemeester) why we disabled cache here ? 323 func (s *DockerSuite) TestBuildOnBuildCmdEntrypointJSON(c *check.C) { 324 name1 := "onbuildcmd" 325 name2 := "onbuildgenerated" 326 327 buildImageSuccessfully(c, name1, withDockerfile(` 328 FROM busybox 329 ONBUILD CMD ["hello world"] 330 ONBUILD ENTRYPOINT ["echo"] 331 ONBUILD RUN ["true"]`)) 332 333 buildImageSuccessfully(c, name2, withDockerfile(fmt.Sprintf(`FROM %s`, name1))) 334 335 out, _ := dockerCmd(c, "run", name2) 336 if !regexp.MustCompile(`(?m)^hello world`).MatchString(out) { 337 c.Fatalf("did not get echo output from onbuild. Got: %q", out) 338 } 339 340 } 341 342 // FIXME(vdemeester) why we disabled cache here ? 343 func (s *DockerSuite) TestBuildOnBuildEntrypointJSON(c *check.C) { 344 name1 := "onbuildcmd" 345 name2 := "onbuildgenerated" 346 347 buildImageSuccessfully(c, name1, withDockerfile(` 348 FROM busybox 349 ONBUILD ENTRYPOINT ["echo"]`)) 350 351 buildImageSuccessfully(c, name2, withDockerfile(fmt.Sprintf("FROM %s\nCMD [\"hello world\"]\n", name1))) 352 353 out, _ := dockerCmd(c, "run", name2) 354 if !regexp.MustCompile(`(?m)^hello world`).MatchString(out) { 355 c.Fatal("got malformed output from onbuild", out) 356 } 357 358 } 359 360 func (s *DockerSuite) TestBuildCacheAdd(c *check.C) { 361 testRequires(c, DaemonIsLinux) // Windows doesn't have httpserver image yet 362 name := "testbuildtwoimageswithadd" 363 server := fakeStorage(c, map[string]string{ 364 "robots.txt": "hello", 365 "index.html": "world", 366 }) 367 defer server.Close() 368 369 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(`FROM scratch 370 ADD %s/robots.txt /`, server.URL()))) 371 372 result := buildImage(name, withDockerfile(fmt.Sprintf(`FROM scratch 373 ADD %s/index.html /`, server.URL()))) 374 result.Assert(c, icmd.Success) 375 if strings.Contains(result.Combined(), "Using cache") { 376 c.Fatal("2nd build used cache on ADD, it shouldn't") 377 } 378 } 379 380 func (s *DockerSuite) TestBuildLastModified(c *check.C) { 381 // Temporary fix for #30890. TODO @jhowardmsft figure out what 382 // has changed in the master busybox image. 383 testRequires(c, DaemonIsLinux) 384 385 name := "testbuildlastmodified" 386 387 server := fakeStorage(c, map[string]string{ 388 "file": "hello", 389 }) 390 defer server.Close() 391 392 var out, out2 string 393 394 dFmt := `FROM busybox 395 ADD %s/file /` 396 dockerfile := fmt.Sprintf(dFmt, server.URL()) 397 398 buildImageSuccessfully(c, name, withoutCache, withDockerfile(dockerfile)) 399 out, _ = dockerCmd(c, "run", name, "ls", "-le", "/file") 400 401 // Build it again and make sure the mtime of the file didn't change. 402 // Wait a few seconds to make sure the time changed enough to notice 403 time.Sleep(2 * time.Second) 404 405 buildImageSuccessfully(c, name, withoutCache, withDockerfile(dockerfile)) 406 out2, _ = dockerCmd(c, "run", name, "ls", "-le", "/file") 407 408 if out != out2 { 409 c.Fatalf("MTime changed:\nOrigin:%s\nNew:%s", out, out2) 410 } 411 412 // Now 'touch' the file and make sure the timestamp DID change this time 413 // Create a new fakeStorage instead of just using Add() to help windows 414 server = fakeStorage(c, map[string]string{ 415 "file": "hello", 416 }) 417 defer server.Close() 418 419 dockerfile = fmt.Sprintf(dFmt, server.URL()) 420 buildImageSuccessfully(c, name, withoutCache, withDockerfile(dockerfile)) 421 out2, _ = dockerCmd(c, "run", name, "ls", "-le", "/file") 422 423 if out == out2 { 424 c.Fatalf("MTime didn't change:\nOrigin:%s\nNew:%s", out, out2) 425 } 426 427 } 428 429 // Regression for https://github.com/docker/docker/pull/27805 430 // Makes sure that we don't use the cache if the contents of 431 // a file in a subfolder of the context is modified and we re-build. 432 func (s *DockerSuite) TestBuildModifyFileInFolder(c *check.C) { 433 name := "testbuildmodifyfileinfolder" 434 435 ctx := fakeContext(c, `FROM busybox 436 RUN ["mkdir", "/test"] 437 ADD folder/file /test/changetarget`, 438 map[string]string{}) 439 defer ctx.Close() 440 if err := ctx.Add("folder/file", "first"); err != nil { 441 c.Fatal(err) 442 } 443 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 444 id1 := getIDByName(c, name) 445 if err := ctx.Add("folder/file", "second"); err != nil { 446 c.Fatal(err) 447 } 448 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 449 id2 := getIDByName(c, name) 450 if id1 == id2 { 451 c.Fatal("cache was used even though file contents in folder was changed") 452 } 453 } 454 455 func (s *DockerSuite) TestBuildAddSingleFileToRoot(c *check.C) { 456 testRequires(c, DaemonIsLinux) // Linux specific test 457 buildImageSuccessfully(c, "testaddimg", withBuildContext(c, 458 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 459 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 460 RUN echo 'dockerio:x:1001:' >> /etc/group 461 RUN touch /exists 462 RUN chown dockerio.dockerio /exists 463 ADD test_file / 464 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] 465 RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ] 466 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod)), 467 withFile("test_file", "test1"))) 468 } 469 470 // Issue #3960: "ADD src ." hangs 471 func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *check.C) { 472 name := "testaddsinglefiletoworkdir" 473 ctx := fakeContext(c, `FROM busybox 474 ADD test_file .`, 475 map[string]string{ 476 "test_file": "test1", 477 }) 478 defer ctx.Close() 479 480 errChan := make(chan error) 481 go func() { 482 errChan <- buildImage(name, withExternalBuildContext(ctx)).Error 483 close(errChan) 484 }() 485 select { 486 case <-time.After(15 * time.Second): 487 c.Fatal("Build with adding to workdir timed out") 488 case err := <-errChan: 489 c.Assert(err, check.IsNil) 490 } 491 } 492 493 func (s *DockerSuite) TestBuildAddSingleFileToExistDir(c *check.C) { 494 testRequires(c, DaemonIsLinux) // Linux specific test 495 buildImageSuccessfully(c, "testaddsinglefiletoexistdir", withBuildContext(c, 496 withFile("Dockerfile", `FROM busybox 497 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 498 RUN echo 'dockerio:x:1001:' >> /etc/group 499 RUN mkdir /exists 500 RUN touch /exists/exists_file 501 RUN chown -R dockerio.dockerio /exists 502 ADD test_file /exists/ 503 RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 504 RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] 505 RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 506 withFile("test_file", "test1"))) 507 } 508 509 func (s *DockerSuite) TestBuildCopyAddMultipleFiles(c *check.C) { 510 testRequires(c, DaemonIsLinux) // Linux specific test 511 server := fakeStorage(c, map[string]string{ 512 "robots.txt": "hello", 513 }) 514 defer server.Close() 515 516 buildImageSuccessfully(c, "testcopymultiplefilestofile", withBuildContext(c, 517 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 518 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 519 RUN echo 'dockerio:x:1001:' >> /etc/group 520 RUN mkdir /exists 521 RUN touch /exists/exists_file 522 RUN chown -R dockerio.dockerio /exists 523 COPY test_file1 test_file2 /exists/ 524 ADD test_file3 test_file4 %s/robots.txt /exists/ 525 RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 526 RUN [ $(ls -l /exists/test_file1 | awk '{print $3":"$4}') = 'root:root' ] 527 RUN [ $(ls -l /exists/test_file2 | awk '{print $3":"$4}') = 'root:root' ] 528 RUN [ $(ls -l /exists/test_file3 | awk '{print $3":"$4}') = 'root:root' ] 529 RUN [ $(ls -l /exists/test_file4 | awk '{print $3":"$4}') = 'root:root' ] 530 RUN [ $(ls -l /exists/robots.txt | awk '{print $3":"$4}') = 'root:root' ] 531 RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 532 `, server.URL())), 533 withFile("test_file1", "test1"), 534 withFile("test_file2", "test2"), 535 withFile("test_file3", "test3"), 536 withFile("test_file3", "test3"), 537 withFile("test_file4", "test4"))) 538 } 539 540 // These tests are mainly for user namespaces to verify that new directories 541 // are created as the remapped root uid/gid pair 542 func (s *DockerSuite) TestBuildUsernamespaceValidateRemappedRoot(c *check.C) { 543 testRequires(c, DaemonIsLinux) 544 testCases := []string{ 545 "ADD . /new_dir", 546 "COPY test_dir /new_dir", 547 "WORKDIR /new_dir", 548 } 549 name := "testbuildusernamespacevalidateremappedroot" 550 for _, tc := range testCases { 551 buildImageSuccessfully(c, name, withBuildContext(c, 552 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 553 %s 554 RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'root:root' ]`, tc)), 555 withFile("test_dir/test_file", "test file"))) 556 557 dockerCmd(c, "rmi", name) 558 } 559 } 560 561 func (s *DockerSuite) TestBuildAddAndCopyFileWithWhitespace(c *check.C) { 562 testRequires(c, DaemonIsLinux) // Not currently passing on Windows 563 name := "testaddfilewithwhitespace" 564 565 for _, command := range []string{"ADD", "COPY"} { 566 buildImageSuccessfully(c, name, withBuildContext(c, 567 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 568 RUN mkdir "/test dir" 569 RUN mkdir "/test_dir" 570 %s [ "test file1", "/test_file1" ] 571 %s [ "test_file2", "/test file2" ] 572 %s [ "test file3", "/test file3" ] 573 %s [ "test dir/test_file4", "/test_dir/test_file4" ] 574 %s [ "test_dir/test_file5", "/test dir/test_file5" ] 575 %s [ "test dir/test_file6", "/test dir/test_file6" ] 576 RUN [ $(cat "/test_file1") = 'test1' ] 577 RUN [ $(cat "/test file2") = 'test2' ] 578 RUN [ $(cat "/test file3") = 'test3' ] 579 RUN [ $(cat "/test_dir/test_file4") = 'test4' ] 580 RUN [ $(cat "/test dir/test_file5") = 'test5' ] 581 RUN [ $(cat "/test dir/test_file6") = 'test6' ]`, command, command, command, command, command, command)), 582 withFile("test file1", "test1"), 583 withFile("test_file2", "test2"), 584 withFile("test file3", "test3"), 585 withFile("test dir/test_file4", "test4"), 586 withFile("test_dir/test_file5", "test5"), 587 withFile("test dir/test_file6", "test6"), 588 )) 589 590 dockerCmd(c, "rmi", name) 591 } 592 } 593 594 func (s *DockerSuite) TestBuildCopyFileWithWhitespaceOnWindows(c *check.C) { 595 testRequires(c, DaemonIsWindows) 596 dockerfile := `FROM ` + testEnv.MinimalBaseImage() + ` 597 RUN mkdir "C:/test dir" 598 RUN mkdir "C:/test_dir" 599 COPY [ "test file1", "/test_file1" ] 600 COPY [ "test_file2", "/test file2" ] 601 COPY [ "test file3", "/test file3" ] 602 COPY [ "test dir/test_file4", "/test_dir/test_file4" ] 603 COPY [ "test_dir/test_file5", "/test dir/test_file5" ] 604 COPY [ "test dir/test_file6", "/test dir/test_file6" ] 605 RUN find "test1" "C:/test_file1" 606 RUN find "test2" "C:/test file2" 607 RUN find "test3" "C:/test file3" 608 RUN find "test4" "C:/test_dir/test_file4" 609 RUN find "test5" "C:/test dir/test_file5" 610 RUN find "test6" "C:/test dir/test_file6"` 611 612 name := "testcopyfilewithwhitespace" 613 buildImageSuccessfully(c, name, withBuildContext(c, 614 withFile("Dockerfile", dockerfile), 615 withFile("test file1", "test1"), 616 withFile("test_file2", "test2"), 617 withFile("test file3", "test3"), 618 withFile("test dir/test_file4", "test4"), 619 withFile("test_dir/test_file5", "test5"), 620 withFile("test dir/test_file6", "test6"), 621 )) 622 } 623 624 func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) { 625 name := "testcopywildcard" 626 server := fakeStorage(c, map[string]string{ 627 "robots.txt": "hello", 628 "index.html": "world", 629 }) 630 defer server.Close() 631 632 ctx := fakeContext(c, fmt.Sprintf(`FROM busybox 633 COPY file*.txt /tmp/ 634 RUN ls /tmp/file1.txt /tmp/file2.txt 635 RUN [ "mkdir", "/tmp1" ] 636 COPY dir* /tmp1/ 637 RUN ls /tmp1/dirt /tmp1/nested_file /tmp1/nested_dir/nest_nest_file 638 RUN [ "mkdir", "/tmp2" ] 639 ADD dir/*dir %s/robots.txt /tmp2/ 640 RUN ls /tmp2/nest_nest_file /tmp2/robots.txt 641 `, server.URL()), 642 map[string]string{ 643 "file1.txt": "test1", 644 "file2.txt": "test2", 645 "dir/nested_file": "nested file", 646 "dir/nested_dir/nest_nest_file": "2 times nested", 647 "dirt": "dirty", 648 }) 649 defer ctx.Close() 650 651 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 652 id1 := getIDByName(c, name) 653 654 // Now make sure we use a cache the 2nd time 655 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 656 id2 := getIDByName(c, name) 657 658 if id1 != id2 { 659 c.Fatal("didn't use the cache") 660 } 661 662 } 663 664 func (s *DockerSuite) TestBuildCopyWildcardInName(c *check.C) { 665 // Run this only on Linux 666 // Below is the original comment (that I don't agree with — vdemeester) 667 // Normally we would do c.Fatal(err) here but given that 668 // the odds of this failing are so rare, it must be because 669 // the OS we're running the client on doesn't support * in 670 // filenames (like windows). So, instead of failing the test 671 // just let it pass. Then we don't need to explicitly 672 // say which OSs this works on or not. 673 testRequires(c, DaemonIsLinux, UnixCli) 674 675 buildImageSuccessfully(c, "testcopywildcardinname", withBuildContext(c, 676 withFile("Dockerfile", `FROM busybox 677 COPY *.txt /tmp/ 678 RUN [ "$(cat /tmp/\*.txt)" = 'hi there' ] 679 `), 680 withFile("*.txt", "hi there"), 681 )) 682 } 683 684 func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) { 685 name := "testcopywildcardcache" 686 ctx := fakeContext(c, `FROM busybox 687 COPY file1.txt /tmp/`, 688 map[string]string{ 689 "file1.txt": "test1", 690 }) 691 defer ctx.Close() 692 693 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 694 id1 := getIDByName(c, name) 695 696 // Now make sure we use a cache the 2nd time even with wild cards. 697 // Use the same context so the file is the same and the checksum will match 698 ctx.Add("Dockerfile", `FROM busybox 699 COPY file*.txt /tmp/`) 700 701 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 702 id2 := getIDByName(c, name) 703 704 if id1 != id2 { 705 c.Fatal("didn't use the cache") 706 } 707 708 } 709 710 func (s *DockerSuite) TestBuildAddSingleFileToNonExistingDir(c *check.C) { 711 testRequires(c, DaemonIsLinux) // Linux specific test 712 buildImageSuccessfully(c, "testaddsinglefiletononexistingdir", withBuildContext(c, 713 withFile("Dockerfile", `FROM busybox 714 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 715 RUN echo 'dockerio:x:1001:' >> /etc/group 716 RUN touch /exists 717 RUN chown dockerio.dockerio /exists 718 ADD test_file /test_dir/ 719 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] 720 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] 721 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 722 withFile("test_file", "test1"))) 723 } 724 725 func (s *DockerSuite) TestBuildAddDirContentToRoot(c *check.C) { 726 testRequires(c, DaemonIsLinux) // Linux specific test 727 buildImageSuccessfully(c, "testadddircontenttoroot", withBuildContext(c, 728 withFile("Dockerfile", `FROM busybox 729 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 730 RUN echo 'dockerio:x:1001:' >> /etc/group 731 RUN touch /exists 732 RUN chown dockerio.dockerio exists 733 ADD test_dir / 734 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] 735 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 736 withFile("test_dir/test_file", "test1"))) 737 } 738 739 func (s *DockerSuite) TestBuildAddDirContentToExistingDir(c *check.C) { 740 testRequires(c, DaemonIsLinux) // Linux specific test 741 buildImageSuccessfully(c, "testadddircontenttoexistingdir", withBuildContext(c, 742 withFile("Dockerfile", `FROM busybox 743 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 744 RUN echo 'dockerio:x:1001:' >> /etc/group 745 RUN mkdir /exists 746 RUN touch /exists/exists_file 747 RUN chown -R dockerio.dockerio /exists 748 ADD test_dir/ /exists/ 749 RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 750 RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 751 RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`), 752 withFile("test_dir/test_file", "test1"))) 753 } 754 755 func (s *DockerSuite) TestBuildAddWholeDirToRoot(c *check.C) { 756 testRequires(c, DaemonIsLinux) // Linux specific test 757 buildImageSuccessfully(c, "testaddwholedirtoroot", withBuildContext(c, 758 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 759 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 760 RUN echo 'dockerio:x:1001:' >> /etc/group 761 RUN touch /exists 762 RUN chown dockerio.dockerio exists 763 ADD test_dir /test_dir 764 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] 765 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ] 766 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] 767 RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ] 768 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod)), 769 withFile("test_dir/test_file", "test1"))) 770 } 771 772 // Testing #5941 : Having an etc directory in context conflicts with the /etc/mtab 773 func (s *DockerSuite) TestBuildAddOrCopyEtcToRootShouldNotConflict(c *check.C) { 774 buildImageSuccessfully(c, "testaddetctoroot", withBuildContext(c, 775 withFile("Dockerfile", `FROM `+minimalBaseImage()+` 776 ADD . /`), 777 withFile("etc/test_file", "test1"))) 778 buildImageSuccessfully(c, "testcopyetctoroot", withBuildContext(c, 779 withFile("Dockerfile", `FROM `+minimalBaseImage()+` 780 COPY . /`), 781 withFile("etc/test_file", "test1"))) 782 } 783 784 // Testing #9401 : Losing setuid flag after a ADD 785 func (s *DockerSuite) TestBuildAddPreservesFilesSpecialBits(c *check.C) { 786 testRequires(c, DaemonIsLinux) // Linux specific test 787 buildImageSuccessfully(c, "testaddetctoroot", withBuildContext(c, 788 withFile("Dockerfile", `FROM busybox 789 ADD suidbin /usr/bin/suidbin 790 RUN chmod 4755 /usr/bin/suidbin 791 RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ] 792 ADD ./data/ / 793 RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`), 794 withFile("suidbin", "suidbin"), 795 withFile("/data/usr/test_file", "test1"))) 796 } 797 798 func (s *DockerSuite) TestBuildCopySingleFileToRoot(c *check.C) { 799 testRequires(c, DaemonIsLinux) // Linux specific test 800 buildImageSuccessfully(c, "testcopysinglefiletoroot", withBuildContext(c, 801 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 802 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 803 RUN echo 'dockerio:x:1001:' >> /etc/group 804 RUN touch /exists 805 RUN chown dockerio.dockerio /exists 806 COPY test_file / 807 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] 808 RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ] 809 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod)), 810 withFile("test_file", "test1"))) 811 } 812 813 // Issue #3960: "ADD src ." hangs - adapted for COPY 814 func (s *DockerSuite) TestBuildCopySingleFileToWorkdir(c *check.C) { 815 name := "testcopysinglefiletoworkdir" 816 ctx := fakeContext(c, `FROM busybox 817 COPY test_file .`, 818 map[string]string{ 819 "test_file": "test1", 820 }) 821 defer ctx.Close() 822 823 errChan := make(chan error) 824 go func() { 825 errChan <- buildImage(name, withExternalBuildContext(ctx)).Error 826 close(errChan) 827 }() 828 select { 829 case <-time.After(15 * time.Second): 830 c.Fatal("Build with adding to workdir timed out") 831 case err := <-errChan: 832 c.Assert(err, check.IsNil) 833 } 834 } 835 836 func (s *DockerSuite) TestBuildCopySingleFileToExistDir(c *check.C) { 837 testRequires(c, DaemonIsLinux) // Linux specific test 838 buildImageSuccessfully(c, "testcopysinglefiletoexistdir", withBuildContext(c, 839 withFile("Dockerfile", `FROM busybox 840 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 841 RUN echo 'dockerio:x:1001:' >> /etc/group 842 RUN mkdir /exists 843 RUN touch /exists/exists_file 844 RUN chown -R dockerio.dockerio /exists 845 COPY test_file /exists/ 846 RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 847 RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] 848 RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 849 withFile("test_file", "test1"))) 850 } 851 852 func (s *DockerSuite) TestBuildCopySingleFileToNonExistDir(c *check.C) { 853 testRequires(c, DaemonIsLinux) // Linux specific 854 buildImageSuccessfully(c, "testcopysinglefiletononexistdir", withBuildContext(c, 855 withFile("Dockerfile", `FROM busybox 856 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 857 RUN echo 'dockerio:x:1001:' >> /etc/group 858 RUN touch /exists 859 RUN chown dockerio.dockerio /exists 860 COPY test_file /test_dir/ 861 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] 862 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] 863 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 864 withFile("test_file", "test1"))) 865 } 866 867 func (s *DockerSuite) TestBuildCopyDirContentToRoot(c *check.C) { 868 testRequires(c, DaemonIsLinux) // Linux specific test 869 buildImageSuccessfully(c, "testcopydircontenttoroot", withBuildContext(c, 870 withFile("Dockerfile", `FROM busybox 871 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 872 RUN echo 'dockerio:x:1001:' >> /etc/group 873 RUN touch /exists 874 RUN chown dockerio.dockerio exists 875 COPY test_dir / 876 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] 877 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`), 878 withFile("test_dir/test_file", "test1"))) 879 } 880 881 func (s *DockerSuite) TestBuildCopyDirContentToExistDir(c *check.C) { 882 testRequires(c, DaemonIsLinux) // Linux specific test 883 buildImageSuccessfully(c, "testcopydircontenttoexistdir", withBuildContext(c, 884 withFile("Dockerfile", `FROM busybox 885 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 886 RUN echo 'dockerio:x:1001:' >> /etc/group 887 RUN mkdir /exists 888 RUN touch /exists/exists_file 889 RUN chown -R dockerio.dockerio /exists 890 COPY test_dir/ /exists/ 891 RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 892 RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] 893 RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`), 894 withFile("test_dir/test_file", "test1"))) 895 } 896 897 func (s *DockerSuite) TestBuildCopyWholeDirToRoot(c *check.C) { 898 testRequires(c, DaemonIsLinux) // Linux specific test 899 buildImageSuccessfully(c, "testcopywholedirtoroot", withBuildContext(c, 900 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 901 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 902 RUN echo 'dockerio:x:1001:' >> /etc/group 903 RUN touch /exists 904 RUN chown dockerio.dockerio exists 905 COPY test_dir /test_dir 906 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] 907 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ] 908 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] 909 RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ] 910 RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod)), 911 withFile("test_dir/test_file", "test1"))) 912 } 913 914 func (s *DockerSuite) TestBuildAddBadLinks(c *check.C) { 915 testRequires(c, DaemonIsLinux) // Not currently working on Windows 916 917 dockerfile := ` 918 FROM scratch 919 ADD links.tar / 920 ADD foo.txt /symlink/ 921 ` 922 targetFile := "foo.txt" 923 var ( 924 name = "test-link-absolute" 925 ) 926 ctx := fakeContext(c, dockerfile, nil) 927 defer ctx.Close() 928 929 tempDir, err := ioutil.TempDir("", "test-link-absolute-temp-") 930 if err != nil { 931 c.Fatalf("failed to create temporary directory: %s", tempDir) 932 } 933 defer os.RemoveAll(tempDir) 934 935 var symlinkTarget string 936 if runtime.GOOS == "windows" { 937 var driveLetter string 938 if abs, err := filepath.Abs(tempDir); err != nil { 939 c.Fatal(err) 940 } else { 941 driveLetter = abs[:1] 942 } 943 tempDirWithoutDrive := tempDir[2:] 944 symlinkTarget = fmt.Sprintf(`%s:\..\..\..\..\..\..\..\..\..\..\..\..%s`, driveLetter, tempDirWithoutDrive) 945 } else { 946 symlinkTarget = fmt.Sprintf("/../../../../../../../../../../../..%s", tempDir) 947 } 948 949 tarPath := filepath.Join(ctx.Dir, "links.tar") 950 nonExistingFile := filepath.Join(tempDir, targetFile) 951 fooPath := filepath.Join(ctx.Dir, targetFile) 952 953 tarOut, err := os.Create(tarPath) 954 if err != nil { 955 c.Fatal(err) 956 } 957 958 tarWriter := tar.NewWriter(tarOut) 959 960 header := &tar.Header{ 961 Name: "symlink", 962 Typeflag: tar.TypeSymlink, 963 Linkname: symlinkTarget, 964 Mode: 0755, 965 Uid: 0, 966 Gid: 0, 967 } 968 969 err = tarWriter.WriteHeader(header) 970 if err != nil { 971 c.Fatal(err) 972 } 973 974 tarWriter.Close() 975 tarOut.Close() 976 977 foo, err := os.Create(fooPath) 978 if err != nil { 979 c.Fatal(err) 980 } 981 defer foo.Close() 982 983 if _, err := foo.WriteString("test"); err != nil { 984 c.Fatal(err) 985 } 986 987 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 988 if _, err := os.Stat(nonExistingFile); err == nil || err != nil && !os.IsNotExist(err) { 989 c.Fatalf("%s shouldn't have been written and it shouldn't exist", nonExistingFile) 990 } 991 992 } 993 994 func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) { 995 testRequires(c, DaemonIsLinux) // ln not implemented on Windows busybox 996 const ( 997 dockerfileTemplate = ` 998 FROM busybox 999 RUN ln -s /../../../../../../../../%s /x 1000 VOLUME /x 1001 ADD foo.txt /x/` 1002 targetFile = "foo.txt" 1003 ) 1004 var ( 1005 name = "test-link-absolute-volume" 1006 dockerfile = "" 1007 ) 1008 1009 tempDir, err := ioutil.TempDir("", "test-link-absolute-volume-temp-") 1010 if err != nil { 1011 c.Fatalf("failed to create temporary directory: %s", tempDir) 1012 } 1013 defer os.RemoveAll(tempDir) 1014 1015 dockerfile = fmt.Sprintf(dockerfileTemplate, tempDir) 1016 nonExistingFile := filepath.Join(tempDir, targetFile) 1017 1018 ctx := fakeContext(c, dockerfile, nil) 1019 defer ctx.Close() 1020 fooPath := filepath.Join(ctx.Dir, targetFile) 1021 1022 foo, err := os.Create(fooPath) 1023 if err != nil { 1024 c.Fatal(err) 1025 } 1026 defer foo.Close() 1027 1028 if _, err := foo.WriteString("test"); err != nil { 1029 c.Fatal(err) 1030 } 1031 1032 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1033 if _, err := os.Stat(nonExistingFile); err == nil || err != nil && !os.IsNotExist(err) { 1034 c.Fatalf("%s shouldn't have been written and it shouldn't exist", nonExistingFile) 1035 } 1036 1037 } 1038 1039 // Issue #5270 - ensure we throw a better error than "unexpected EOF" 1040 // when we can't access files in the context. 1041 func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) { 1042 testRequires(c, DaemonIsLinux, UnixCli) // test uses chown/chmod: not available on windows 1043 1044 { 1045 name := "testbuildinaccessiblefiles" 1046 ctx := fakeContext(c, "FROM scratch\nADD . /foo/", map[string]string{"fileWithoutReadAccess": "foo"}) 1047 defer ctx.Close() 1048 // This is used to ensure we detect inaccessible files early during build in the cli client 1049 pathToFileWithoutReadAccess := filepath.Join(ctx.Dir, "fileWithoutReadAccess") 1050 1051 if err := os.Chown(pathToFileWithoutReadAccess, 0, 0); err != nil { 1052 c.Fatalf("failed to chown file to root: %s", err) 1053 } 1054 if err := os.Chmod(pathToFileWithoutReadAccess, 0700); err != nil { 1055 c.Fatalf("failed to chmod file to 700: %s", err) 1056 } 1057 result := icmd.RunCmd(icmd.Cmd{ 1058 Command: []string{"su", "unprivilegeduser", "-c", fmt.Sprintf("%s build -t %s .", dockerBinary, name)}, 1059 Dir: ctx.Dir, 1060 }) 1061 if result.Error == nil { 1062 c.Fatalf("build should have failed: %s %s", result.Error, result.Combined()) 1063 } 1064 1065 // check if we've detected the failure before we started building 1066 if !strings.Contains(result.Combined(), "no permission to read from ") { 1067 c.Fatalf("output should've contained the string: no permission to read from but contained: %s", result.Combined()) 1068 } 1069 1070 if !strings.Contains(result.Combined(), "Error checking context") { 1071 c.Fatalf("output should've contained the string: Error checking context") 1072 } 1073 } 1074 { 1075 name := "testbuildinaccessibledirectory" 1076 ctx := fakeContext(c, "FROM scratch\nADD . /foo/", map[string]string{"directoryWeCantStat/bar": "foo"}) 1077 defer ctx.Close() 1078 // This is used to ensure we detect inaccessible directories early during build in the cli client 1079 pathToDirectoryWithoutReadAccess := filepath.Join(ctx.Dir, "directoryWeCantStat") 1080 pathToFileInDirectoryWithoutReadAccess := filepath.Join(pathToDirectoryWithoutReadAccess, "bar") 1081 1082 if err := os.Chown(pathToDirectoryWithoutReadAccess, 0, 0); err != nil { 1083 c.Fatalf("failed to chown directory to root: %s", err) 1084 } 1085 if err := os.Chmod(pathToDirectoryWithoutReadAccess, 0444); err != nil { 1086 c.Fatalf("failed to chmod directory to 444: %s", err) 1087 } 1088 if err := os.Chmod(pathToFileInDirectoryWithoutReadAccess, 0700); err != nil { 1089 c.Fatalf("failed to chmod file to 700: %s", err) 1090 } 1091 1092 result := icmd.RunCmd(icmd.Cmd{ 1093 Command: []string{"su", "unprivilegeduser", "-c", fmt.Sprintf("%s build -t %s .", dockerBinary, name)}, 1094 Dir: ctx.Dir, 1095 }) 1096 if result.Error == nil { 1097 c.Fatalf("build should have failed: %s %s", result.Error, result.Combined()) 1098 } 1099 1100 // check if we've detected the failure before we started building 1101 if !strings.Contains(result.Combined(), "can't stat") { 1102 c.Fatalf("output should've contained the string: can't access %s", result.Combined()) 1103 } 1104 1105 if !strings.Contains(result.Combined(), "Error checking context") { 1106 c.Fatalf("output should've contained the string: Error checking context\ngot:%s", result.Combined()) 1107 } 1108 1109 } 1110 { 1111 name := "testlinksok" 1112 ctx := fakeContext(c, "FROM scratch\nADD . /foo/", nil) 1113 defer ctx.Close() 1114 1115 target := "../../../../../../../../../../../../../../../../../../../azA" 1116 if err := os.Symlink(filepath.Join(ctx.Dir, "g"), target); err != nil { 1117 c.Fatal(err) 1118 } 1119 defer os.Remove(target) 1120 // This is used to ensure we don't follow links when checking if everything in the context is accessible 1121 // This test doesn't require that we run commands as an unprivileged user 1122 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1123 } 1124 { 1125 name := "testbuildignoredinaccessible" 1126 ctx := fakeContext(c, "FROM scratch\nADD . /foo/", 1127 map[string]string{ 1128 "directoryWeCantStat/bar": "foo", 1129 ".dockerignore": "directoryWeCantStat", 1130 }) 1131 defer ctx.Close() 1132 // This is used to ensure we don't try to add inaccessible files when they are ignored by a .dockerignore pattern 1133 pathToDirectoryWithoutReadAccess := filepath.Join(ctx.Dir, "directoryWeCantStat") 1134 pathToFileInDirectoryWithoutReadAccess := filepath.Join(pathToDirectoryWithoutReadAccess, "bar") 1135 if err := os.Chown(pathToDirectoryWithoutReadAccess, 0, 0); err != nil { 1136 c.Fatalf("failed to chown directory to root: %s", err) 1137 } 1138 if err := os.Chmod(pathToDirectoryWithoutReadAccess, 0444); err != nil { 1139 c.Fatalf("failed to chmod directory to 444: %s", err) 1140 } 1141 if err := os.Chmod(pathToFileInDirectoryWithoutReadAccess, 0700); err != nil { 1142 c.Fatalf("failed to chmod file to 700: %s", err) 1143 } 1144 1145 result := icmd.RunCmd(icmd.Cmd{ 1146 Dir: ctx.Dir, 1147 Command: []string{"su", "unprivilegeduser", "-c", 1148 fmt.Sprintf("%s build -t %s .", dockerBinary, name)}, 1149 }) 1150 result.Assert(c, icmd.Expected{}) 1151 } 1152 } 1153 1154 func (s *DockerSuite) TestBuildForceRm(c *check.C) { 1155 containerCountBefore := getContainerCount(c) 1156 name := "testbuildforcerm" 1157 1158 buildImage(name, withBuildFlags("--force-rm"), withBuildContext(c, 1159 withFile("Dockerfile", `FROM `+minimalBaseImage()+` 1160 RUN true 1161 RUN thiswillfail`))).Assert(c, icmd.Expected{ 1162 ExitCode: 1, 1163 }) 1164 1165 containerCountAfter := getContainerCount(c) 1166 if containerCountBefore != containerCountAfter { 1167 c.Fatalf("--force-rm shouldn't have left containers behind") 1168 } 1169 1170 } 1171 1172 func (s *DockerSuite) TestBuildRm(c *check.C) { 1173 name := "testbuildrm" 1174 1175 testCases := []struct { 1176 buildflags []string 1177 shouldLeftContainerBehind bool 1178 }{ 1179 // Default case (i.e. --rm=true) 1180 { 1181 buildflags: []string{}, 1182 shouldLeftContainerBehind: false, 1183 }, 1184 { 1185 buildflags: []string{"--rm"}, 1186 shouldLeftContainerBehind: false, 1187 }, 1188 { 1189 buildflags: []string{"--rm=false"}, 1190 shouldLeftContainerBehind: true, 1191 }, 1192 } 1193 1194 for _, tc := range testCases { 1195 containerCountBefore := getContainerCount(c) 1196 1197 buildImageSuccessfully(c, name, withBuildFlags(tc.buildflags...), withDockerfile(`FROM busybox 1198 RUN echo hello world`)) 1199 1200 containerCountAfter := getContainerCount(c) 1201 if tc.shouldLeftContainerBehind { 1202 if containerCountBefore == containerCountAfter { 1203 c.Fatalf("flags %v should have left containers behind", tc.buildflags) 1204 } 1205 } else { 1206 if containerCountBefore != containerCountAfter { 1207 c.Fatalf("flags %v shouldn't have left containers behind", tc.buildflags) 1208 } 1209 } 1210 1211 dockerCmd(c, "rmi", name) 1212 } 1213 } 1214 1215 func (s *DockerSuite) TestBuildWithVolumes(c *check.C) { 1216 testRequires(c, DaemonIsLinux) // Invalid volume paths on Windows 1217 var ( 1218 result map[string]map[string]struct{} 1219 name = "testbuildvolumes" 1220 emptyMap = make(map[string]struct{}) 1221 expected = map[string]map[string]struct{}{ 1222 "/test1": emptyMap, 1223 "/test2": emptyMap, 1224 "/test3": emptyMap, 1225 "/test4": emptyMap, 1226 "/test5": emptyMap, 1227 "/test6": emptyMap, 1228 "[/test7": emptyMap, 1229 "/test8]": emptyMap, 1230 } 1231 ) 1232 1233 buildImageSuccessfully(c, name, withDockerfile(`FROM scratch 1234 VOLUME /test1 1235 VOLUME /test2 1236 VOLUME /test3 /test4 1237 VOLUME ["/test5", "/test6"] 1238 VOLUME [/test7 /test8] 1239 `)) 1240 1241 inspectFieldAndUnmarshall(c, name, "Config.Volumes", &result) 1242 1243 equal := reflect.DeepEqual(&result, &expected) 1244 if !equal { 1245 c.Fatalf("Volumes %s, expected %s", result, expected) 1246 } 1247 1248 } 1249 1250 func (s *DockerSuite) TestBuildMaintainer(c *check.C) { 1251 name := "testbuildmaintainer" 1252 1253 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 1254 MAINTAINER dockerio`)) 1255 1256 expected := "dockerio" 1257 res := inspectField(c, name, "Author") 1258 if res != expected { 1259 c.Fatalf("Maintainer %s, expected %s", res, expected) 1260 } 1261 } 1262 1263 func (s *DockerSuite) TestBuildUser(c *check.C) { 1264 testRequires(c, DaemonIsLinux) 1265 name := "testbuilduser" 1266 expected := "dockerio" 1267 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1268 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 1269 USER dockerio 1270 RUN [ $(whoami) = 'dockerio' ]`)) 1271 res := inspectField(c, name, "Config.User") 1272 if res != expected { 1273 c.Fatalf("User %s, expected %s", res, expected) 1274 } 1275 } 1276 1277 func (s *DockerSuite) TestBuildRelativeWorkdir(c *check.C) { 1278 name := "testbuildrelativeworkdir" 1279 1280 var ( 1281 expected1 string 1282 expected2 string 1283 expected3 string 1284 expected4 string 1285 expectedFinal string 1286 ) 1287 1288 if testEnv.DaemonPlatform() == "windows" { 1289 expected1 = `C:/` 1290 expected2 = `C:/test1` 1291 expected3 = `C:/test2` 1292 expected4 = `C:/test2/test3` 1293 expectedFinal = `C:\test2\test3` // Note inspect is going to return Windows paths, as it's not in busybox 1294 } else { 1295 expected1 = `/` 1296 expected2 = `/test1` 1297 expected3 = `/test2` 1298 expected4 = `/test2/test3` 1299 expectedFinal = `/test2/test3` 1300 } 1301 1302 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1303 RUN sh -c "[ "$PWD" = "`+expected1+`" ]" 1304 WORKDIR test1 1305 RUN sh -c "[ "$PWD" = "`+expected2+`" ]" 1306 WORKDIR /test2 1307 RUN sh -c "[ "$PWD" = "`+expected3+`" ]" 1308 WORKDIR test3 1309 RUN sh -c "[ "$PWD" = "`+expected4+`" ]"`)) 1310 1311 res := inspectField(c, name, "Config.WorkingDir") 1312 if res != expectedFinal { 1313 c.Fatalf("Workdir %s, expected %s", res, expectedFinal) 1314 } 1315 } 1316 1317 // #22181 Regression test. Single end-to-end test of using 1318 // Windows semantics. Most path handling verifications are in unit tests 1319 func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) { 1320 testRequires(c, DaemonIsWindows) 1321 buildImageSuccessfully(c, "testbuildwindowsworkdirprocessing", withDockerfile(`FROM busybox 1322 WORKDIR C:\\foo 1323 WORKDIR bar 1324 RUN sh -c "[ "$PWD" = "C:/foo/bar" ]" 1325 `)) 1326 } 1327 1328 // #22181 Regression test. Most paths handling verifications are in unit test. 1329 // One functional test for end-to-end 1330 func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) { 1331 testRequires(c, DaemonIsWindows) 1332 // TODO Windows (@jhowardmsft). Needs a follow-up PR to 22181 to 1333 // support backslash such as .\\ being equivalent to ./ and c:\\ being 1334 // equivalent to c:/. This is not currently (nor ever has been) supported 1335 // by docker on the Windows platform. 1336 buildImageSuccessfully(c, "testbuildwindowsaddcopypathprocessing", withBuildContext(c, 1337 withFile("Dockerfile", `FROM busybox 1338 # No trailing slash on COPY/ADD 1339 # Results in dir being changed to a file 1340 WORKDIR /wc1 1341 COPY wc1 c:/wc1 1342 WORKDIR /wc2 1343 ADD wc2 c:/wc2 1344 WORKDIR c:/ 1345 RUN sh -c "[ $(cat c:/wc1/wc1) = 'hellowc1' ]" 1346 RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]" 1347 1348 # Trailing slash on COPY/ADD, Windows-style path. 1349 WORKDIR /wd1 1350 COPY wd1 c:/wd1/ 1351 WORKDIR /wd2 1352 ADD wd2 c:/wd2/ 1353 RUN sh -c "[ $(cat c:/wd1/wd1) = 'hellowd1' ]" 1354 RUN sh -c "[ $(cat c:/wd2/wd2) = 'worldwd2' ]" 1355 `), 1356 withFile("wc1", "hellowc1"), 1357 withFile("wc2", "worldwc2"), 1358 withFile("wd1", "hellowd1"), 1359 withFile("wd2", "worldwd2"), 1360 )) 1361 } 1362 1363 func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *check.C) { 1364 name := "testbuildworkdirwithenvvariables" 1365 1366 var expected string 1367 if testEnv.DaemonPlatform() == "windows" { 1368 expected = `C:\test1\test2` 1369 } else { 1370 expected = `/test1/test2` 1371 } 1372 1373 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1374 ENV DIRPATH /test1 1375 ENV SUBDIRNAME test2 1376 WORKDIR $DIRPATH 1377 WORKDIR $SUBDIRNAME/$MISSING_VAR`)) 1378 res := inspectField(c, name, "Config.WorkingDir") 1379 if res != expected { 1380 c.Fatalf("Workdir %s, expected %s", res, expected) 1381 } 1382 } 1383 1384 func (s *DockerSuite) TestBuildRelativeCopy(c *check.C) { 1385 // cat /test1/test2/foo gets permission denied for the user 1386 testRequires(c, NotUserNamespace) 1387 1388 var expected string 1389 if testEnv.DaemonPlatform() == "windows" { 1390 expected = `C:/test1/test2` 1391 } else { 1392 expected = `/test1/test2` 1393 } 1394 1395 buildImageSuccessfully(c, "testbuildrelativecopy", withBuildContext(c, 1396 withFile("Dockerfile", `FROM busybox 1397 WORKDIR /test1 1398 WORKDIR test2 1399 RUN sh -c "[ "$PWD" = '`+expected+`' ]" 1400 COPY foo ./ 1401 RUN sh -c "[ $(cat /test1/test2/foo) = 'hello' ]" 1402 ADD foo ./bar/baz 1403 RUN sh -c "[ $(cat /test1/test2/bar/baz) = 'hello' ]" 1404 COPY foo ./bar/baz2 1405 RUN sh -c "[ $(cat /test1/test2/bar/baz2) = 'hello' ]" 1406 WORKDIR .. 1407 COPY foo ./ 1408 RUN sh -c "[ $(cat /test1/foo) = 'hello' ]" 1409 COPY foo /test3/ 1410 RUN sh -c "[ $(cat /test3/foo) = 'hello' ]" 1411 WORKDIR /test4 1412 COPY . . 1413 RUN sh -c "[ $(cat /test4/foo) = 'hello' ]" 1414 WORKDIR /test5/test6 1415 COPY foo ../ 1416 RUN sh -c "[ $(cat /test5/foo) = 'hello' ]" 1417 `), 1418 withFile("foo", "hello"), 1419 )) 1420 } 1421 1422 func (s *DockerSuite) TestBuildBlankName(c *check.C) { 1423 name := "testbuildblankname" 1424 testCases := []struct { 1425 expression string 1426 expectedStderr string 1427 }{ 1428 { 1429 expression: "ENV =", 1430 expectedStderr: "ENV names can not be blank", 1431 }, 1432 { 1433 expression: "LABEL =", 1434 expectedStderr: "LABEL names can not be blank", 1435 }, 1436 { 1437 expression: "ARG =foo", 1438 expectedStderr: "ARG names can not be blank", 1439 }, 1440 } 1441 1442 for _, tc := range testCases { 1443 buildImage(name, withDockerfile(fmt.Sprintf(`FROM busybox 1444 %s`, tc.expression))).Assert(c, icmd.Expected{ 1445 ExitCode: 1, 1446 Err: tc.expectedStderr, 1447 }) 1448 } 1449 } 1450 1451 func (s *DockerSuite) TestBuildEnv(c *check.C) { 1452 testRequires(c, DaemonIsLinux) // ENV expansion is different in Windows 1453 name := "testbuildenv" 1454 expected := "[PATH=/test:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]" 1455 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1456 ENV PATH /test:$PATH 1457 ENV PORT 2375 1458 RUN [ $(env | grep PORT) = 'PORT=2375' ]`)) 1459 res := inspectField(c, name, "Config.Env") 1460 if res != expected { 1461 c.Fatalf("Env %s, expected %s", res, expected) 1462 } 1463 } 1464 1465 func (s *DockerSuite) TestBuildPATH(c *check.C) { 1466 testRequires(c, DaemonIsLinux) // ENV expansion is different in Windows 1467 1468 defPath := "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 1469 1470 fn := func(dockerfile string, expected string) { 1471 buildImageSuccessfully(c, "testbldpath", withDockerfile(dockerfile)) 1472 res := inspectField(c, "testbldpath", "Config.Env") 1473 if res != expected { 1474 c.Fatalf("Env %q, expected %q for dockerfile:%q", res, expected, dockerfile) 1475 } 1476 } 1477 1478 tests := []struct{ dockerfile, exp string }{ 1479 {"FROM scratch\nMAINTAINER me", "[PATH=" + defPath + "]"}, 1480 {"FROM busybox\nMAINTAINER me", "[PATH=" + defPath + "]"}, 1481 {"FROM scratch\nENV FOO=bar", "[PATH=" + defPath + " FOO=bar]"}, 1482 {"FROM busybox\nENV FOO=bar", "[PATH=" + defPath + " FOO=bar]"}, 1483 {"FROM scratch\nENV PATH=/test", "[PATH=/test]"}, 1484 {"FROM busybox\nENV PATH=/test", "[PATH=/test]"}, 1485 {"FROM scratch\nENV PATH=''", "[PATH=]"}, 1486 {"FROM busybox\nENV PATH=''", "[PATH=]"}, 1487 } 1488 1489 for _, test := range tests { 1490 fn(test.dockerfile, test.exp) 1491 } 1492 } 1493 1494 func (s *DockerSuite) TestBuildContextCleanup(c *check.C) { 1495 testRequires(c, SameHostDaemon) 1496 1497 name := "testbuildcontextcleanup" 1498 entries, err := ioutil.ReadDir(filepath.Join(testEnv.DockerBasePath(), "tmp")) 1499 if err != nil { 1500 c.Fatalf("failed to list contents of tmp dir: %s", err) 1501 } 1502 1503 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 1504 ENTRYPOINT ["/bin/echo"]`)) 1505 1506 entriesFinal, err := ioutil.ReadDir(filepath.Join(testEnv.DockerBasePath(), "tmp")) 1507 if err != nil { 1508 c.Fatalf("failed to list contents of tmp dir: %s", err) 1509 } 1510 if err = testutil.CompareDirectoryEntries(entries, entriesFinal); err != nil { 1511 c.Fatalf("context should have been deleted, but wasn't") 1512 } 1513 1514 } 1515 1516 func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) { 1517 testRequires(c, SameHostDaemon) 1518 1519 name := "testbuildcontextcleanup" 1520 entries, err := ioutil.ReadDir(filepath.Join(testEnv.DockerBasePath(), "tmp")) 1521 if err != nil { 1522 c.Fatalf("failed to list contents of tmp dir: %s", err) 1523 } 1524 1525 buildImage(name, withDockerfile(`FROM `+minimalBaseImage()+` 1526 RUN /non/existing/command`)).Assert(c, icmd.Expected{ 1527 ExitCode: 1, 1528 }) 1529 1530 entriesFinal, err := ioutil.ReadDir(filepath.Join(testEnv.DockerBasePath(), "tmp")) 1531 if err != nil { 1532 c.Fatalf("failed to list contents of tmp dir: %s", err) 1533 } 1534 if err = testutil.CompareDirectoryEntries(entries, entriesFinal); err != nil { 1535 c.Fatalf("context should have been deleted, but wasn't") 1536 } 1537 1538 } 1539 1540 func (s *DockerSuite) TestBuildCmd(c *check.C) { 1541 name := "testbuildcmd" 1542 expected := "[/bin/echo Hello World]" 1543 1544 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 1545 CMD ["/bin/echo", "Hello World"]`)) 1546 1547 res := inspectField(c, name, "Config.Cmd") 1548 if res != expected { 1549 c.Fatalf("Cmd %s, expected %s", res, expected) 1550 } 1551 } 1552 1553 func (s *DockerSuite) TestBuildExpose(c *check.C) { 1554 testRequires(c, DaemonIsLinux) // Expose not implemented on Windows 1555 name := "testbuildexpose" 1556 expected := "map[2375/tcp:{}]" 1557 1558 buildImageSuccessfully(c, name, withDockerfile(`FROM scratch 1559 EXPOSE 2375`)) 1560 1561 res := inspectField(c, name, "Config.ExposedPorts") 1562 if res != expected { 1563 c.Fatalf("Exposed ports %s, expected %s", res, expected) 1564 } 1565 } 1566 1567 func (s *DockerSuite) TestBuildExposeMorePorts(c *check.C) { 1568 testRequires(c, DaemonIsLinux) // Expose not implemented on Windows 1569 // start building docker file with a large number of ports 1570 portList := make([]string, 50) 1571 line := make([]string, 100) 1572 expectedPorts := make([]int, len(portList)*len(line)) 1573 for i := 0; i < len(portList); i++ { 1574 for j := 0; j < len(line); j++ { 1575 p := i*len(line) + j + 1 1576 line[j] = strconv.Itoa(p) 1577 expectedPorts[p-1] = p 1578 } 1579 if i == len(portList)-1 { 1580 portList[i] = strings.Join(line, " ") 1581 } else { 1582 portList[i] = strings.Join(line, " ") + ` \` 1583 } 1584 } 1585 1586 dockerfile := `FROM scratch 1587 EXPOSE {{range .}} {{.}} 1588 {{end}}` 1589 tmpl := template.Must(template.New("dockerfile").Parse(dockerfile)) 1590 buf := bytes.NewBuffer(nil) 1591 tmpl.Execute(buf, portList) 1592 1593 name := "testbuildexpose" 1594 buildImageSuccessfully(c, name, withDockerfile(buf.String())) 1595 1596 // check if all the ports are saved inside Config.ExposedPorts 1597 res := inspectFieldJSON(c, name, "Config.ExposedPorts") 1598 var exposedPorts map[string]interface{} 1599 if err := json.Unmarshal([]byte(res), &exposedPorts); err != nil { 1600 c.Fatal(err) 1601 } 1602 1603 for _, p := range expectedPorts { 1604 ep := fmt.Sprintf("%d/tcp", p) 1605 if _, ok := exposedPorts[ep]; !ok { 1606 c.Errorf("Port(%s) is not exposed", ep) 1607 } else { 1608 delete(exposedPorts, ep) 1609 } 1610 } 1611 if len(exposedPorts) != 0 { 1612 c.Errorf("Unexpected extra exposed ports %v", exposedPorts) 1613 } 1614 } 1615 1616 func (s *DockerSuite) TestBuildExposeOrder(c *check.C) { 1617 testRequires(c, DaemonIsLinux) // Expose not implemented on Windows 1618 buildID := func(name, exposed string) string { 1619 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(`FROM scratch 1620 EXPOSE %s`, exposed))) 1621 id := inspectField(c, name, "Id") 1622 return id 1623 } 1624 1625 id1 := buildID("testbuildexpose1", "80 2375") 1626 id2 := buildID("testbuildexpose2", "2375 80") 1627 if id1 != id2 { 1628 c.Errorf("EXPOSE should invalidate the cache only when ports actually changed") 1629 } 1630 } 1631 1632 func (s *DockerSuite) TestBuildExposeUpperCaseProto(c *check.C) { 1633 testRequires(c, DaemonIsLinux) // Expose not implemented on Windows 1634 name := "testbuildexposeuppercaseproto" 1635 expected := "map[5678/udp:{}]" 1636 buildImageSuccessfully(c, name, withDockerfile(`FROM scratch 1637 EXPOSE 5678/UDP`)) 1638 res := inspectField(c, name, "Config.ExposedPorts") 1639 if res != expected { 1640 c.Fatalf("Exposed ports %s, expected %s", res, expected) 1641 } 1642 } 1643 1644 func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *check.C) { 1645 name := "testbuildentrypointinheritance" 1646 name2 := "testbuildentrypointinheritance2" 1647 1648 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1649 ENTRYPOINT ["/bin/echo"]`)) 1650 res := inspectField(c, name, "Config.Entrypoint") 1651 1652 expected := "[/bin/echo]" 1653 if res != expected { 1654 c.Fatalf("Entrypoint %s, expected %s", res, expected) 1655 } 1656 1657 buildImageSuccessfully(c, name2, withDockerfile(fmt.Sprintf(`FROM %s 1658 ENTRYPOINT []`, name))) 1659 res = inspectField(c, name2, "Config.Entrypoint") 1660 1661 expected = "[]" 1662 if res != expected { 1663 c.Fatalf("Entrypoint %s, expected %s", res, expected) 1664 } 1665 } 1666 1667 func (s *DockerSuite) TestBuildEmptyEntrypoint(c *check.C) { 1668 name := "testbuildentrypoint" 1669 expected := "[]" 1670 1671 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 1672 ENTRYPOINT []`)) 1673 1674 res := inspectField(c, name, "Config.Entrypoint") 1675 if res != expected { 1676 c.Fatalf("Entrypoint %s, expected %s", res, expected) 1677 } 1678 1679 } 1680 1681 func (s *DockerSuite) TestBuildEntrypoint(c *check.C) { 1682 name := "testbuildentrypoint" 1683 1684 expected := "[/bin/echo]" 1685 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 1686 ENTRYPOINT ["/bin/echo"]`)) 1687 1688 res := inspectField(c, name, "Config.Entrypoint") 1689 if res != expected { 1690 c.Fatalf("Entrypoint %s, expected %s", res, expected) 1691 } 1692 1693 } 1694 1695 // #6445 ensure ONBUILD triggers aren't committed to grandchildren 1696 func (s *DockerSuite) TestBuildOnBuildLimitedInheritence(c *check.C) { 1697 buildImageSuccessfully(c, "testonbuildtrigger1", withDockerfile(` 1698 FROM busybox 1699 RUN echo "GRANDPARENT" 1700 ONBUILD RUN echo "ONBUILD PARENT" 1701 `)) 1702 // ONBUILD should be run in second build. 1703 buildImage("testonbuildtrigger2", withDockerfile("FROM testonbuildtrigger1")).Assert(c, icmd.Expected{ 1704 Out: "ONBUILD PARENT", 1705 }) 1706 // ONBUILD should *not* be run in third build. 1707 result := buildImage("testonbuildtrigger3", withDockerfile("FROM testonbuildtrigger2")) 1708 result.Assert(c, icmd.Success) 1709 if strings.Contains(result.Combined(), "ONBUILD PARENT") { 1710 c.Fatalf("ONBUILD instruction ran in grandchild of ONBUILD parent") 1711 } 1712 } 1713 1714 func (s *DockerSuite) TestBuildSameDockerfileWithAndWithoutCache(c *check.C) { 1715 testRequires(c, DaemonIsLinux) // Expose not implemented on Windows 1716 name := "testbuildwithcache" 1717 dockerfile := `FROM scratch 1718 MAINTAINER dockerio 1719 EXPOSE 5432 1720 ENTRYPOINT ["/bin/echo"]` 1721 buildImageSuccessfully(c, name, withDockerfile(dockerfile)) 1722 id1 := getIDByName(c, name) 1723 buildImageSuccessfully(c, name, withDockerfile(dockerfile)) 1724 id2 := getIDByName(c, name) 1725 buildImageSuccessfully(c, name, withoutCache, withDockerfile(dockerfile)) 1726 id3 := getIDByName(c, name) 1727 if id1 != id2 { 1728 c.Fatal("The cache should have been used but hasn't.") 1729 } 1730 if id1 == id3 { 1731 c.Fatal("The cache should have been invalided but hasn't.") 1732 } 1733 } 1734 1735 // Make sure that ADD/COPY still populate the cache even if they don't use it 1736 func (s *DockerSuite) TestBuildConditionalCache(c *check.C) { 1737 name := "testbuildconditionalcache" 1738 1739 dockerfile := ` 1740 FROM busybox 1741 ADD foo /tmp/` 1742 ctx := fakeContext(c, dockerfile, map[string]string{ 1743 "foo": "hello", 1744 }) 1745 defer ctx.Close() 1746 1747 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1748 id1 := getIDByName(c, name) 1749 1750 if err := ctx.Add("foo", "bye"); err != nil { 1751 c.Fatalf("Error modifying foo: %s", err) 1752 } 1753 1754 // Updating a file should invalidate the cache 1755 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1756 id2 := getIDByName(c, name) 1757 if id2 == id1 { 1758 c.Fatal("Should not have used the cache") 1759 } 1760 1761 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1762 id3 := getIDByName(c, name) 1763 if id3 != id2 { 1764 c.Fatal("Should have used the cache") 1765 } 1766 } 1767 1768 // FIXME(vdemeester) this really seems to test the same thing as before 1769 func (s *DockerSuite) TestBuildAddMultipleLocalFileWithAndWithoutCache(c *check.C) { 1770 name := "testbuildaddmultiplelocalfilewithcache" 1771 dockerfile := ` 1772 FROM busybox 1773 MAINTAINER dockerio 1774 ADD foo Dockerfile /usr/lib/bla/ 1775 RUN sh -c "[ $(cat /usr/lib/bla/foo) = "hello" ]"` 1776 ctx := fakeContext(c, dockerfile, map[string]string{ 1777 "foo": "hello", 1778 }) 1779 defer ctx.Close() 1780 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1781 id1 := getIDByName(c, name) 1782 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1783 id2 := getIDByName(c, name) 1784 buildImageSuccessfully(c, name, withoutCache, withExternalBuildContext(ctx)) 1785 id3 := getIDByName(c, name) 1786 if id1 != id2 { 1787 c.Fatal("The cache should have been used but hasn't.") 1788 } 1789 if id1 == id3 { 1790 c.Fatal("The cache should have been invalided but hasn't.") 1791 } 1792 } 1793 1794 func (s *DockerSuite) TestBuildCopyDirButNotFile(c *check.C) { 1795 name := "testbuildcopydirbutnotfile" 1796 name2 := "testbuildcopydirbutnotfile2" 1797 1798 dockerfile := ` 1799 FROM ` + minimalBaseImage() + ` 1800 COPY dir /tmp/` 1801 ctx := fakeContext(c, dockerfile, map[string]string{ 1802 "dir/foo": "hello", 1803 }) 1804 defer ctx.Close() 1805 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1806 id1 := getIDByName(c, name) 1807 // Check that adding file with similar name doesn't mess with cache 1808 if err := ctx.Add("dir_file", "hello2"); err != nil { 1809 c.Fatal(err) 1810 } 1811 buildImageSuccessfully(c, name2, withExternalBuildContext(ctx)) 1812 id2 := getIDByName(c, name2) 1813 if id1 != id2 { 1814 c.Fatal("The cache should have been used but wasn't") 1815 } 1816 } 1817 1818 func (s *DockerSuite) TestBuildAddCurrentDirWithCache(c *check.C) { 1819 name := "testbuildaddcurrentdirwithcache" 1820 name2 := name + "2" 1821 name3 := name + "3" 1822 name4 := name + "4" 1823 dockerfile := ` 1824 FROM ` + minimalBaseImage() + ` 1825 MAINTAINER dockerio 1826 ADD . /usr/lib/bla` 1827 ctx := fakeContext(c, dockerfile, map[string]string{ 1828 "foo": "hello", 1829 }) 1830 defer ctx.Close() 1831 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1832 id1 := getIDByName(c, name) 1833 // Check that adding file invalidate cache of "ADD ." 1834 if err := ctx.Add("bar", "hello2"); err != nil { 1835 c.Fatal(err) 1836 } 1837 buildImageSuccessfully(c, name2, withExternalBuildContext(ctx)) 1838 id2 := getIDByName(c, name2) 1839 if id1 == id2 { 1840 c.Fatal("The cache should have been invalided but hasn't.") 1841 } 1842 // Check that changing file invalidate cache of "ADD ." 1843 if err := ctx.Add("foo", "hello1"); err != nil { 1844 c.Fatal(err) 1845 } 1846 buildImageSuccessfully(c, name3, withExternalBuildContext(ctx)) 1847 id3 := getIDByName(c, name3) 1848 if id2 == id3 { 1849 c.Fatal("The cache should have been invalided but hasn't.") 1850 } 1851 // Check that changing file to same content with different mtime does not 1852 // invalidate cache of "ADD ." 1853 time.Sleep(1 * time.Second) // wait second because of mtime precision 1854 if err := ctx.Add("foo", "hello1"); err != nil { 1855 c.Fatal(err) 1856 } 1857 buildImageSuccessfully(c, name4, withExternalBuildContext(ctx)) 1858 id4 := getIDByName(c, name4) 1859 if id3 != id4 { 1860 c.Fatal("The cache should have been used but hasn't.") 1861 } 1862 } 1863 1864 // FIXME(vdemeester) this really seems to test the same thing as before (TestBuildAddMultipleLocalFileWithAndWithoutCache) 1865 func (s *DockerSuite) TestBuildAddCurrentDirWithoutCache(c *check.C) { 1866 name := "testbuildaddcurrentdirwithoutcache" 1867 dockerfile := ` 1868 FROM ` + minimalBaseImage() + ` 1869 MAINTAINER dockerio 1870 ADD . /usr/lib/bla` 1871 ctx := fakeContext(c, dockerfile, map[string]string{ 1872 "foo": "hello", 1873 }) 1874 defer ctx.Close() 1875 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1876 id1 := getIDByName(c, name) 1877 buildImageSuccessfully(c, name, withoutCache, withExternalBuildContext(ctx)) 1878 id2 := getIDByName(c, name) 1879 if id1 == id2 { 1880 c.Fatal("The cache should have been invalided but hasn't.") 1881 } 1882 } 1883 1884 func (s *DockerSuite) TestBuildAddRemoteFileWithAndWithoutCache(c *check.C) { 1885 name := "testbuildaddremotefilewithcache" 1886 server := fakeStorage(c, map[string]string{ 1887 "baz": "hello", 1888 }) 1889 defer server.Close() 1890 1891 dockerfile := fmt.Sprintf(`FROM `+minimalBaseImage()+` 1892 MAINTAINER dockerio 1893 ADD %s/baz /usr/lib/baz/quux`, server.URL()) 1894 buildImageSuccessfully(c, name, withDockerfile(dockerfile)) 1895 id1 := getIDByName(c, name) 1896 buildImageSuccessfully(c, name, withDockerfile(dockerfile)) 1897 id2 := getIDByName(c, name) 1898 buildImageSuccessfully(c, name, withoutCache, withDockerfile(dockerfile)) 1899 id3 := getIDByName(c, name) 1900 1901 if id1 != id2 { 1902 c.Fatal("The cache should have been used but hasn't.") 1903 } 1904 if id1 == id3 { 1905 c.Fatal("The cache should have been invalided but hasn't.") 1906 } 1907 } 1908 1909 func (s *DockerSuite) TestBuildAddRemoteFileMTime(c *check.C) { 1910 name := "testbuildaddremotefilemtime" 1911 name2 := name + "2" 1912 name3 := name + "3" 1913 1914 files := map[string]string{"baz": "hello"} 1915 server := fakeStorage(c, files) 1916 defer server.Close() 1917 1918 ctx := fakeContext(c, fmt.Sprintf(`FROM `+minimalBaseImage()+` 1919 MAINTAINER dockerio 1920 ADD %s/baz /usr/lib/baz/quux`, server.URL()), nil) 1921 defer ctx.Close() 1922 1923 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1924 id1 := getIDByName(c, name) 1925 buildImageSuccessfully(c, name2, withExternalBuildContext(ctx)) 1926 id2 := getIDByName(c, name2) 1927 if id1 != id2 { 1928 c.Fatal("The cache should have been used but wasn't - #1") 1929 } 1930 1931 // Now create a different server with same contents (causes different mtime) 1932 // The cache should still be used 1933 1934 // allow some time for clock to pass as mtime precision is only 1s 1935 time.Sleep(2 * time.Second) 1936 1937 server2 := fakeStorage(c, files) 1938 defer server2.Close() 1939 1940 ctx2 := fakeContext(c, fmt.Sprintf(`FROM `+minimalBaseImage()+` 1941 MAINTAINER dockerio 1942 ADD %s/baz /usr/lib/baz/quux`, server2.URL()), nil) 1943 defer ctx2.Close() 1944 buildImageSuccessfully(c, name3, withExternalBuildContext(ctx2)) 1945 id3 := getIDByName(c, name3) 1946 if id1 != id3 { 1947 c.Fatal("The cache should have been used but wasn't") 1948 } 1949 } 1950 1951 // FIXME(vdemeester) this really seems to test the same thing as before (combined) 1952 func (s *DockerSuite) TestBuildAddLocalAndRemoteFilesWithAndWithoutCache(c *check.C) { 1953 name := "testbuildaddlocalandremotefilewithcache" 1954 server := fakeStorage(c, map[string]string{ 1955 "baz": "hello", 1956 }) 1957 defer server.Close() 1958 1959 ctx := fakeContext(c, fmt.Sprintf(`FROM `+minimalBaseImage()+` 1960 MAINTAINER dockerio 1961 ADD foo /usr/lib/bla/bar 1962 ADD %s/baz /usr/lib/baz/quux`, server.URL()), 1963 map[string]string{ 1964 "foo": "hello world", 1965 }) 1966 defer ctx.Close() 1967 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1968 id1 := getIDByName(c, name) 1969 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 1970 id2 := getIDByName(c, name) 1971 buildImageSuccessfully(c, name, withoutCache, withExternalBuildContext(ctx)) 1972 id3 := getIDByName(c, name) 1973 if id1 != id2 { 1974 c.Fatal("The cache should have been used but hasn't.") 1975 } 1976 if id1 == id3 { 1977 c.Fatal("The cache should have been invalidated but hasn't.") 1978 } 1979 } 1980 1981 func testContextTar(c *check.C, compression archive.Compression) { 1982 ctx := fakeContext(c, 1983 `FROM busybox 1984 ADD foo /foo 1985 CMD ["cat", "/foo"]`, 1986 map[string]string{ 1987 "foo": "bar", 1988 }, 1989 ) 1990 defer ctx.Close() 1991 context, err := archive.Tar(ctx.Dir, compression) 1992 if err != nil { 1993 c.Fatalf("failed to build context tar: %v", err) 1994 } 1995 name := "contexttar" 1996 1997 icmd.RunCmd(icmd.Cmd{ 1998 Command: []string{dockerBinary, "build", "-t", name, "-"}, 1999 Stdin: context, 2000 }).Assert(c, icmd.Success) 2001 } 2002 2003 func (s *DockerSuite) TestBuildContextTarGzip(c *check.C) { 2004 testContextTar(c, archive.Gzip) 2005 } 2006 2007 func (s *DockerSuite) TestBuildContextTarNoCompression(c *check.C) { 2008 testContextTar(c, archive.Uncompressed) 2009 } 2010 2011 func (s *DockerSuite) TestBuildNoContext(c *check.C) { 2012 name := "nocontext" 2013 icmd.RunCmd(icmd.Cmd{ 2014 Command: []string{dockerBinary, "build", "-t", name, "-"}, 2015 Stdin: strings.NewReader( 2016 `FROM busybox 2017 CMD ["echo", "ok"]`), 2018 }).Assert(c, icmd.Success) 2019 2020 if out, _ := dockerCmd(c, "run", "--rm", "nocontext"); out != "ok\n" { 2021 c.Fatalf("run produced invalid output: %q, expected %q", out, "ok") 2022 } 2023 } 2024 2025 func (s *DockerSuite) TestBuildWithVolumeOwnership(c *check.C) { 2026 testRequires(c, DaemonIsLinux) 2027 name := "testbuildimg" 2028 2029 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox:latest 2030 RUN mkdir /test && chown daemon:daemon /test && chmod 0600 /test 2031 VOLUME /test`)) 2032 2033 out, _ := dockerCmd(c, "run", "--rm", "testbuildimg", "ls", "-la", "/test") 2034 if expected := "drw-------"; !strings.Contains(out, expected) { 2035 c.Fatalf("expected %s received %s", expected, out) 2036 } 2037 if expected := "daemon daemon"; !strings.Contains(out, expected) { 2038 c.Fatalf("expected %s received %s", expected, out) 2039 } 2040 2041 } 2042 2043 // testing #1405 - config.Cmd does not get cleaned up if 2044 // utilizing cache 2045 func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *check.C) { 2046 name := "testbuildcmdcleanup" 2047 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2048 RUN echo "hello"`)) 2049 2050 buildImageSuccessfully(c, name, withBuildContext(c, 2051 withFile("Dockerfile", `FROM busybox 2052 RUN echo "hello" 2053 ADD foo /foo 2054 ENTRYPOINT ["/bin/echo"]`), 2055 withFile("foo", "hello"))) 2056 2057 res := inspectField(c, name, "Config.Cmd") 2058 // Cmd must be cleaned up 2059 if res != "[]" { 2060 c.Fatalf("Cmd %s, expected nil", res) 2061 } 2062 } 2063 2064 func (s *DockerSuite) TestBuildAddFileNotFound(c *check.C) { 2065 name := "testbuildaddnotfound" 2066 expected := "foo: no such file or directory" 2067 2068 if testEnv.DaemonPlatform() == "windows" { 2069 expected = "foo: The system cannot find the file specified" 2070 } 2071 2072 buildImage(name, withBuildContext(c, 2073 withFile("Dockerfile", `FROM `+minimalBaseImage()+` 2074 ADD foo /usr/local/bar`), 2075 withFile("bar", "hello"))).Assert(c, icmd.Expected{ 2076 ExitCode: 1, 2077 Err: expected, 2078 }) 2079 } 2080 2081 func (s *DockerSuite) TestBuildInheritance(c *check.C) { 2082 testRequires(c, DaemonIsLinux) 2083 name := "testbuildinheritance" 2084 2085 buildImageSuccessfully(c, name, withDockerfile(`FROM scratch 2086 EXPOSE 2375`)) 2087 ports1 := inspectField(c, name, "Config.ExposedPorts") 2088 2089 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(`FROM %s 2090 ENTRYPOINT ["/bin/echo"]`, name))) 2091 2092 res := inspectField(c, name, "Config.Entrypoint") 2093 if expected := "[/bin/echo]"; res != expected { 2094 c.Fatalf("Entrypoint %s, expected %s", res, expected) 2095 } 2096 ports2 := inspectField(c, name, "Config.ExposedPorts") 2097 if ports1 != ports2 { 2098 c.Fatalf("Ports must be same: %s != %s", ports1, ports2) 2099 } 2100 } 2101 2102 func (s *DockerSuite) TestBuildFails(c *check.C) { 2103 name := "testbuildfails" 2104 buildImage(name, withDockerfile(`FROM busybox 2105 RUN sh -c "exit 23"`)).Assert(c, icmd.Expected{ 2106 ExitCode: 23, 2107 Err: "returned a non-zero code: 23", 2108 }) 2109 } 2110 2111 func (s *DockerSuite) TestBuildOnBuild(c *check.C) { 2112 name := "testbuildonbuild" 2113 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2114 ONBUILD RUN touch foobar`)) 2115 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(`FROM %s 2116 RUN [ -f foobar ]`, name))) 2117 } 2118 2119 // gh #2446 2120 func (s *DockerSuite) TestBuildAddToSymlinkDest(c *check.C) { 2121 makeLink := `ln -s /foo /bar` 2122 if testEnv.DaemonPlatform() == "windows" { 2123 makeLink = `mklink /D C:\bar C:\foo` 2124 } 2125 name := "testbuildaddtosymlinkdest" 2126 buildImageSuccessfully(c, name, withBuildContext(c, 2127 withFile("Dockerfile", ` 2128 FROM busybox 2129 RUN sh -c "mkdir /foo" 2130 RUN `+makeLink+` 2131 ADD foo /bar/ 2132 RUN sh -c "[ -f /bar/foo ]" 2133 RUN sh -c "[ -f /foo/foo ]"`), 2134 withFile("foo", "hello"), 2135 )) 2136 } 2137 2138 func (s *DockerSuite) TestBuildEscapeWhitespace(c *check.C) { 2139 name := "testbuildescapewhitespace" 2140 2141 buildImageSuccessfully(c, name, withDockerfile(` 2142 # ESCAPE=\ 2143 FROM busybox 2144 MAINTAINER "Docker \ 2145 IO <io@\ 2146 docker.com>" 2147 `)) 2148 2149 res := inspectField(c, name, "Author") 2150 if res != "\"Docker IO <io@docker.com>\"" { 2151 c.Fatalf("Parsed string did not match the escaped string. Got: %q", res) 2152 } 2153 2154 } 2155 2156 func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) { 2157 // Verify that strings that look like ints are still passed as strings 2158 name := "testbuildstringing" 2159 2160 buildImageSuccessfully(c, name, withDockerfile(` 2161 FROM busybox 2162 MAINTAINER 123`)) 2163 2164 out, _ := dockerCmd(c, "inspect", name) 2165 if !strings.Contains(out, "\"123\"") { 2166 c.Fatalf("Output does not contain the int as a string:\n%s", out) 2167 } 2168 2169 } 2170 2171 func (s *DockerSuite) TestBuildDockerignore(c *check.C) { 2172 name := "testbuilddockerignore" 2173 buildImageSuccessfully(c, name, withBuildContext(c, 2174 withFile("Dockerfile", ` 2175 FROM busybox 2176 ADD . /bla 2177 RUN sh -c "[[ -f /bla/src/x.go ]]" 2178 RUN sh -c "[[ -f /bla/Makefile ]]" 2179 RUN sh -c "[[ ! -e /bla/src/_vendor ]]" 2180 RUN sh -c "[[ ! -e /bla/.gitignore ]]" 2181 RUN sh -c "[[ ! -e /bla/README.md ]]" 2182 RUN sh -c "[[ ! -e /bla/dir/foo ]]" 2183 RUN sh -c "[[ ! -e /bla/foo ]]" 2184 RUN sh -c "[[ ! -e /bla/.git ]]" 2185 RUN sh -c "[[ ! -e v.cc ]]" 2186 RUN sh -c "[[ ! -e src/v.cc ]]" 2187 RUN sh -c "[[ ! -e src/_vendor/v.cc ]]"`), 2188 withFile("Makefile", "all:"), 2189 withFile(".git/HEAD", "ref: foo"), 2190 withFile("src/x.go", "package main"), 2191 withFile("src/_vendor/v.go", "package main"), 2192 withFile("src/_vendor/v.cc", "package main"), 2193 withFile("src/v.cc", "package main"), 2194 withFile("v.cc", "package main"), 2195 withFile("dir/foo", ""), 2196 withFile(".gitignore", ""), 2197 withFile("README.md", "readme"), 2198 withFile(".dockerignore", ` 2199 .git 2200 pkg 2201 .gitignore 2202 src/_vendor 2203 *.md 2204 **/*.cc 2205 dir`), 2206 )) 2207 } 2208 2209 func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *check.C) { 2210 name := "testbuilddockerignorecleanpaths" 2211 buildImageSuccessfully(c, name, withBuildContext(c, 2212 withFile("Dockerfile", ` 2213 FROM busybox 2214 ADD . /tmp/ 2215 RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (! ls /tmp/dir1/foo)"`), 2216 withFile("foo", "foo"), 2217 withFile("foo2", "foo2"), 2218 withFile("dir1/foo", "foo in dir1"), 2219 withFile(".dockerignore", "./foo\ndir1//foo\n./dir1/../foo2"), 2220 )) 2221 } 2222 2223 func (s *DockerSuite) TestBuildDockerignoreExceptions(c *check.C) { 2224 name := "testbuilddockerignoreexceptions" 2225 buildImageSuccessfully(c, name, withBuildContext(c, 2226 withFile("Dockerfile", ` 2227 FROM busybox 2228 ADD . /bla 2229 RUN sh -c "[[ -f /bla/src/x.go ]]" 2230 RUN sh -c "[[ -f /bla/Makefile ]]" 2231 RUN sh -c "[[ ! -e /bla/src/_vendor ]]" 2232 RUN sh -c "[[ ! -e /bla/.gitignore ]]" 2233 RUN sh -c "[[ ! -e /bla/README.md ]]" 2234 RUN sh -c "[[ -e /bla/dir/dir/foo ]]" 2235 RUN sh -c "[[ ! -e /bla/dir/foo1 ]]" 2236 RUN sh -c "[[ -f /bla/dir/e ]]" 2237 RUN sh -c "[[ -f /bla/dir/e-dir/foo ]]" 2238 RUN sh -c "[[ ! -e /bla/foo ]]" 2239 RUN sh -c "[[ ! -e /bla/.git ]]" 2240 RUN sh -c "[[ -e /bla/dir/a.cc ]]"`), 2241 withFile("Makefile", "all:"), 2242 withFile(".git/HEAD", "ref: foo"), 2243 withFile("src/x.go", "package main"), 2244 withFile("src/_vendor/v.go", "package main"), 2245 withFile("dir/foo", ""), 2246 withFile("dir/foo1", ""), 2247 withFile("dir/dir/f1", ""), 2248 withFile("dir/dir/foo", ""), 2249 withFile("dir/e", ""), 2250 withFile("dir/e-dir/foo", ""), 2251 withFile(".gitignore", ""), 2252 withFile("README.md", "readme"), 2253 withFile("dir/a.cc", "hello"), 2254 withFile(".dockerignore", ` 2255 .git 2256 pkg 2257 .gitignore 2258 src/_vendor 2259 *.md 2260 dir 2261 !dir/e* 2262 !dir/dir/foo 2263 **/*.cc 2264 !**/*.cc`), 2265 )) 2266 } 2267 2268 func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *check.C) { 2269 name := "testbuilddockerignoredockerfile" 2270 dockerfile := ` 2271 FROM busybox 2272 ADD . /tmp/ 2273 RUN sh -c "! ls /tmp/Dockerfile" 2274 RUN ls /tmp/.dockerignore` 2275 buildImageSuccessfully(c, name, withBuildContext(c, 2276 withFile("Dockerfile", dockerfile), 2277 withFile(".dockerignore", "Dockerfile\n"), 2278 )) 2279 buildImageSuccessfully(c, name, withBuildContext(c, 2280 withFile("Dockerfile", dockerfile), 2281 withFile(".dockerignore", "./Dockerfile\n"), 2282 )) 2283 } 2284 2285 func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *check.C) { 2286 name := "testbuilddockerignoredockerfile" 2287 dockerfile := ` 2288 FROM busybox 2289 ADD . /tmp/ 2290 RUN ls /tmp/Dockerfile 2291 RUN sh -c "! ls /tmp/MyDockerfile" 2292 RUN ls /tmp/.dockerignore` 2293 buildImageSuccessfully(c, name, withBuildFlags("-f", "MyDockerfile"), withBuildContext(c, 2294 withFile("Dockerfile", "Should not use me"), 2295 withFile("MyDockerfile", dockerfile), 2296 withFile(".dockerignore", "MyDockerfile\n"), 2297 )) 2298 buildImageSuccessfully(c, name, withBuildFlags("-f", "MyDockerfile"), withBuildContext(c, 2299 withFile("Dockerfile", "Should not use me"), 2300 withFile("MyDockerfile", dockerfile), 2301 withFile(".dockerignore", "./MyDockerfile\n"), 2302 )) 2303 } 2304 2305 func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *check.C) { 2306 name := "testbuilddockerignoredockerignore" 2307 dockerfile := ` 2308 FROM busybox 2309 ADD . /tmp/ 2310 RUN sh -c "! ls /tmp/.dockerignore" 2311 RUN ls /tmp/Dockerfile` 2312 buildImageSuccessfully(c, name, withBuildContext(c, 2313 withFile("Dockerfile", dockerfile), 2314 withFile(".dockerignore", ".dockerignore\n"), 2315 )) 2316 } 2317 2318 func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *check.C) { 2319 name := "testbuilddockerignoretouchdockerfile" 2320 dockerfile := ` 2321 FROM busybox 2322 ADD . /tmp/` 2323 ctx := fakeContext(c, dockerfile, map[string]string{ 2324 "Dockerfile": dockerfile, 2325 ".dockerignore": "Dockerfile\n", 2326 }) 2327 defer ctx.Close() 2328 2329 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2330 id1 := getIDByName(c, name) 2331 2332 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2333 id2 := getIDByName(c, name) 2334 if id1 != id2 { 2335 c.Fatalf("Didn't use the cache - 1") 2336 } 2337 2338 // Now make sure touching Dockerfile doesn't invalidate the cache 2339 if err := ctx.Add("Dockerfile", dockerfile+"\n# hi"); err != nil { 2340 c.Fatalf("Didn't add Dockerfile: %s", err) 2341 } 2342 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2343 id2 = getIDByName(c, name) 2344 if id1 != id2 { 2345 c.Fatalf("Didn't use the cache - 2") 2346 } 2347 2348 // One more time but just 'touch' it instead of changing the content 2349 if err := ctx.Add("Dockerfile", dockerfile+"\n# hi"); err != nil { 2350 c.Fatalf("Didn't add Dockerfile: %s", err) 2351 } 2352 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2353 id2 = getIDByName(c, name) 2354 if id1 != id2 { 2355 c.Fatalf("Didn't use the cache - 3") 2356 } 2357 } 2358 2359 func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) { 2360 name := "testbuilddockerignorewholedir" 2361 2362 dockerfile := ` 2363 FROM busybox 2364 COPY . / 2365 RUN sh -c "[[ ! -e /.gitignore ]]" 2366 RUN sh -c "[[ ! -e /Makefile ]]"` 2367 2368 buildImageSuccessfully(c, name, withBuildContext(c, 2369 withFile("Dockerfile", dockerfile), 2370 withFile(".dockerignore", "*\n"), 2371 withFile("Makefile", "all:"), 2372 withFile(".gitignore", ""), 2373 )) 2374 } 2375 2376 func (s *DockerSuite) TestBuildDockerignoringOnlyDotfiles(c *check.C) { 2377 name := "testbuilddockerignorewholedir" 2378 2379 dockerfile := ` 2380 FROM busybox 2381 COPY . / 2382 RUN sh -c "[[ ! -e /.gitignore ]]" 2383 RUN sh -c "[[ -f /Makefile ]]"` 2384 2385 buildImageSuccessfully(c, name, withBuildContext(c, 2386 withFile("Dockerfile", dockerfile), 2387 withFile(".dockerignore", ".*"), 2388 withFile("Makefile", "all:"), 2389 withFile(".gitignore", ""), 2390 )) 2391 } 2392 2393 func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *check.C) { 2394 name := "testbuilddockerignorebadexclusion" 2395 buildImage(name, withBuildContext(c, 2396 withFile("Dockerfile", ` 2397 FROM busybox 2398 COPY . / 2399 RUN sh -c "[[ ! -e /.gitignore ]]" 2400 RUN sh -c "[[ -f /Makefile ]]"`), 2401 withFile("Makefile", "all:"), 2402 withFile(".gitignore", ""), 2403 withFile(".dockerignore", "!\n"), 2404 )).Assert(c, icmd.Expected{ 2405 ExitCode: 1, 2406 Err: "Error checking context: 'Illegal exclusion pattern: !", 2407 }) 2408 } 2409 2410 func (s *DockerSuite) TestBuildDockerignoringWildTopDir(c *check.C) { 2411 dockerfile := ` 2412 FROM busybox 2413 COPY . / 2414 RUN sh -c "[[ ! -e /.dockerignore ]]" 2415 RUN sh -c "[[ ! -e /Dockerfile ]]" 2416 RUN sh -c "[[ ! -e /file1 ]]" 2417 RUN sh -c "[[ ! -e /dir ]]"` 2418 2419 // All of these should result in ignoring all files 2420 for _, variant := range []string{"**", "**/", "**/**", "*"} { 2421 buildImageSuccessfully(c, "noname", withBuildContext(c, 2422 withFile("Dockerfile", dockerfile), 2423 withFile("file1", ""), 2424 withFile("dir/file1", ""), 2425 withFile(".dockerignore", variant), 2426 )) 2427 2428 dockerCmd(c, "rmi", "noname") 2429 } 2430 } 2431 2432 func (s *DockerSuite) TestBuildDockerignoringWildDirs(c *check.C) { 2433 dockerfile := ` 2434 FROM busybox 2435 COPY . / 2436 #RUN sh -c "[[ -e /.dockerignore ]]" 2437 RUN sh -c "[[ -e /Dockerfile ]] && \ 2438 [[ ! -e /file0 ]] && \ 2439 [[ ! -e /dir1/file0 ]] && \ 2440 [[ ! -e /dir2/file0 ]] && \ 2441 [[ ! -e /file1 ]] && \ 2442 [[ ! -e /dir1/file1 ]] && \ 2443 [[ ! -e /dir1/dir2/file1 ]] && \ 2444 [[ ! -e /dir1/file2 ]] && \ 2445 [[ -e /dir1/dir2/file2 ]] && \ 2446 [[ ! -e /dir1/dir2/file4 ]] && \ 2447 [[ ! -e /dir1/dir2/file5 ]] && \ 2448 [[ ! -e /dir1/dir2/file6 ]] && \ 2449 [[ ! -e /dir1/dir3/file7 ]] && \ 2450 [[ ! -e /dir1/dir3/file8 ]] && \ 2451 [[ -e /dir1/dir3 ]] && \ 2452 [[ -e /dir1/dir4 ]] && \ 2453 [[ ! -e 'dir1/dir5/fileAA' ]] && \ 2454 [[ -e 'dir1/dir5/fileAB' ]] && \ 2455 [[ -e 'dir1/dir5/fileB' ]]" # "." in pattern means nothing 2456 2457 RUN echo all done!` 2458 2459 dockerignore := ` 2460 **/file0 2461 **/*file1 2462 **/dir1/file2 2463 dir1/**/file4 2464 **/dir2/file5 2465 **/dir1/dir2/file6 2466 dir1/dir3/** 2467 **/dir4/** 2468 **/file?A 2469 **/file\?B 2470 **/dir5/file. 2471 ` 2472 2473 buildImageSuccessfully(c, "noname", withBuildContext(c, 2474 withFile("Dockerfile", dockerfile), 2475 withFile(".dockerignore", dockerignore), 2476 withFile("dir1/file0", ""), 2477 withFile("dir1/dir2/file0", ""), 2478 withFile("file1", ""), 2479 withFile("dir1/file1", ""), 2480 withFile("dir1/dir2/file1", ""), 2481 withFile("dir1/file2", ""), 2482 withFile("dir1/dir2/file2", ""), // remains 2483 withFile("dir1/dir2/file4", ""), 2484 withFile("dir1/dir2/file5", ""), 2485 withFile("dir1/dir2/file6", ""), 2486 withFile("dir1/dir3/file7", ""), 2487 withFile("dir1/dir3/file8", ""), 2488 withFile("dir1/dir4/file9", ""), 2489 withFile("dir1/dir5/fileAA", ""), 2490 withFile("dir1/dir5/fileAB", ""), 2491 withFile("dir1/dir5/fileB", ""), 2492 )) 2493 } 2494 2495 func (s *DockerSuite) TestBuildLineBreak(c *check.C) { 2496 testRequires(c, DaemonIsLinux) 2497 name := "testbuildlinebreak" 2498 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2499 RUN sh -c 'echo root:testpass \ 2500 > /tmp/passwd' 2501 RUN mkdir -p /var/run/sshd 2502 RUN sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]" 2503 RUN sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`)) 2504 } 2505 2506 func (s *DockerSuite) TestBuildEOLInLine(c *check.C) { 2507 testRequires(c, DaemonIsLinux) 2508 name := "testbuildeolinline" 2509 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2510 RUN sh -c 'echo root:testpass > /tmp/passwd' 2511 RUN echo "foo \n bar"; echo "baz" 2512 RUN mkdir -p /var/run/sshd 2513 RUN sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]" 2514 RUN sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`)) 2515 } 2516 2517 func (s *DockerSuite) TestBuildCommentsShebangs(c *check.C) { 2518 testRequires(c, DaemonIsLinux) 2519 name := "testbuildcomments" 2520 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2521 # This is an ordinary comment. 2522 RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh 2523 RUN [ ! -x /hello.sh ] 2524 # comment with line break \ 2525 RUN chmod +x /hello.sh 2526 RUN [ -x /hello.sh ] 2527 RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ] 2528 RUN [ "$(/hello.sh)" = "hello world" ]`)) 2529 } 2530 2531 func (s *DockerSuite) TestBuildUsersAndGroups(c *check.C) { 2532 testRequires(c, DaemonIsLinux) 2533 name := "testbuildusers" 2534 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 2535 2536 # Make sure our defaults work 2537 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)" = '0:0/root:root' ] 2538 2539 # TODO decide if "args.user = strconv.Itoa(syscall.Getuid())" is acceptable behavior for changeUser in sysvinit instead of "return nil" when "USER" isn't specified (so that we get the proper group list even if that is the empty list, even in the default case of not supplying an explicit USER to run as, which implies USER 0) 2540 USER root 2541 RUN [ "$(id -G):$(id -Gn)" = '0 10:root wheel' ] 2542 2543 # Setup dockerio user and group 2544 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd && \ 2545 echo 'dockerio:x:1001:' >> /etc/group 2546 2547 # Make sure we can switch to our user and all the information is exactly as we expect it to be 2548 USER dockerio 2549 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2550 2551 # Switch back to root and double check that worked exactly as we might expect it to 2552 USER root 2553 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0 10:root wheel' ] && \ 2554 # Add a "supplementary" group for our dockerio user 2555 echo 'supplementary:x:1002:dockerio' >> /etc/group 2556 2557 # ... and then go verify that we get it like we expect 2558 USER dockerio 2559 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ] 2560 USER 1001 2561 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ] 2562 2563 # super test the new "user:group" syntax 2564 USER dockerio:dockerio 2565 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2566 USER 1001:dockerio 2567 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2568 USER dockerio:1001 2569 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2570 USER 1001:1001 2571 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2572 USER dockerio:supplementary 2573 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2574 USER dockerio:1002 2575 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2576 USER 1001:supplementary 2577 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2578 USER 1001:1002 2579 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2580 2581 # make sure unknown uid/gid still works properly 2582 USER 1042:1043 2583 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/1042:1043/1043:1043' ]`)) 2584 } 2585 2586 // FIXME(vdemeester) rename this test (and probably "merge" it with the one below TestBuildEnvUsage2) 2587 func (s *DockerSuite) TestBuildEnvUsage(c *check.C) { 2588 // /docker/world/hello is not owned by the correct user 2589 testRequires(c, NotUserNamespace) 2590 testRequires(c, DaemonIsLinux) 2591 name := "testbuildenvusage" 2592 dockerfile := `FROM busybox 2593 ENV HOME /root 2594 ENV PATH $HOME/bin:$PATH 2595 ENV PATH /tmp:$PATH 2596 RUN [ "$PATH" = "/tmp:$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ] 2597 ENV FOO /foo/baz 2598 ENV BAR /bar 2599 ENV BAZ $BAR 2600 ENV FOOPATH $PATH:$FOO 2601 RUN [ "$BAR" = "$BAZ" ] 2602 RUN [ "$FOOPATH" = "$PATH:/foo/baz" ] 2603 ENV FROM hello/docker/world 2604 ENV TO /docker/world/hello 2605 ADD $FROM $TO 2606 RUN [ "$(cat $TO)" = "hello" ] 2607 ENV abc=def 2608 ENV ghi=$abc 2609 RUN [ "$ghi" = "def" ] 2610 ` 2611 buildImageSuccessfully(c, name, withBuildContext(c, 2612 withFile("Dockerfile", dockerfile), 2613 withFile("hello/docker/world", "hello"), 2614 )) 2615 } 2616 2617 // FIXME(vdemeester) rename this test (and probably "merge" it with the one above TestBuildEnvUsage) 2618 func (s *DockerSuite) TestBuildEnvUsage2(c *check.C) { 2619 // /docker/world/hello is not owned by the correct user 2620 testRequires(c, NotUserNamespace) 2621 testRequires(c, DaemonIsLinux) 2622 name := "testbuildenvusage2" 2623 dockerfile := `FROM busybox 2624 ENV abc=def def="hello world" 2625 RUN [ "$abc,$def" = "def,hello world" ] 2626 ENV def=hello\ world v1=abc v2="hi there" v3='boogie nights' v4="with'quotes too" 2627 RUN [ "$def,$v1,$v2,$v3,$v4" = "hello world,abc,hi there,boogie nights,with'quotes too" ] 2628 ENV abc=zzz FROM=hello/docker/world 2629 ENV abc=zzz TO=/docker/world/hello 2630 ADD $FROM $TO 2631 RUN [ "$abc,$(cat $TO)" = "zzz,hello" ] 2632 ENV abc 'yyy' 2633 RUN [ $abc = 'yyy' ] 2634 ENV abc= 2635 RUN [ "$abc" = "" ] 2636 2637 # use grep to make sure if the builder substitutes \$foo by mistake 2638 # we don't get a false positive 2639 ENV abc=\$foo 2640 RUN [ "$abc" = "\$foo" ] && (echo "$abc" | grep foo) 2641 ENV abc \$foo 2642 RUN [ "$abc" = "\$foo" ] && (echo "$abc" | grep foo) 2643 2644 ENV abc=\'foo\' abc2=\"foo\" 2645 RUN [ "$abc,$abc2" = "'foo',\"foo\"" ] 2646 ENV abc "foo" 2647 RUN [ "$abc" = "foo" ] 2648 ENV abc 'foo' 2649 RUN [ "$abc" = 'foo' ] 2650 ENV abc \'foo\' 2651 RUN [ "$abc" = "'foo'" ] 2652 ENV abc \"foo\" 2653 RUN [ "$abc" = '"foo"' ] 2654 2655 ENV abc=ABC 2656 RUN [ "$abc" = "ABC" ] 2657 ENV def1=${abc:-DEF} def2=${ccc:-DEF} 2658 ENV def3=${ccc:-${def2}xx} def4=${abc:+ALT} def5=${def2:+${abc}:} def6=${ccc:-\$abc:} def7=${ccc:-\${abc}:} 2659 RUN [ "$def1,$def2,$def3,$def4,$def5,$def6,$def7" = 'ABC,DEF,DEFxx,ALT,ABC:,$abc:,${abc:}' ] 2660 ENV mypath=${mypath:+$mypath:}/home 2661 ENV mypath=${mypath:+$mypath:}/away 2662 RUN [ "$mypath" = '/home:/away' ] 2663 2664 ENV e1=bar 2665 ENV e2=$e1 e3=$e11 e4=\$e1 e5=\$e11 2666 RUN [ "$e0,$e1,$e2,$e3,$e4,$e5" = ',bar,bar,,$e1,$e11' ] 2667 2668 ENV ee1 bar 2669 ENV ee2 $ee1 2670 ENV ee3 $ee11 2671 ENV ee4 \$ee1 2672 ENV ee5 \$ee11 2673 RUN [ "$ee1,$ee2,$ee3,$ee4,$ee5" = 'bar,bar,,$ee1,$ee11' ] 2674 2675 ENV eee1="foo" eee2='foo' 2676 ENV eee3 "foo" 2677 ENV eee4 'foo' 2678 RUN [ "$eee1,$eee2,$eee3,$eee4" = 'foo,foo,foo,foo' ] 2679 2680 ` 2681 buildImageSuccessfully(c, name, withBuildContext(c, 2682 withFile("Dockerfile", dockerfile), 2683 withFile("hello/docker/world", "hello"), 2684 )) 2685 } 2686 2687 func (s *DockerSuite) TestBuildAddScript(c *check.C) { 2688 testRequires(c, DaemonIsLinux) 2689 name := "testbuildaddscript" 2690 dockerfile := ` 2691 FROM busybox 2692 ADD test /test 2693 RUN ["chmod","+x","/test"] 2694 RUN ["/test"] 2695 RUN [ "$(cat /testfile)" = 'test!' ]` 2696 2697 buildImageSuccessfully(c, name, withBuildContext(c, 2698 withFile("Dockerfile", dockerfile), 2699 withFile("test", "#!/bin/sh\necho 'test!' > /testfile"), 2700 )) 2701 } 2702 2703 func (s *DockerSuite) TestBuildAddTar(c *check.C) { 2704 // /test/foo is not owned by the correct user 2705 testRequires(c, NotUserNamespace) 2706 name := "testbuildaddtar" 2707 2708 ctx := func() *FakeContext { 2709 dockerfile := ` 2710 FROM busybox 2711 ADD test.tar / 2712 RUN cat /test/foo | grep Hi 2713 ADD test.tar /test.tar 2714 RUN cat /test.tar/test/foo | grep Hi 2715 ADD test.tar /unlikely-to-exist 2716 RUN cat /unlikely-to-exist/test/foo | grep Hi 2717 ADD test.tar /unlikely-to-exist-trailing-slash/ 2718 RUN cat /unlikely-to-exist-trailing-slash/test/foo | grep Hi 2719 RUN sh -c "mkdir /existing-directory" #sh -c is needed on Windows to use the correct mkdir 2720 ADD test.tar /existing-directory 2721 RUN cat /existing-directory/test/foo | grep Hi 2722 ADD test.tar /existing-directory-trailing-slash/ 2723 RUN cat /existing-directory-trailing-slash/test/foo | grep Hi` 2724 tmpDir, err := ioutil.TempDir("", "fake-context") 2725 c.Assert(err, check.IsNil) 2726 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2727 if err != nil { 2728 c.Fatalf("failed to create test.tar archive: %v", err) 2729 } 2730 defer testTar.Close() 2731 2732 tw := tar.NewWriter(testTar) 2733 2734 if err := tw.WriteHeader(&tar.Header{ 2735 Name: "test/foo", 2736 Size: 2, 2737 }); err != nil { 2738 c.Fatalf("failed to write tar file header: %v", err) 2739 } 2740 if _, err := tw.Write([]byte("Hi")); err != nil { 2741 c.Fatalf("failed to write tar file content: %v", err) 2742 } 2743 if err := tw.Close(); err != nil { 2744 c.Fatalf("failed to close tar archive: %v", err) 2745 } 2746 2747 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2748 c.Fatalf("failed to open destination dockerfile: %v", err) 2749 } 2750 return fakeContextFromDir(tmpDir) 2751 }() 2752 defer ctx.Close() 2753 2754 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2755 } 2756 2757 func (s *DockerSuite) TestBuildAddBrokenTar(c *check.C) { 2758 name := "testbuildaddbrokentar" 2759 2760 ctx := func() *FakeContext { 2761 dockerfile := ` 2762 FROM busybox 2763 ADD test.tar /` 2764 tmpDir, err := ioutil.TempDir("", "fake-context") 2765 c.Assert(err, check.IsNil) 2766 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2767 if err != nil { 2768 c.Fatalf("failed to create test.tar archive: %v", err) 2769 } 2770 defer testTar.Close() 2771 2772 tw := tar.NewWriter(testTar) 2773 2774 if err := tw.WriteHeader(&tar.Header{ 2775 Name: "test/foo", 2776 Size: 2, 2777 }); err != nil { 2778 c.Fatalf("failed to write tar file header: %v", err) 2779 } 2780 if _, err := tw.Write([]byte("Hi")); err != nil { 2781 c.Fatalf("failed to write tar file content: %v", err) 2782 } 2783 if err := tw.Close(); err != nil { 2784 c.Fatalf("failed to close tar archive: %v", err) 2785 } 2786 2787 // Corrupt the tar by removing one byte off the end 2788 stat, err := testTar.Stat() 2789 if err != nil { 2790 c.Fatalf("failed to stat tar archive: %v", err) 2791 } 2792 if err := testTar.Truncate(stat.Size() - 1); err != nil { 2793 c.Fatalf("failed to truncate tar archive: %v", err) 2794 } 2795 2796 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2797 c.Fatalf("failed to open destination dockerfile: %v", err) 2798 } 2799 return fakeContextFromDir(tmpDir) 2800 }() 2801 defer ctx.Close() 2802 2803 buildImage(name, withExternalBuildContext(ctx)).Assert(c, icmd.Expected{ 2804 ExitCode: 1, 2805 }) 2806 } 2807 2808 func (s *DockerSuite) TestBuildAddNonTar(c *check.C) { 2809 name := "testbuildaddnontar" 2810 2811 // Should not try to extract test.tar 2812 buildImageSuccessfully(c, name, withBuildContext(c, 2813 withFile("Dockerfile", ` 2814 FROM busybox 2815 ADD test.tar / 2816 RUN test -f /test.tar`), 2817 withFile("test.tar", "not_a_tar_file"), 2818 )) 2819 } 2820 2821 func (s *DockerSuite) TestBuildAddTarXz(c *check.C) { 2822 // /test/foo is not owned by the correct user 2823 testRequires(c, NotUserNamespace) 2824 testRequires(c, DaemonIsLinux) 2825 name := "testbuildaddtarxz" 2826 2827 ctx := func() *FakeContext { 2828 dockerfile := ` 2829 FROM busybox 2830 ADD test.tar.xz / 2831 RUN cat /test/foo | grep Hi` 2832 tmpDir, err := ioutil.TempDir("", "fake-context") 2833 c.Assert(err, check.IsNil) 2834 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2835 if err != nil { 2836 c.Fatalf("failed to create test.tar archive: %v", err) 2837 } 2838 defer testTar.Close() 2839 2840 tw := tar.NewWriter(testTar) 2841 2842 if err := tw.WriteHeader(&tar.Header{ 2843 Name: "test/foo", 2844 Size: 2, 2845 }); err != nil { 2846 c.Fatalf("failed to write tar file header: %v", err) 2847 } 2848 if _, err := tw.Write([]byte("Hi")); err != nil { 2849 c.Fatalf("failed to write tar file content: %v", err) 2850 } 2851 if err := tw.Close(); err != nil { 2852 c.Fatalf("failed to close tar archive: %v", err) 2853 } 2854 2855 icmd.RunCmd(icmd.Cmd{ 2856 Command: []string{"xz", "-k", "test.tar"}, 2857 Dir: tmpDir, 2858 }).Assert(c, icmd.Success) 2859 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2860 c.Fatalf("failed to open destination dockerfile: %v", err) 2861 } 2862 return fakeContextFromDir(tmpDir) 2863 }() 2864 2865 defer ctx.Close() 2866 2867 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2868 } 2869 2870 func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) { 2871 testRequires(c, DaemonIsLinux) 2872 name := "testbuildaddtarxzgz" 2873 2874 ctx := func() *FakeContext { 2875 dockerfile := ` 2876 FROM busybox 2877 ADD test.tar.xz.gz / 2878 RUN ls /test.tar.xz.gz` 2879 tmpDir, err := ioutil.TempDir("", "fake-context") 2880 c.Assert(err, check.IsNil) 2881 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2882 if err != nil { 2883 c.Fatalf("failed to create test.tar archive: %v", err) 2884 } 2885 defer testTar.Close() 2886 2887 tw := tar.NewWriter(testTar) 2888 2889 if err := tw.WriteHeader(&tar.Header{ 2890 Name: "test/foo", 2891 Size: 2, 2892 }); err != nil { 2893 c.Fatalf("failed to write tar file header: %v", err) 2894 } 2895 if _, err := tw.Write([]byte("Hi")); err != nil { 2896 c.Fatalf("failed to write tar file content: %v", err) 2897 } 2898 if err := tw.Close(); err != nil { 2899 c.Fatalf("failed to close tar archive: %v", err) 2900 } 2901 2902 icmd.RunCmd(icmd.Cmd{ 2903 Command: []string{"xz", "-k", "test.tar"}, 2904 Dir: tmpDir, 2905 }).Assert(c, icmd.Success) 2906 2907 icmd.RunCmd(icmd.Cmd{ 2908 Command: []string{"gzip", "test.tar.xz"}, 2909 Dir: tmpDir, 2910 }) 2911 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2912 c.Fatalf("failed to open destination dockerfile: %v", err) 2913 } 2914 return fakeContextFromDir(tmpDir) 2915 }() 2916 2917 defer ctx.Close() 2918 2919 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 2920 } 2921 2922 func (s *DockerSuite) TestBuildFromGit(c *check.C) { 2923 name := "testbuildfromgit" 2924 git := newFakeGit(c, "repo", map[string]string{ 2925 "Dockerfile": `FROM busybox 2926 ADD first /first 2927 RUN [ -f /first ] 2928 MAINTAINER docker`, 2929 "first": "test git data", 2930 }, true) 2931 defer git.Close() 2932 2933 buildImageSuccessfully(c, name, withBuildContextPath(git.RepoURL)) 2934 2935 res := inspectField(c, name, "Author") 2936 if res != "docker" { 2937 c.Fatalf("Maintainer should be docker, got %s", res) 2938 } 2939 } 2940 2941 func (s *DockerSuite) TestBuildFromGitWithContext(c *check.C) { 2942 name := "testbuildfromgit" 2943 git := newFakeGit(c, "repo", map[string]string{ 2944 "docker/Dockerfile": `FROM busybox 2945 ADD first /first 2946 RUN [ -f /first ] 2947 MAINTAINER docker`, 2948 "docker/first": "test git data", 2949 }, true) 2950 defer git.Close() 2951 2952 buildImageSuccessfully(c, name, withBuildContextPath(fmt.Sprintf("%s#master:docker", git.RepoURL))) 2953 2954 res := inspectField(c, name, "Author") 2955 if res != "docker" { 2956 c.Fatalf("Maintainer should be docker, got %s", res) 2957 } 2958 } 2959 2960 func (s *DockerSuite) TestBuildFromGitwithF(c *check.C) { 2961 name := "testbuildfromgitwithf" 2962 git := newFakeGit(c, "repo", map[string]string{ 2963 "myApp/myDockerfile": `FROM busybox 2964 RUN echo hi from Dockerfile`, 2965 }, true) 2966 defer git.Close() 2967 2968 buildImage(name, withBuildFlags("-f", "myApp/myDockerfile"), withBuildContextPath(git.RepoURL)).Assert(c, icmd.Expected{ 2969 Out: "hi from Dockerfile", 2970 }) 2971 } 2972 2973 func (s *DockerSuite) TestBuildFromRemoteTarball(c *check.C) { 2974 name := "testbuildfromremotetarball" 2975 2976 buffer := new(bytes.Buffer) 2977 tw := tar.NewWriter(buffer) 2978 defer tw.Close() 2979 2980 dockerfile := []byte(`FROM busybox 2981 MAINTAINER docker`) 2982 if err := tw.WriteHeader(&tar.Header{ 2983 Name: "Dockerfile", 2984 Size: int64(len(dockerfile)), 2985 }); err != nil { 2986 c.Fatalf("failed to write tar file header: %v", err) 2987 } 2988 if _, err := tw.Write(dockerfile); err != nil { 2989 c.Fatalf("failed to write tar file content: %v", err) 2990 } 2991 if err := tw.Close(); err != nil { 2992 c.Fatalf("failed to close tar archive: %v", err) 2993 } 2994 2995 server := fakeBinaryStorage(c, map[string]*bytes.Buffer{ 2996 "testT.tar": buffer, 2997 }) 2998 defer server.Close() 2999 3000 buildImageSuccessfully(c, name, withBuildContextPath(server.URL()+"/testT.tar")) 3001 3002 res := inspectField(c, name, "Author") 3003 if res != "docker" { 3004 c.Fatalf("Maintainer should be docker, got %s", res) 3005 } 3006 } 3007 3008 func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) { 3009 name := "testbuildcmdcleanuponentrypoint" 3010 3011 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 3012 CMD ["test"] 3013 ENTRYPOINT ["echo"]`)) 3014 buildImageSuccessfully(c, name, withDockerfile(fmt.Sprintf(`FROM %s 3015 ENTRYPOINT ["cat"]`, name))) 3016 3017 res := inspectField(c, name, "Config.Cmd") 3018 if res != "[]" { 3019 c.Fatalf("Cmd %s, expected nil", res) 3020 } 3021 res = inspectField(c, name, "Config.Entrypoint") 3022 if expected := "[cat]"; res != expected { 3023 c.Fatalf("Entrypoint %s, expected %s", res, expected) 3024 } 3025 } 3026 3027 func (s *DockerSuite) TestBuildClearCmd(c *check.C) { 3028 name := "testbuildclearcmd" 3029 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 3030 ENTRYPOINT ["/bin/bash"] 3031 CMD []`)) 3032 3033 res := inspectFieldJSON(c, name, "Config.Cmd") 3034 if res != "[]" { 3035 c.Fatalf("Cmd %s, expected %s", res, "[]") 3036 } 3037 } 3038 3039 func (s *DockerSuite) TestBuildEmptyCmd(c *check.C) { 3040 // Skip on Windows. Base image on Windows has a CMD set in the image. 3041 testRequires(c, DaemonIsLinux) 3042 3043 name := "testbuildemptycmd" 3044 buildImageSuccessfully(c, name, withDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")) 3045 3046 res := inspectFieldJSON(c, name, "Config.Cmd") 3047 if res != "null" { 3048 c.Fatalf("Cmd %s, expected %s", res, "null") 3049 } 3050 } 3051 3052 func (s *DockerSuite) TestBuildOnBuildOutput(c *check.C) { 3053 name := "testbuildonbuildparent" 3054 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nONBUILD RUN echo foo\n")) 3055 3056 buildImage(name, withDockerfile("FROM "+name+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{ 3057 Out: "# Executing 1 build trigger", 3058 }) 3059 } 3060 3061 // FIXME(vdemeester) should be a unit test 3062 func (s *DockerSuite) TestBuildInvalidTag(c *check.C) { 3063 name := "abcd:" + stringutils.GenerateRandomAlphaOnlyString(200) 3064 buildImage(name, withDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{ 3065 ExitCode: 125, 3066 Err: "invalid reference format", 3067 }) 3068 } 3069 3070 func (s *DockerSuite) TestBuildCmdShDashC(c *check.C) { 3071 name := "testbuildcmdshc" 3072 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nCMD echo cmd\n")) 3073 3074 res := inspectFieldJSON(c, name, "Config.Cmd") 3075 expected := `["/bin/sh","-c","echo cmd"]` 3076 if testEnv.DaemonPlatform() == "windows" { 3077 expected = `["cmd","/S","/C","echo cmd"]` 3078 } 3079 if res != expected { 3080 c.Fatalf("Expected value %s not in Config.Cmd: %s", expected, res) 3081 } 3082 3083 } 3084 3085 func (s *DockerSuite) TestBuildCmdSpaces(c *check.C) { 3086 // Test to make sure that when we strcat arrays we take into account 3087 // the arg separator to make sure ["echo","hi"] and ["echo hi"] don't 3088 // look the same 3089 name := "testbuildcmdspaces" 3090 3091 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nCMD [\"echo hi\"]\n")) 3092 id1 := getIDByName(c, name) 3093 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nCMD [\"echo\", \"hi\"]\n")) 3094 id2 := getIDByName(c, name) 3095 3096 if id1 == id2 { 3097 c.Fatal("Should not have resulted in the same CMD") 3098 } 3099 3100 // Now do the same with ENTRYPOINT 3101 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nENTRYPOINT [\"echo hi\"]\n")) 3102 id1 = getIDByName(c, name) 3103 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nENTRYPOINT [\"echo\", \"hi\"]\n")) 3104 id2 = getIDByName(c, name) 3105 3106 if id1 == id2 { 3107 c.Fatal("Should not have resulted in the same ENTRYPOINT") 3108 } 3109 } 3110 3111 func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *check.C) { 3112 name := "testbuildcmdjson" 3113 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nCMD [\"echo\", \"cmd\"]")) 3114 3115 res := inspectFieldJSON(c, name, "Config.Cmd") 3116 expected := `["echo","cmd"]` 3117 if res != expected { 3118 c.Fatalf("Expected value %s not in Config.Cmd: %s", expected, res) 3119 } 3120 } 3121 3122 func (s *DockerSuite) TestBuildEntrypointCanBeOverridenByChild(c *check.C) { 3123 buildImageSuccessfully(c, "parent", withDockerfile(` 3124 FROM busybox 3125 ENTRYPOINT exit 130 3126 `)) 3127 3128 icmd.RunCommand(dockerBinary, "run", "parent").Assert(c, icmd.Expected{ 3129 ExitCode: 130, 3130 }) 3131 3132 buildImageSuccessfully(c, "child", withDockerfile(` 3133 FROM parent 3134 ENTRYPOINT exit 5 3135 `)) 3136 3137 icmd.RunCommand(dockerBinary, "run", "child").Assert(c, icmd.Expected{ 3138 ExitCode: 5, 3139 }) 3140 } 3141 3142 func (s *DockerSuite) TestBuildEntrypointCanBeOverridenByChildInspect(c *check.C) { 3143 var ( 3144 name = "testbuildepinherit" 3145 name2 = "testbuildepinherit2" 3146 expected = `["/bin/sh","-c","echo quux"]` 3147 ) 3148 3149 if testEnv.DaemonPlatform() == "windows" { 3150 expected = `["cmd","/S","/C","echo quux"]` 3151 } 3152 3153 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nENTRYPOINT /foo/bar")) 3154 buildImageSuccessfully(c, name2, withDockerfile(fmt.Sprintf("FROM %s\nENTRYPOINT echo quux", name))) 3155 3156 res := inspectFieldJSON(c, name2, "Config.Entrypoint") 3157 if res != expected { 3158 c.Fatalf("Expected value %s not in Config.Entrypoint: %s", expected, res) 3159 } 3160 3161 icmd.RunCommand(dockerBinary, "run", name2).Assert(c, icmd.Expected{ 3162 Out: "quux", 3163 }) 3164 } 3165 3166 func (s *DockerSuite) TestBuildRunShEntrypoint(c *check.C) { 3167 name := "testbuildentrypoint" 3168 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3169 ENTRYPOINT echo`)) 3170 dockerCmd(c, "run", "--rm", name) 3171 } 3172 3173 func (s *DockerSuite) TestBuildExoticShellInterpolation(c *check.C) { 3174 testRequires(c, DaemonIsLinux) 3175 name := "testbuildexoticshellinterpolation" 3176 3177 buildImageSuccessfully(c, name, withDockerfile(` 3178 FROM busybox 3179 3180 ENV SOME_VAR a.b.c 3181 3182 RUN [ "$SOME_VAR" = 'a.b.c' ] 3183 RUN [ "${SOME_VAR}" = 'a.b.c' ] 3184 RUN [ "${SOME_VAR%.*}" = 'a.b' ] 3185 RUN [ "${SOME_VAR%%.*}" = 'a' ] 3186 RUN [ "${SOME_VAR#*.}" = 'b.c' ] 3187 RUN [ "${SOME_VAR##*.}" = 'c' ] 3188 RUN [ "${SOME_VAR/c/d}" = 'a.b.d' ] 3189 RUN [ "${#SOME_VAR}" = '5' ] 3190 3191 RUN [ "${SOME_UNSET_VAR:-$SOME_VAR}" = 'a.b.c' ] 3192 RUN [ "${SOME_VAR:+Version: ${SOME_VAR}}" = 'Version: a.b.c' ] 3193 RUN [ "${SOME_UNSET_VAR:+${SOME_VAR}}" = '' ] 3194 RUN [ "${SOME_UNSET_VAR:-${SOME_VAR:-d.e.f}}" = 'a.b.c' ] 3195 `)) 3196 } 3197 3198 func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) { 3199 // This testcase is supposed to generate an error because the 3200 // JSON array we're passing in on the CMD uses single quotes instead 3201 // of double quotes (per the JSON spec). This means we interpret it 3202 // as a "string" instead of "JSON array" and pass it on to "sh -c" and 3203 // it should barf on it. 3204 name := "testbuildsinglequotefails" 3205 expectedExitCode := 2 3206 if testEnv.DaemonPlatform() == "windows" { 3207 expectedExitCode = 127 3208 } 3209 3210 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3211 CMD [ '/bin/sh', '-c', 'echo hi' ]`)) 3212 3213 icmd.RunCommand(dockerBinary, "run", "--rm", name).Assert(c, icmd.Expected{ 3214 ExitCode: expectedExitCode, 3215 }) 3216 } 3217 3218 func (s *DockerSuite) TestBuildVerboseOut(c *check.C) { 3219 name := "testbuildverboseout" 3220 expected := "\n123\n" 3221 3222 if testEnv.DaemonPlatform() == "windows" { 3223 expected = "\n123\r\n" 3224 } 3225 3226 buildImage(name, withDockerfile(`FROM busybox 3227 RUN echo 123`)).Assert(c, icmd.Expected{ 3228 Out: expected, 3229 }) 3230 } 3231 3232 func (s *DockerSuite) TestBuildWithTabs(c *check.C) { 3233 name := "testbuildwithtabs" 3234 buildImageSuccessfully(c, name, withDockerfile("FROM busybox\nRUN echo\tone\t\ttwo")) 3235 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 3236 expected1 := `["/bin/sh","-c","echo\tone\t\ttwo"]` 3237 expected2 := `["/bin/sh","-c","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3238 if testEnv.DaemonPlatform() == "windows" { 3239 expected1 = `["cmd","/S","/C","echo\tone\t\ttwo"]` 3240 expected2 = `["cmd","/S","/C","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3241 } 3242 if res != expected1 && res != expected2 { 3243 c.Fatalf("Missing tabs.\nGot: %s\nExp: %s or %s", res, expected1, expected2) 3244 } 3245 } 3246 3247 func (s *DockerSuite) TestBuildLabels(c *check.C) { 3248 name := "testbuildlabel" 3249 expected := `{"License":"GPL","Vendor":"Acme"}` 3250 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3251 LABEL Vendor=Acme 3252 LABEL License GPL`)) 3253 res := inspectFieldJSON(c, name, "Config.Labels") 3254 if res != expected { 3255 c.Fatalf("Labels %s, expected %s", res, expected) 3256 } 3257 } 3258 3259 func (s *DockerSuite) TestBuildLabelsCache(c *check.C) { 3260 name := "testbuildlabelcache" 3261 3262 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3263 LABEL Vendor=Acme`)) 3264 id1 := getIDByName(c, name) 3265 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3266 LABEL Vendor=Acme`)) 3267 id2 := getIDByName(c, name) 3268 if id1 != id2 { 3269 c.Fatalf("Build 2 should have worked & used cache(%s,%s)", id1, id2) 3270 } 3271 3272 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3273 LABEL Vendor=Acme1`)) 3274 id2 = getIDByName(c, name) 3275 if id1 == id2 { 3276 c.Fatalf("Build 3 should have worked & NOT used cache(%s,%s)", id1, id2) 3277 } 3278 3279 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3280 LABEL Vendor Acme`)) 3281 id2 = getIDByName(c, name) 3282 if id1 != id2 { 3283 c.Fatalf("Build 4 should have worked & used cache(%s,%s)", id1, id2) 3284 } 3285 3286 // Now make sure the cache isn't used by mistake 3287 buildImageSuccessfully(c, name, withoutCache, withDockerfile(`FROM busybox 3288 LABEL f1=b1 f2=b2`)) 3289 3290 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 3291 LABEL f1=b1 f2=b2`)) 3292 id2 = getIDByName(c, name) 3293 if id1 == id2 { 3294 c.Fatalf("Build 6 should have worked & NOT used the cache(%s,%s)", id1, id2) 3295 } 3296 3297 } 3298 3299 func (s *DockerSuite) TestBuildNotVerboseSuccess(c *check.C) { 3300 // This test makes sure that -q works correctly when build is successful: 3301 // stdout has only the image ID (long image ID) and stderr is empty. 3302 outRegexp := regexp.MustCompile("^(sha256:|)[a-z0-9]{64}\\n$") 3303 buildFlags := withBuildFlags("-q") 3304 3305 tt := []struct { 3306 Name string 3307 BuildFunc func(string) *icmd.Result 3308 }{ 3309 { 3310 Name: "quiet_build_stdin_success", 3311 BuildFunc: func(name string) *icmd.Result { 3312 return buildImage(name, buildFlags, withDockerfile("FROM busybox")) 3313 }, 3314 }, 3315 { 3316 Name: "quiet_build_ctx_success", 3317 BuildFunc: func(name string) *icmd.Result { 3318 return buildImage(name, buildFlags, withBuildContext(c, 3319 withFile("Dockerfile", "FROM busybox"), 3320 withFile("quiet_build_success_fctx", "test"), 3321 )) 3322 }, 3323 }, 3324 { 3325 Name: "quiet_build_git_success", 3326 BuildFunc: func(name string) *icmd.Result { 3327 git := newFakeGit(c, "repo", map[string]string{ 3328 "Dockerfile": "FROM busybox", 3329 }, true) 3330 return buildImage(name, buildFlags, withBuildContextPath(git.RepoURL)) 3331 }, 3332 }, 3333 } 3334 3335 for _, te := range tt { 3336 result := te.BuildFunc(te.Name) 3337 result.Assert(c, icmd.Success) 3338 if outRegexp.Find([]byte(result.Stdout())) == nil { 3339 c.Fatalf("Test %s expected stdout to match the [%v] regexp, but it is [%v]", te.Name, outRegexp, result.Stdout()) 3340 } 3341 3342 if result.Stderr() != "" { 3343 c.Fatalf("Test %s expected stderr to be empty, but it is [%#v]", te.Name, result.Stderr()) 3344 } 3345 } 3346 3347 } 3348 3349 func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *check.C) { 3350 // This test makes sure that -q works correctly when build fails by 3351 // comparing between the stderr output in quiet mode and in stdout 3352 // and stderr output in verbose mode 3353 testRequires(c, Network) 3354 testName := "quiet_build_not_exists_image" 3355 dockerfile := "FROM busybox11" 3356 quietResult := buildImage(testName, withBuildFlags("-q"), withDockerfile(dockerfile)) 3357 quietResult.Assert(c, icmd.Expected{ 3358 ExitCode: 1, 3359 }) 3360 result := buildImage(testName, withDockerfile(dockerfile)) 3361 result.Assert(c, icmd.Expected{ 3362 ExitCode: 1, 3363 }) 3364 if quietResult.Stderr() != result.Combined() { 3365 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", testName, quietResult.Stderr(), result.Combined())) 3366 } 3367 } 3368 3369 func (s *DockerSuite) TestBuildNotVerboseFailure(c *check.C) { 3370 // This test makes sure that -q works correctly when build fails by 3371 // comparing between the stderr output in quiet mode and in stdout 3372 // and stderr output in verbose mode 3373 testCases := []struct { 3374 testName string 3375 dockerfile string 3376 }{ 3377 {"quiet_build_no_from_at_the_beginning", "RUN whoami"}, 3378 {"quiet_build_unknown_instr", "FROMD busybox"}, 3379 } 3380 3381 for _, tc := range testCases { 3382 quietResult := buildImage(tc.testName, withBuildFlags("-q"), withDockerfile(tc.dockerfile)) 3383 quietResult.Assert(c, icmd.Expected{ 3384 ExitCode: 1, 3385 }) 3386 result := buildImage(tc.testName, withDockerfile(tc.dockerfile)) 3387 result.Assert(c, icmd.Expected{ 3388 ExitCode: 1, 3389 }) 3390 if quietResult.Stderr() != result.Combined() { 3391 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", tc.testName, quietResult.Stderr(), result.Combined())) 3392 } 3393 } 3394 } 3395 3396 func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *check.C) { 3397 // This test ensures that when given a wrong URL, stderr in quiet mode and 3398 // stderr in verbose mode are identical. 3399 // TODO(vdemeester) with cobra, stdout has a carriage return too much so this test should not check stdout 3400 URL := "http://something.invalid" 3401 name := "quiet_build_wrong_remote" 3402 quietResult := buildImage(name, withBuildFlags("-q"), withBuildContextPath(URL)) 3403 quietResult.Assert(c, icmd.Expected{ 3404 ExitCode: 1, 3405 }) 3406 result := buildImage(name, withBuildContextPath(URL)) 3407 result.Assert(c, icmd.Expected{ 3408 ExitCode: 1, 3409 }) 3410 if strings.TrimSpace(quietResult.Stderr()) != strings.TrimSpace(result.Combined()) { 3411 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", name, quietResult.Stderr(), result.Combined())) 3412 } 3413 } 3414 3415 func (s *DockerSuite) TestBuildStderr(c *check.C) { 3416 // This test just makes sure that no non-error output goes 3417 // to stderr 3418 name := "testbuildstderr" 3419 result := buildImage(name, withDockerfile("FROM busybox\nRUN echo one")) 3420 result.Assert(c, icmd.Success) 3421 3422 // Windows to non-Windows should have a security warning 3423 if runtime.GOOS == "windows" && testEnv.DaemonPlatform() != "windows" && !strings.Contains(result.Stdout(), "SECURITY WARNING:") { 3424 c.Fatalf("Stdout contains unexpected output: %q", result.Stdout()) 3425 } 3426 3427 // Stderr should always be empty 3428 if result.Stderr() != "" { 3429 c.Fatalf("Stderr should have been empty, instead it's: %q", result.Stderr()) 3430 } 3431 } 3432 3433 func (s *DockerSuite) TestBuildChownSingleFile(c *check.C) { 3434 testRequires(c, UnixCli, DaemonIsLinux) // test uses chown: not available on windows 3435 3436 name := "testbuildchownsinglefile" 3437 3438 ctx := fakeContext(c, ` 3439 FROM busybox 3440 COPY test / 3441 RUN ls -l /test 3442 RUN [ $(ls -l /test | awk '{print $3":"$4}') = 'root:root' ] 3443 `, map[string]string{ 3444 "test": "test", 3445 }) 3446 defer ctx.Close() 3447 3448 if err := os.Chown(filepath.Join(ctx.Dir, "test"), 4242, 4242); err != nil { 3449 c.Fatal(err) 3450 } 3451 3452 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 3453 } 3454 3455 func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) { 3456 name := "testbuildsymlinkbreakout" 3457 tmpdir, err := ioutil.TempDir("", name) 3458 c.Assert(err, check.IsNil) 3459 defer os.RemoveAll(tmpdir) 3460 ctx := filepath.Join(tmpdir, "context") 3461 if err := os.MkdirAll(ctx, 0755); err != nil { 3462 c.Fatal(err) 3463 } 3464 if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(` 3465 from busybox 3466 add symlink.tar / 3467 add inject /symlink/ 3468 `), 0644); err != nil { 3469 c.Fatal(err) 3470 } 3471 inject := filepath.Join(ctx, "inject") 3472 if err := ioutil.WriteFile(inject, nil, 0644); err != nil { 3473 c.Fatal(err) 3474 } 3475 f, err := os.Create(filepath.Join(ctx, "symlink.tar")) 3476 if err != nil { 3477 c.Fatal(err) 3478 } 3479 w := tar.NewWriter(f) 3480 w.WriteHeader(&tar.Header{ 3481 Name: "symlink2", 3482 Typeflag: tar.TypeSymlink, 3483 Linkname: "/../../../../../../../../../../../../../../", 3484 Uid: os.Getuid(), 3485 Gid: os.Getgid(), 3486 }) 3487 w.WriteHeader(&tar.Header{ 3488 Name: "symlink", 3489 Typeflag: tar.TypeSymlink, 3490 Linkname: filepath.Join("symlink2", tmpdir), 3491 Uid: os.Getuid(), 3492 Gid: os.Getgid(), 3493 }) 3494 w.Close() 3495 f.Close() 3496 3497 buildImageSuccessfully(c, name, withoutCache, withExternalBuildContext(fakeContextFromDir(ctx))) 3498 if _, err := os.Lstat(filepath.Join(tmpdir, "inject")); err == nil { 3499 c.Fatal("symlink breakout - inject") 3500 } else if !os.IsNotExist(err) { 3501 c.Fatalf("unexpected error: %v", err) 3502 } 3503 } 3504 3505 func (s *DockerSuite) TestBuildXZHost(c *check.C) { 3506 // /usr/local/sbin/xz gets permission denied for the user 3507 testRequires(c, NotUserNamespace) 3508 testRequires(c, DaemonIsLinux) 3509 name := "testbuildxzhost" 3510 3511 buildImageSuccessfully(c, name, withBuildContext(c, 3512 withFile("Dockerfile", ` 3513 FROM busybox 3514 ADD xz /usr/local/sbin/ 3515 RUN chmod 755 /usr/local/sbin/xz 3516 ADD test.xz / 3517 RUN [ ! -e /injected ]`), 3518 withFile("test.xz", "\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\x02\x00"+ 3519 "\x21\x01\x16\x00\x00\x00\x74\x2f\xe5\xa3\x01\x00\x3f\xfd"+ 3520 "\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\x02\x00\x21"), 3521 withFile("xz", "#!/bin/sh\ntouch /injected"), 3522 )) 3523 } 3524 3525 func (s *DockerSuite) TestBuildVolumesRetainContents(c *check.C) { 3526 // /foo/file gets permission denied for the user 3527 testRequires(c, NotUserNamespace) 3528 testRequires(c, DaemonIsLinux) // TODO Windows: Issue #20127 3529 var ( 3530 name = "testbuildvolumescontent" 3531 expected = "some text" 3532 volName = "/foo" 3533 ) 3534 3535 if testEnv.DaemonPlatform() == "windows" { 3536 volName = "C:/foo" 3537 } 3538 3539 buildImageSuccessfully(c, name, withBuildContext(c, 3540 withFile("Dockerfile", ` 3541 FROM busybox 3542 COPY content /foo/file 3543 VOLUME `+volName+` 3544 CMD cat /foo/file`), 3545 withFile("content", expected), 3546 )) 3547 3548 out, _ := dockerCmd(c, "run", "--rm", name) 3549 if out != expected { 3550 c.Fatalf("expected file contents for /foo/file to be %q but received %q", expected, out) 3551 } 3552 3553 } 3554 3555 // FIXME(vdemeester) part of this should be unit test, other part should be clearer 3556 func (s *DockerSuite) TestBuildRenamedDockerfile(c *check.C) { 3557 ctx := fakeContext(c, `FROM busybox 3558 RUN echo from Dockerfile`, 3559 map[string]string{ 3560 "Dockerfile": "FROM busybox\nRUN echo from Dockerfile", 3561 "files/Dockerfile": "FROM busybox\nRUN echo from files/Dockerfile", 3562 "files/dFile": "FROM busybox\nRUN echo from files/dFile", 3563 "dFile": "FROM busybox\nRUN echo from dFile", 3564 "files/dFile2": "FROM busybox\nRUN echo from files/dFile2", 3565 }) 3566 defer ctx.Close() 3567 3568 out, _, err := dockerCmdInDir(c, ctx.Dir, "build", "-t", "test1", ".") 3569 if err != nil { 3570 c.Fatalf("Failed to build: %s\n%s", out, err) 3571 } 3572 if !strings.Contains(out, "from Dockerfile") { 3573 c.Fatalf("test1 should have used Dockerfile, output:%s", out) 3574 } 3575 3576 out, _, err = dockerCmdInDir(c, ctx.Dir, "build", "-f", filepath.Join("files", "Dockerfile"), "-t", "test2", ".") 3577 if err != nil { 3578 c.Fatal(err) 3579 } 3580 if !strings.Contains(out, "from files/Dockerfile") { 3581 c.Fatalf("test2 should have used files/Dockerfile, output:%s", out) 3582 } 3583 3584 out, _, err = dockerCmdInDir(c, ctx.Dir, "build", fmt.Sprintf("--file=%s", filepath.Join("files", "dFile")), "-t", "test3", ".") 3585 if err != nil { 3586 c.Fatal(err) 3587 } 3588 if !strings.Contains(out, "from files/dFile") { 3589 c.Fatalf("test3 should have used files/dFile, output:%s", out) 3590 } 3591 3592 out, _, err = dockerCmdInDir(c, ctx.Dir, "build", "--file=dFile", "-t", "test4", ".") 3593 if err != nil { 3594 c.Fatal(err) 3595 } 3596 if !strings.Contains(out, "from dFile") { 3597 c.Fatalf("test4 should have used dFile, output:%s", out) 3598 } 3599 3600 dirWithNoDockerfile, err := ioutil.TempDir(os.TempDir(), "test5") 3601 c.Assert(err, check.IsNil) 3602 nonDockerfileFile := filepath.Join(dirWithNoDockerfile, "notDockerfile") 3603 if _, err = os.Create(nonDockerfileFile); err != nil { 3604 c.Fatal(err) 3605 } 3606 out, _, err = dockerCmdInDir(c, ctx.Dir, "build", fmt.Sprintf("--file=%s", nonDockerfileFile), "-t", "test5", ".") 3607 3608 if err == nil { 3609 c.Fatalf("test5 was supposed to fail to find passwd") 3610 } 3611 3612 if expected := fmt.Sprintf("The Dockerfile (%s) must be within the build context (.)", nonDockerfileFile); !strings.Contains(out, expected) { 3613 c.Fatalf("wrong error message:%v\nexpected to contain=%v", out, expected) 3614 } 3615 3616 out, _, err = dockerCmdInDir(c, filepath.Join(ctx.Dir, "files"), "build", "-f", filepath.Join("..", "Dockerfile"), "-t", "test6", "..") 3617 if err != nil { 3618 c.Fatalf("test6 failed: %s", err) 3619 } 3620 if !strings.Contains(out, "from Dockerfile") { 3621 c.Fatalf("test6 should have used root Dockerfile, output:%s", out) 3622 } 3623 3624 out, _, err = dockerCmdInDir(c, filepath.Join(ctx.Dir, "files"), "build", "-f", filepath.Join(ctx.Dir, "files", "Dockerfile"), "-t", "test7", "..") 3625 if err != nil { 3626 c.Fatalf("test7 failed: %s", err) 3627 } 3628 if !strings.Contains(out, "from files/Dockerfile") { 3629 c.Fatalf("test7 should have used files Dockerfile, output:%s", out) 3630 } 3631 3632 out, _, err = dockerCmdInDir(c, filepath.Join(ctx.Dir, "files"), "build", "-f", filepath.Join("..", "Dockerfile"), "-t", "test8", ".") 3633 if err == nil || !strings.Contains(out, "must be within the build context") { 3634 c.Fatalf("test8 should have failed with Dockerfile out of context: %s", err) 3635 } 3636 3637 tmpDir := os.TempDir() 3638 out, _, err = dockerCmdInDir(c, tmpDir, "build", "-t", "test9", ctx.Dir) 3639 if err != nil { 3640 c.Fatalf("test9 - failed: %s", err) 3641 } 3642 if !strings.Contains(out, "from Dockerfile") { 3643 c.Fatalf("test9 should have used root Dockerfile, output:%s", out) 3644 } 3645 3646 out, _, err = dockerCmdInDir(c, filepath.Join(ctx.Dir, "files"), "build", "-f", "dFile2", "-t", "test10", ".") 3647 if err != nil { 3648 c.Fatalf("test10 should have worked: %s", err) 3649 } 3650 if !strings.Contains(out, "from files/dFile2") { 3651 c.Fatalf("test10 should have used files/dFile2, output:%s", out) 3652 } 3653 3654 } 3655 3656 func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) { 3657 testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows 3658 testRequires(c, DaemonIsLinux) 3659 3660 // If Dockerfile is not present, use dockerfile 3661 buildImage("test1", withBuildContext(c, 3662 withFile("dockerfile", `FROM busybox 3663 RUN echo from dockerfile`), 3664 )).Assert(c, icmd.Expected{ 3665 Out: "from dockerfile", 3666 }) 3667 3668 // Prefer Dockerfile in place of dockerfile 3669 buildImage("test1", withBuildContext(c, 3670 withFile("dockerfile", `FROM busybox 3671 RUN echo from dockerfile`), 3672 withFile("Dockerfile", `FROM busybox 3673 RUN echo from Dockerfile`), 3674 )).Assert(c, icmd.Expected{ 3675 Out: "from Dockerfile", 3676 }) 3677 } 3678 3679 func (s *DockerSuite) TestBuildFromURLWithF(c *check.C) { 3680 server := fakeStorage(c, map[string]string{"baz": `FROM busybox 3681 RUN echo from baz 3682 COPY * /tmp/ 3683 RUN find /tmp/`}) 3684 defer server.Close() 3685 3686 ctx := fakeContext(c, `FROM busybox 3687 RUN echo from Dockerfile`, 3688 map[string]string{}) 3689 defer ctx.Close() 3690 3691 // Make sure that -f is ignored and that we don't use the Dockerfile 3692 // that's in the current dir 3693 result := buildImage("test1", withBuildFlags("-f", "baz", server.URL()+"/baz"), func(cmd *icmd.Cmd) func() { 3694 cmd.Dir = ctx.Dir 3695 return nil 3696 }) 3697 result.Assert(c, icmd.Success) 3698 3699 if !strings.Contains(result.Combined(), "from baz") || 3700 strings.Contains(result.Combined(), "/tmp/baz") || 3701 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3702 c.Fatalf("Missing proper output: %s", result.Combined()) 3703 } 3704 3705 } 3706 3707 func (s *DockerSuite) TestBuildFromStdinWithF(c *check.C) { 3708 testRequires(c, DaemonIsLinux) // TODO Windows: This test is flaky; no idea why 3709 ctx := fakeContext(c, `FROM busybox 3710 RUN echo "from Dockerfile"`, 3711 map[string]string{}) 3712 defer ctx.Close() 3713 3714 // Make sure that -f is ignored and that we don't use the Dockerfile 3715 // that's in the current dir 3716 result := buildImage("test1", withBuildFlags("-f", "baz", "-"), func(cmd *icmd.Cmd) func() { 3717 cmd.Dir = ctx.Dir 3718 cmd.Stdin = strings.NewReader(`FROM busybox 3719 RUN echo "from baz" 3720 COPY * /tmp/ 3721 RUN sh -c "find /tmp/" # sh -c is needed on Windows to use the correct find`) 3722 return nil 3723 }) 3724 result.Assert(c, icmd.Success) 3725 3726 if !strings.Contains(result.Combined(), "from baz") || 3727 strings.Contains(result.Combined(), "/tmp/baz") || 3728 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3729 c.Fatalf("Missing proper output: %s", result.Combined()) 3730 } 3731 3732 } 3733 3734 func (s *DockerSuite) TestBuildFromOfficialNames(c *check.C) { 3735 name := "testbuildfromofficial" 3736 fromNames := []string{ 3737 "busybox", 3738 "docker.io/busybox", 3739 "index.docker.io/busybox", 3740 "library/busybox", 3741 "docker.io/library/busybox", 3742 "index.docker.io/library/busybox", 3743 } 3744 for idx, fromName := range fromNames { 3745 imgName := fmt.Sprintf("%s%d", name, idx) 3746 buildImageSuccessfully(c, imgName, withDockerfile("FROM "+fromName)) 3747 dockerCmd(c, "rmi", imgName) 3748 } 3749 } 3750 3751 func (s *DockerSuite) TestBuildDockerfileOutsideContext(c *check.C) { 3752 testRequires(c, UnixCli, DaemonIsLinux) // uses os.Symlink: not implemented in windows at the time of writing (go-1.4.2) 3753 3754 name := "testbuilddockerfileoutsidecontext" 3755 tmpdir, err := ioutil.TempDir("", name) 3756 c.Assert(err, check.IsNil) 3757 defer os.RemoveAll(tmpdir) 3758 ctx := filepath.Join(tmpdir, "context") 3759 if err := os.MkdirAll(ctx, 0755); err != nil { 3760 c.Fatal(err) 3761 } 3762 if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte("FROM scratch\nENV X Y"), 0644); err != nil { 3763 c.Fatal(err) 3764 } 3765 wd, err := os.Getwd() 3766 if err != nil { 3767 c.Fatal(err) 3768 } 3769 defer os.Chdir(wd) 3770 if err := os.Chdir(ctx); err != nil { 3771 c.Fatal(err) 3772 } 3773 if err := ioutil.WriteFile(filepath.Join(tmpdir, "outsideDockerfile"), []byte("FROM scratch\nENV x y"), 0644); err != nil { 3774 c.Fatal(err) 3775 } 3776 if err := os.Symlink(filepath.Join("..", "outsideDockerfile"), filepath.Join(ctx, "dockerfile1")); err != nil { 3777 c.Fatal(err) 3778 } 3779 if err := os.Symlink(filepath.Join(tmpdir, "outsideDockerfile"), filepath.Join(ctx, "dockerfile2")); err != nil { 3780 c.Fatal(err) 3781 } 3782 3783 for _, dockerfilePath := range []string{ 3784 filepath.Join("..", "outsideDockerfile"), 3785 filepath.Join(ctx, "dockerfile1"), 3786 filepath.Join(ctx, "dockerfile2"), 3787 } { 3788 result := dockerCmdWithResult("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".") 3789 c.Assert(result, icmd.Matches, icmd.Expected{ 3790 Err: "must be within the build context", 3791 ExitCode: 1, 3792 }) 3793 deleteImages(name) 3794 } 3795 3796 os.Chdir(tmpdir) 3797 3798 // Path to Dockerfile should be resolved relative to working directory, not relative to context. 3799 // There is a Dockerfile in the context, but since there is no Dockerfile in the current directory, the following should fail 3800 out, _, err := dockerCmdWithError("build", "-t", name, "--no-cache", "-f", "Dockerfile", ctx) 3801 if err == nil { 3802 c.Fatalf("Expected error. Out: %s", out) 3803 } 3804 } 3805 3806 // FIXME(vdemeester) should be a unit test 3807 func (s *DockerSuite) TestBuildSpaces(c *check.C) { 3808 // Test to make sure that leading/trailing spaces on a command 3809 // doesn't change the error msg we get 3810 name := "testspaces" 3811 ctx := fakeContext(c, "FROM busybox\nCOPY\n", 3812 map[string]string{ 3813 "Dockerfile": "FROM busybox\nCOPY\n", 3814 }) 3815 defer ctx.Close() 3816 3817 result1 := buildImage(name, withExternalBuildContext(ctx)) 3818 result1.Assert(c, icmd.Expected{ 3819 ExitCode: 1, 3820 }) 3821 3822 ctx.Add("Dockerfile", "FROM busybox\nCOPY ") 3823 result2 := buildImage(name, withExternalBuildContext(ctx)) 3824 result2.Assert(c, icmd.Expected{ 3825 ExitCode: 1, 3826 }) 3827 3828 removeLogTimestamps := func(s string) string { 3829 return regexp.MustCompile(`time="(.*?)"`).ReplaceAllString(s, `time=[TIMESTAMP]`) 3830 } 3831 3832 // Skip over the times 3833 e1 := removeLogTimestamps(result1.Error.Error()) 3834 e2 := removeLogTimestamps(result2.Error.Error()) 3835 3836 // Ignore whitespace since that's what were verifying doesn't change stuff 3837 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3838 c.Fatalf("Build 2's error wasn't the same as build 1's\n1:%s\n2:%s", result1.Error, result2.Error) 3839 } 3840 3841 ctx.Add("Dockerfile", "FROM busybox\n COPY") 3842 result2 = buildImage(name, withoutCache, withExternalBuildContext(ctx)) 3843 result2.Assert(c, icmd.Expected{ 3844 ExitCode: 1, 3845 }) 3846 3847 // Skip over the times 3848 e1 = removeLogTimestamps(result1.Error.Error()) 3849 e2 = removeLogTimestamps(result2.Error.Error()) 3850 3851 // Ignore whitespace since that's what were verifying doesn't change stuff 3852 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3853 c.Fatalf("Build 3's error wasn't the same as build 1's\n1:%s\n3:%s", result1.Error, result2.Error) 3854 } 3855 3856 ctx.Add("Dockerfile", "FROM busybox\n COPY ") 3857 result2 = buildImage(name, withoutCache, withExternalBuildContext(ctx)) 3858 result2.Assert(c, icmd.Expected{ 3859 ExitCode: 1, 3860 }) 3861 3862 // Skip over the times 3863 e1 = removeLogTimestamps(result1.Error.Error()) 3864 e2 = removeLogTimestamps(result2.Error.Error()) 3865 3866 // Ignore whitespace since that's what were verifying doesn't change stuff 3867 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3868 c.Fatalf("Build 4's error wasn't the same as build 1's\n1:%s\n4:%s", result1.Error, result2.Error) 3869 } 3870 3871 } 3872 3873 func (s *DockerSuite) TestBuildSpacesWithQuotes(c *check.C) { 3874 // Test to make sure that spaces in quotes aren't lost 3875 name := "testspacesquotes" 3876 3877 dockerfile := `FROM busybox 3878 RUN echo " \ 3879 foo "` 3880 3881 expected := "\n foo \n" 3882 // Windows uses the builtin echo, which preserves quotes 3883 if testEnv.DaemonPlatform() == "windows" { 3884 expected = "\" foo \"" 3885 } 3886 3887 buildImage(name, withDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3888 Out: expected, 3889 }) 3890 } 3891 3892 // #4393 3893 func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *check.C) { 3894 testRequires(c, DaemonIsLinux) // TODO Windows: This should error out 3895 buildImage("docker-test-errcreatevolumewithfile", withDockerfile(` 3896 FROM busybox 3897 RUN touch /foo 3898 VOLUME /foo 3899 `)).Assert(c, icmd.Expected{ 3900 ExitCode: 1, 3901 Err: "file exists", 3902 }) 3903 } 3904 3905 // FIXME(vdemeester) should be a unit test 3906 func (s *DockerSuite) TestBuildMissingArgs(c *check.C) { 3907 // Test to make sure that all Dockerfile commands (except the ones listed 3908 // in skipCmds) will generate an error if no args are provided. 3909 // Note: INSERT is deprecated so we exclude it because of that. 3910 skipCmds := map[string]struct{}{ 3911 "CMD": {}, 3912 "RUN": {}, 3913 "ENTRYPOINT": {}, 3914 "INSERT": {}, 3915 } 3916 3917 if testEnv.DaemonPlatform() == "windows" { 3918 skipCmds = map[string]struct{}{ 3919 "CMD": {}, 3920 "RUN": {}, 3921 "ENTRYPOINT": {}, 3922 "INSERT": {}, 3923 "STOPSIGNAL": {}, 3924 "ARG": {}, 3925 "USER": {}, 3926 "EXPOSE": {}, 3927 } 3928 } 3929 3930 for cmd := range command.Commands { 3931 cmd = strings.ToUpper(cmd) 3932 if _, ok := skipCmds[cmd]; ok { 3933 continue 3934 } 3935 var dockerfile string 3936 if cmd == "FROM" { 3937 dockerfile = cmd 3938 } else { 3939 // Add FROM to make sure we don't complain about it missing 3940 dockerfile = "FROM busybox\n" + cmd 3941 } 3942 3943 buildImage("args", withDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3944 ExitCode: 1, 3945 Err: cmd + " requires", 3946 }) 3947 } 3948 3949 } 3950 3951 func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) { 3952 testRequires(c, DaemonIsLinux) 3953 buildImage("sc", withDockerfile("FROM scratch")).Assert(c, icmd.Expected{ 3954 ExitCode: 1, 3955 Err: "No image was generated", 3956 }) 3957 } 3958 3959 func (s *DockerSuite) TestBuildDotDotFile(c *check.C) { 3960 buildImageSuccessfully(c, "sc", withBuildContext(c, 3961 withFile("Dockerfile", "FROM busybox\n"), 3962 withFile("..gitme", ""), 3963 )) 3964 } 3965 3966 func (s *DockerSuite) TestBuildRUNoneJSON(c *check.C) { 3967 testRequires(c, DaemonIsLinux) // No hello-world Windows image 3968 name := "testbuildrunonejson" 3969 3970 buildImage(name, withDockerfile(`FROM hello-world:frozen 3971 RUN [ "/hello" ]`)).Assert(c, icmd.Expected{ 3972 Out: "Hello from Docker", 3973 }) 3974 } 3975 3976 func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { 3977 name := "testbuildemptystringvolume" 3978 3979 buildImage(name, withDockerfile(` 3980 FROM busybox 3981 ENV foo="" 3982 VOLUME $foo 3983 `)).Assert(c, icmd.Expected{ 3984 ExitCode: 1, 3985 }) 3986 } 3987 3988 func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *check.C) { 3989 testRequires(c, SameHostDaemon, DaemonIsLinux) 3990 3991 cgroupParent := "test" 3992 data, err := ioutil.ReadFile("/proc/self/cgroup") 3993 if err != nil { 3994 c.Fatalf("failed to read '/proc/self/cgroup - %v", err) 3995 } 3996 selfCgroupPaths := testutil.ParseCgroupPaths(string(data)) 3997 _, found := selfCgroupPaths["memory"] 3998 if !found { 3999 c.Fatalf("unable to find self memory cgroup path. CgroupsPath: %v", selfCgroupPaths) 4000 } 4001 result := buildImage("buildcgroupparent", 4002 withBuildFlags("--cgroup-parent", cgroupParent), 4003 withDockerfile(` 4004 FROM busybox 4005 RUN cat /proc/self/cgroup 4006 `)) 4007 result.Assert(c, icmd.Success) 4008 m, err := regexp.MatchString(fmt.Sprintf("memory:.*/%s/.*", cgroupParent), result.Combined()) 4009 c.Assert(err, check.IsNil) 4010 if !m { 4011 c.Fatalf("There is no expected memory cgroup with parent /%s/: %s", cgroupParent, result.Combined()) 4012 } 4013 } 4014 4015 // FIXME(vdemeester) could be a unit test 4016 func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) { 4017 // Check to make sure our build output prints the Dockerfile cmd 4018 // property - there was a bug that caused it to be duplicated on the 4019 // Step X line 4020 name := "testbuildnodupoutput" 4021 result := buildImage(name, withDockerfile(` 4022 FROM busybox 4023 RUN env`)) 4024 result.Assert(c, icmd.Success) 4025 exp := "\nStep 2/2 : RUN env\n" 4026 if !strings.Contains(result.Combined(), exp) { 4027 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4028 } 4029 } 4030 4031 // GH15826 4032 // FIXME(vdemeester) could be a unit test 4033 func (s *DockerSuite) TestBuildStartsFromOne(c *check.C) { 4034 // Explicit check to ensure that build starts from step 1 rather than 0 4035 name := "testbuildstartsfromone" 4036 result := buildImage(name, withDockerfile(`FROM busybox`)) 4037 result.Assert(c, icmd.Success) 4038 exp := "\nStep 1/1 : FROM busybox\n" 4039 if !strings.Contains(result.Combined(), exp) { 4040 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4041 } 4042 } 4043 4044 func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) { 4045 // Test to make sure the bad command is quoted with just "s and 4046 // not as a Go []string 4047 name := "testbuildbadrunerrmsg" 4048 shell := "/bin/sh -c" 4049 exitCode := 127 4050 if testEnv.DaemonPlatform() == "windows" { 4051 shell = "cmd /S /C" 4052 // architectural - Windows has to start the container to determine the exe is bad, Linux does not 4053 exitCode = 1 4054 } 4055 exp := fmt.Sprintf(`The command '%s badEXE a1 \& a2 a3' returned a non-zero code: %d`, shell, exitCode) 4056 4057 buildImage(name, withDockerfile(` 4058 FROM busybox 4059 RUN badEXE a1 \& a2 a3`)).Assert(c, icmd.Expected{ 4060 ExitCode: exitCode, 4061 Err: exp, 4062 }) 4063 } 4064 4065 func (s *DockerTrustSuite) TestTrustedBuild(c *check.C) { 4066 repoName := s.setupTrustedImage(c, "trusted-build") 4067 dockerFile := fmt.Sprintf(` 4068 FROM %s 4069 RUN [] 4070 `, repoName) 4071 4072 name := "testtrustedbuild" 4073 4074 buildImage(name, trustedBuild, withDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4075 Out: fmt.Sprintf("FROM %s@sha", repoName[:len(repoName)-7]), 4076 }) 4077 4078 // We should also have a tag reference for the image. 4079 dockerCmd(c, "inspect", repoName) 4080 4081 // We should now be able to remove the tag reference. 4082 dockerCmd(c, "rmi", repoName) 4083 } 4084 4085 func (s *DockerTrustSuite) TestTrustedBuildUntrustedTag(c *check.C) { 4086 repoName := fmt.Sprintf("%v/dockercli/build-untrusted-tag:latest", privateRegistryURL) 4087 dockerFile := fmt.Sprintf(` 4088 FROM %s 4089 RUN [] 4090 `, repoName) 4091 4092 name := "testtrustedbuilduntrustedtag" 4093 4094 buildImage(name, trustedBuild, withDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4095 ExitCode: 1, 4096 Err: "does not have trust data for", 4097 }) 4098 } 4099 4100 func (s *DockerTrustSuite) TestBuildContextDirIsSymlink(c *check.C) { 4101 testRequires(c, DaemonIsLinux) 4102 tempDir, err := ioutil.TempDir("", "test-build-dir-is-symlink-") 4103 c.Assert(err, check.IsNil) 4104 defer os.RemoveAll(tempDir) 4105 4106 // Make a real context directory in this temp directory with a simple 4107 // Dockerfile. 4108 realContextDirname := filepath.Join(tempDir, "context") 4109 if err := os.Mkdir(realContextDirname, os.FileMode(0755)); err != nil { 4110 c.Fatal(err) 4111 } 4112 4113 if err = ioutil.WriteFile( 4114 filepath.Join(realContextDirname, "Dockerfile"), 4115 []byte(` 4116 FROM busybox 4117 RUN echo hello world 4118 `), 4119 os.FileMode(0644), 4120 ); err != nil { 4121 c.Fatal(err) 4122 } 4123 4124 // Make a symlink to the real context directory. 4125 contextSymlinkName := filepath.Join(tempDir, "context_link") 4126 if err := os.Symlink(realContextDirname, contextSymlinkName); err != nil { 4127 c.Fatal(err) 4128 } 4129 4130 // Executing the build with the symlink as the specified context should 4131 // *not* fail. 4132 dockerCmd(c, "build", contextSymlinkName) 4133 } 4134 4135 func (s *DockerTrustSuite) TestTrustedBuildTagFromReleasesRole(c *check.C) { 4136 testRequires(c, NotaryHosting) 4137 4138 latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") 4139 repoName := strings.TrimSuffix(latestTag, ":latest") 4140 4141 // Now create the releases role 4142 s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) 4143 s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) 4144 s.notaryPublish(c, repoName) 4145 4146 // push a different tag to the releases role 4147 otherTag := fmt.Sprintf("%s:other", repoName) 4148 dockerCmd(c, "tag", "busybox", otherTag) 4149 4150 icmd.RunCmd(icmd.Command(dockerBinary, "push", otherTag), trustedCmd).Assert(c, icmd.Success) 4151 s.assertTargetInRoles(c, repoName, "other", "targets/releases") 4152 s.assertTargetNotInRoles(c, repoName, "other", "targets") 4153 4154 out, status := dockerCmd(c, "rmi", otherTag) 4155 c.Assert(status, check.Equals, 0, check.Commentf("docker rmi failed: %s", out)) 4156 4157 dockerFile := fmt.Sprintf(` 4158 FROM %s 4159 RUN [] 4160 `, otherTag) 4161 name := "testtrustedbuildreleasesrole" 4162 buildImage(name, trustedBuild, withDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4163 Out: fmt.Sprintf("FROM %s@sha", repoName), 4164 }) 4165 } 4166 4167 func (s *DockerTrustSuite) TestTrustedBuildTagIgnoresOtherDelegationRoles(c *check.C) { 4168 testRequires(c, NotaryHosting) 4169 4170 latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") 4171 repoName := strings.TrimSuffix(latestTag, ":latest") 4172 4173 // Now create a non-releases delegation role 4174 s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) 4175 s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) 4176 s.notaryPublish(c, repoName) 4177 4178 // push a different tag to the other role 4179 otherTag := fmt.Sprintf("%s:other", repoName) 4180 dockerCmd(c, "tag", "busybox", otherTag) 4181 4182 icmd.RunCmd(icmd.Command(dockerBinary, "push", otherTag), trustedCmd).Assert(c, icmd.Success) 4183 s.assertTargetInRoles(c, repoName, "other", "targets/other") 4184 s.assertTargetNotInRoles(c, repoName, "other", "targets") 4185 4186 out, status := dockerCmd(c, "rmi", otherTag) 4187 c.Assert(status, check.Equals, 0, check.Commentf("docker rmi failed: %s", out)) 4188 4189 dockerFile := fmt.Sprintf(` 4190 FROM %s 4191 RUN [] 4192 `, otherTag) 4193 4194 name := "testtrustedbuildotherrole" 4195 buildImage(name, trustedBuild, withDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4196 ExitCode: 1, 4197 }) 4198 } 4199 4200 // Issue #15634: COPY fails when path starts with "null" 4201 func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) { 4202 name := "testbuildnullstringinaddcopyvolume" 4203 volName := "nullvolume" 4204 if testEnv.DaemonPlatform() == "windows" { 4205 volName = `C:\\nullvolume` 4206 } 4207 4208 buildImageSuccessfully(c, name, withBuildContext(c, 4209 withFile("Dockerfile", ` 4210 FROM busybox 4211 4212 ADD null / 4213 COPY nullfile / 4214 VOLUME `+volName+` 4215 `), 4216 withFile("null", "test1"), 4217 withFile("nullfile", "test2"), 4218 )) 4219 } 4220 4221 func (s *DockerSuite) TestBuildStopSignal(c *check.C) { 4222 testRequires(c, DaemonIsLinux) // Windows does not support STOPSIGNAL yet 4223 imgName := "test_build_stop_signal" 4224 buildImageSuccessfully(c, imgName, withDockerfile(`FROM busybox 4225 STOPSIGNAL SIGKILL`)) 4226 res := inspectFieldJSON(c, imgName, "Config.StopSignal") 4227 if res != `"SIGKILL"` { 4228 c.Fatalf("Signal %s, expected SIGKILL", res) 4229 } 4230 4231 containerName := "test-container-stop-signal" 4232 dockerCmd(c, "run", "-d", "--name", containerName, imgName, "top") 4233 res = inspectFieldJSON(c, containerName, "Config.StopSignal") 4234 if res != `"SIGKILL"` { 4235 c.Fatalf("Signal %s, expected SIGKILL", res) 4236 } 4237 } 4238 4239 func (s *DockerSuite) TestBuildBuildTimeArg(c *check.C) { 4240 imgName := "bldargtest" 4241 envKey := "foo" 4242 envVal := "bar" 4243 var dockerfile string 4244 if testEnv.DaemonPlatform() == "windows" { 4245 // Bugs in Windows busybox port - use the default base image and native cmd stuff 4246 dockerfile = fmt.Sprintf(`FROM `+minimalBaseImage()+` 4247 ARG %s 4248 RUN echo %%%s%% 4249 CMD setlocal enableextensions && if defined %s (echo %%%s%%)`, envKey, envKey, envKey, envKey) 4250 } else { 4251 dockerfile = fmt.Sprintf(`FROM busybox 4252 ARG %s 4253 RUN echo $%s 4254 CMD echo $%s`, envKey, envKey, envKey) 4255 4256 } 4257 buildImage(imgName, 4258 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4259 withDockerfile(dockerfile), 4260 ).Assert(c, icmd.Expected{ 4261 Out: envVal, 4262 }) 4263 4264 containerName := "bldargCont" 4265 out, _ := dockerCmd(c, "run", "--name", containerName, imgName) 4266 out = strings.Trim(out, " \r\n'") 4267 if out != "" { 4268 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4269 } 4270 } 4271 4272 func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) { 4273 imgName := "bldargtest" 4274 envKey := "foo" 4275 envVal := "bar" 4276 envDef := "bar1" 4277 dockerfile := fmt.Sprintf(`FROM busybox 4278 ARG %s=%s`, envKey, envDef) 4279 buildImage(imgName, 4280 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4281 withDockerfile(dockerfile), 4282 ).Assert(c, icmd.Expected{ 4283 Out: envVal, 4284 }) 4285 4286 out, _ := dockerCmd(c, "history", "--no-trunc", imgName) 4287 outputTabs := strings.Split(out, "\n")[1] 4288 if !strings.Contains(outputTabs, envDef) { 4289 c.Fatalf("failed to find arg default in image history output: %q expected: %q", outputTabs, envDef) 4290 } 4291 } 4292 4293 func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) { 4294 imgName := "bldargtest" 4295 envKey := "foo" 4296 envVal := "bar" 4297 dockerfile := fmt.Sprintf(`FROM busybox 4298 ARG %s 4299 RUN echo $%s`, envKey, envKey) 4300 buildImageSuccessfully(c, imgName, 4301 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4302 withDockerfile(dockerfile), 4303 ) 4304 origImgID := getIDByName(c, imgName) 4305 4306 imgNameCache := "bldargtestcachehit" 4307 buildImageSuccessfully(c, imgNameCache, 4308 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4309 withDockerfile(dockerfile), 4310 ) 4311 newImgID := getIDByName(c, imgName) 4312 if newImgID != origImgID { 4313 c.Fatalf("build didn't use cache! expected image id: %q built image id: %q", origImgID, newImgID) 4314 } 4315 } 4316 4317 func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *check.C) { 4318 imgName := "bldargtest" 4319 envKey := "foo" 4320 envVal := "bar" 4321 extraEnvKey := "foo1" 4322 extraEnvVal := "bar1" 4323 dockerfile := fmt.Sprintf(`FROM busybox 4324 ARG %s 4325 ARG %s 4326 RUN echo $%s`, envKey, extraEnvKey, envKey) 4327 buildImageSuccessfully(c, imgName, 4328 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4329 withDockerfile(dockerfile), 4330 ) 4331 origImgID := getIDByName(c, imgName) 4332 4333 imgNameCache := "bldargtestcachemiss" 4334 buildImageSuccessfully(c, imgNameCache, 4335 withBuildFlags( 4336 "--build-arg", fmt.Sprintf("%s=%s", envKey, envVal), 4337 "--build-arg", fmt.Sprintf("%s=%s", extraEnvKey, extraEnvVal), 4338 ), 4339 withDockerfile(dockerfile), 4340 ) 4341 newImgID := getIDByName(c, imgNameCache) 4342 4343 if newImgID == origImgID { 4344 c.Fatalf("build used cache, expected a miss!") 4345 } 4346 } 4347 4348 func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *check.C) { 4349 imgName := "bldargtest" 4350 envKey := "foo" 4351 envVal := "bar" 4352 newEnvVal := "bar1" 4353 dockerfile := fmt.Sprintf(`FROM busybox 4354 ARG %s 4355 RUN echo $%s`, envKey, envKey) 4356 buildImageSuccessfully(c, imgName, 4357 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4358 withDockerfile(dockerfile), 4359 ) 4360 origImgID := getIDByName(c, imgName) 4361 4362 imgNameCache := "bldargtestcachemiss" 4363 buildImageSuccessfully(c, imgNameCache, 4364 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, newEnvVal)), 4365 withDockerfile(dockerfile), 4366 ) 4367 newImgID := getIDByName(c, imgNameCache) 4368 if newImgID == origImgID { 4369 c.Fatalf("build used cache, expected a miss!") 4370 } 4371 } 4372 4373 func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *check.C) { 4374 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4375 imgName := "bldargtest" 4376 envKey := "foo" 4377 envVal := "bar" 4378 envValOveride := "barOverride" 4379 dockerfile := fmt.Sprintf(`FROM busybox 4380 ARG %s 4381 ENV %s %s 4382 RUN echo $%s 4383 CMD echo $%s 4384 `, envKey, envKey, envValOveride, envKey, envKey) 4385 4386 result := buildImage(imgName, 4387 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4388 withDockerfile(dockerfile), 4389 ) 4390 result.Assert(c, icmd.Success) 4391 if strings.Count(result.Combined(), envValOveride) != 2 { 4392 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOveride) 4393 } 4394 4395 containerName := "bldargCont" 4396 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOveride) { 4397 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOveride) 4398 } 4399 } 4400 4401 // FIXME(vdemeester) might be useful to merge with the one above ? 4402 func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *check.C) { 4403 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4404 imgName := "bldargtest" 4405 envKey := "foo" 4406 envVal := "bar" 4407 envValOveride := "barOverride" 4408 dockerfile := fmt.Sprintf(`FROM busybox 4409 ENV %s %s 4410 ARG %s 4411 RUN echo $%s 4412 CMD echo $%s 4413 `, envKey, envValOveride, envKey, envKey, envKey) 4414 result := buildImage(imgName, 4415 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4416 withDockerfile(dockerfile), 4417 ) 4418 result.Assert(c, icmd.Success) 4419 if strings.Count(result.Combined(), envValOveride) != 2 { 4420 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOveride) 4421 } 4422 4423 containerName := "bldargCont" 4424 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOveride) { 4425 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOveride) 4426 } 4427 } 4428 4429 func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) { 4430 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4431 imgName := "bldvarstest" 4432 4433 wdVar := "WDIR" 4434 wdVal := "/tmp/" 4435 addVar := "AFILE" 4436 addVal := "addFile" 4437 copyVar := "CFILE" 4438 copyVal := "copyFile" 4439 envVar := "foo" 4440 envVal := "bar" 4441 exposeVar := "EPORT" 4442 exposeVal := "9999" 4443 userVar := "USER" 4444 userVal := "testUser" 4445 volVar := "VOL" 4446 volVal := "/testVol/" 4447 4448 buildImageSuccessfully(c, imgName, 4449 withBuildFlags( 4450 "--build-arg", fmt.Sprintf("%s=%s", wdVar, wdVal), 4451 "--build-arg", fmt.Sprintf("%s=%s", addVar, addVal), 4452 "--build-arg", fmt.Sprintf("%s=%s", copyVar, copyVal), 4453 "--build-arg", fmt.Sprintf("%s=%s", envVar, envVal), 4454 "--build-arg", fmt.Sprintf("%s=%s", exposeVar, exposeVal), 4455 "--build-arg", fmt.Sprintf("%s=%s", userVar, userVal), 4456 "--build-arg", fmt.Sprintf("%s=%s", volVar, volVal), 4457 ), 4458 withBuildContext(c, 4459 withFile("Dockerfile", fmt.Sprintf(`FROM busybox 4460 ARG %s 4461 WORKDIR ${%s} 4462 ARG %s 4463 ADD ${%s} testDir/ 4464 ARG %s 4465 COPY $%s testDir/ 4466 ARG %s 4467 ENV %s=${%s} 4468 ARG %s 4469 EXPOSE $%s 4470 ARG %s 4471 USER $%s 4472 ARG %s 4473 VOLUME ${%s}`, 4474 wdVar, wdVar, addVar, addVar, copyVar, copyVar, envVar, envVar, 4475 envVar, exposeVar, exposeVar, userVar, userVar, volVar, volVar)), 4476 withFile(addVal, "some stuff"), 4477 withFile(copyVal, "some stuff"), 4478 ), 4479 ) 4480 4481 res := inspectField(c, imgName, "Config.WorkingDir") 4482 if res != filepath.ToSlash(filepath.Clean(wdVal)) { 4483 c.Fatalf("Config.WorkingDir value mismatch. Expected: %s, got: %s", filepath.ToSlash(filepath.Clean(wdVal)), res) 4484 } 4485 4486 var resArr []string 4487 inspectFieldAndUnmarshall(c, imgName, "Config.Env", &resArr) 4488 4489 found := false 4490 for _, v := range resArr { 4491 if fmt.Sprintf("%s=%s", envVar, envVal) == v { 4492 found = true 4493 break 4494 } 4495 } 4496 if !found { 4497 c.Fatalf("Config.Env value mismatch. Expected <key=value> to exist: %s=%s, got: %v", 4498 envVar, envVal, resArr) 4499 } 4500 4501 var resMap map[string]interface{} 4502 inspectFieldAndUnmarshall(c, imgName, "Config.ExposedPorts", &resMap) 4503 if _, ok := resMap[fmt.Sprintf("%s/tcp", exposeVal)]; !ok { 4504 c.Fatalf("Config.ExposedPorts value mismatch. Expected exposed port: %s/tcp, got: %v", exposeVal, resMap) 4505 } 4506 4507 res = inspectField(c, imgName, "Config.User") 4508 if res != userVal { 4509 c.Fatalf("Config.User value mismatch. Expected: %s, got: %s", userVal, res) 4510 } 4511 4512 inspectFieldAndUnmarshall(c, imgName, "Config.Volumes", &resMap) 4513 if _, ok := resMap[volVal]; !ok { 4514 c.Fatalf("Config.Volumes value mismatch. Expected volume: %s, got: %v", volVal, resMap) 4515 } 4516 } 4517 4518 func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *check.C) { 4519 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4520 imgName := "bldvarstest" 4521 envKey := "foo" 4522 envVal := "bar" 4523 envKey1 := "foo1" 4524 envValOveride := "barOverride" 4525 dockerfile := fmt.Sprintf(`FROM busybox 4526 ARG %s 4527 ENV %s %s 4528 ENV %s ${%s} 4529 RUN echo $%s 4530 CMD echo $%s`, envKey, envKey, envValOveride, envKey1, envKey, envKey1, envKey1) 4531 result := buildImage(imgName, 4532 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4533 withDockerfile(dockerfile), 4534 ) 4535 result.Assert(c, icmd.Success) 4536 if strings.Count(result.Combined(), envValOveride) != 2 { 4537 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOveride) 4538 } 4539 4540 containerName := "bldargCont" 4541 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOveride) { 4542 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOveride) 4543 } 4544 } 4545 4546 func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *check.C) { 4547 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4548 imgName := "bldargtest" 4549 envKey := "foo" 4550 envVal := "bar" 4551 dockerfile := fmt.Sprintf(`FROM busybox 4552 RUN echo $%s 4553 ARG %s 4554 CMD echo $%s`, envKey, envKey, envKey) 4555 result := buildImage(imgName, 4556 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4557 withDockerfile(dockerfile), 4558 ) 4559 result.Assert(c, icmd.Success) 4560 if strings.Contains(result.Combined(), envVal) { 4561 c.Fatalf("able to access environment variable in output: %q expected to be missing", result.Combined()) 4562 } 4563 4564 containerName := "bldargCont" 4565 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4566 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4567 } 4568 } 4569 4570 func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *check.C) { 4571 testRequires(c, DaemonIsLinux) // Windows does not support --build-arg 4572 imgName := "bldargtest" 4573 envKey := "HTTP_PROXY" 4574 envVal := "bar" 4575 dockerfile := fmt.Sprintf(`FROM busybox 4576 RUN echo $%s 4577 CMD echo $%s`, envKey, envKey) 4578 4579 result := buildImage(imgName, 4580 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4581 withDockerfile(dockerfile), 4582 ) 4583 result.Assert(c, icmd.Success) 4584 if !strings.Contains(result.Combined(), envVal) { 4585 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envVal) 4586 } 4587 containerName := "bldargCont" 4588 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4589 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4590 } 4591 } 4592 4593 func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *check.C) { 4594 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4595 imgName := "bldargtest" 4596 envKey := "foo" 4597 envVal := "bar" 4598 envValOveride := "barOverride" 4599 dockerfile := fmt.Sprintf(`FROM busybox 4600 ARG %s=%s 4601 ENV %s $%s 4602 RUN echo $%s 4603 CMD echo $%s`, envKey, envVal, envKey, envKey, envKey, envKey) 4604 result := buildImage(imgName, 4605 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envValOveride)), 4606 withDockerfile(dockerfile), 4607 ) 4608 result.Assert(c, icmd.Success) 4609 if strings.Count(result.Combined(), envValOveride) != 1 { 4610 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOveride) 4611 } 4612 4613 containerName := "bldargCont" 4614 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOveride) { 4615 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOveride) 4616 } 4617 } 4618 4619 func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *check.C) { 4620 imgName := "bldargtest" 4621 envKey := "foo" 4622 envVal := "bar" 4623 dockerfile := fmt.Sprintf(`FROM busybox 4624 RUN echo $%s 4625 CMD echo $%s`, envKey, envKey) 4626 warnStr := "[Warning] One or more build-args" 4627 buildImage(imgName, 4628 withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4629 withDockerfile(dockerfile), 4630 ).Assert(c, icmd.Expected{ 4631 Out: warnStr, 4632 }) 4633 } 4634 4635 func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) { 4636 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4637 dockerfile := `FROM busybox 4638 ARG FOO1=fromfile 4639 ARG FOO2=fromfile 4640 ARG FOO3=fromfile 4641 ARG FOO4=fromfile 4642 ARG FOO5 4643 ARG FOO6 4644 RUN env 4645 RUN [ "$FOO1" == "fromcmd" ] 4646 RUN [ "$FOO2" == "" ] 4647 RUN [ "$FOO3" == "fromenv" ] 4648 RUN [ "$FOO4" == "fromfile" ] 4649 RUN [ "$FOO5" == "fromcmd" ] 4650 # The following should not exist at all in the env 4651 RUN [ "$(env | grep FOO6)" == "" ] 4652 RUN [ "$(env | grep FOO7)" == "" ] 4653 RUN [ "$(env | grep FOO8)" == "" ] 4654 RUN [ "$(env | grep FOO9)" == "" ] 4655 ` 4656 result := buildImage("testbuildtimeargenv", 4657 withBuildFlags( 4658 "--build-arg", fmt.Sprintf("FOO1=fromcmd"), 4659 "--build-arg", fmt.Sprintf("FOO2="), 4660 "--build-arg", fmt.Sprintf("FOO3"), // set in env 4661 "--build-arg", fmt.Sprintf("FOO4"), // not set in env 4662 "--build-arg", fmt.Sprintf("FOO5=fromcmd"), 4663 // FOO6 is not set at all 4664 "--build-arg", fmt.Sprintf("FOO7=fromcmd"), // should produce a warning 4665 "--build-arg", fmt.Sprintf("FOO8="), // should produce a warning 4666 "--build-arg", fmt.Sprintf("FOO9"), // should produce a warning 4667 ), 4668 withEnvironmentVariales(append(os.Environ(), 4669 "FOO1=fromenv", 4670 "FOO2=fromenv", 4671 "FOO3=fromenv")...), 4672 withBuildContext(c, 4673 withFile("Dockerfile", dockerfile), 4674 ), 4675 ) 4676 result.Assert(c, icmd.Success) 4677 4678 // Now check to make sure we got a warning msg about unused build-args 4679 i := strings.Index(result.Combined(), "[Warning]") 4680 if i < 0 { 4681 c.Fatalf("Missing the build-arg warning in %q", result.Combined()) 4682 } 4683 4684 out := result.Combined()[i:] // "out" should contain just the warning message now 4685 4686 // These were specified on a --build-arg but no ARG was in the Dockerfile 4687 c.Assert(out, checker.Contains, "FOO7") 4688 c.Assert(out, checker.Contains, "FOO8") 4689 c.Assert(out, checker.Contains, "FOO9") 4690 } 4691 4692 func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *check.C) { 4693 imgName := "bldargtest" 4694 envKey := "foo" 4695 envKey1 := "foo1" 4696 envKey2 := "foo2" 4697 envKey3 := "foo3" 4698 dockerfile := fmt.Sprintf(`FROM busybox 4699 ARG %s="" 4700 ARG %s='' 4701 ARG %s="''" 4702 ARG %s='""' 4703 RUN [ "$%s" != "$%s" ] 4704 RUN [ "$%s" != "$%s" ] 4705 RUN [ "$%s" != "$%s" ] 4706 RUN [ "$%s" != "$%s" ] 4707 RUN [ "$%s" != "$%s" ]`, envKey, envKey1, envKey2, envKey3, 4708 envKey, envKey2, envKey, envKey3, envKey1, envKey2, envKey1, envKey3, 4709 envKey2, envKey3) 4710 buildImageSuccessfully(c, imgName, withDockerfile(dockerfile)) 4711 } 4712 4713 func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) { 4714 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4715 imgName := "bldargtest" 4716 envKey := "foo" 4717 envKey1 := "foo1" 4718 envKey2 := "foo2" 4719 dockerfile := fmt.Sprintf(`FROM busybox 4720 ARG %s= 4721 ARG %s="" 4722 ARG %s='' 4723 RUN [ "$%s" == "$%s" ] 4724 RUN [ "$%s" == "$%s" ] 4725 RUN [ "$%s" == "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2) 4726 buildImageSuccessfully(c, imgName, withDockerfile(dockerfile)) 4727 } 4728 4729 func (s *DockerSuite) TestBuildBuildTimeArgDefintionWithNoEnvInjection(c *check.C) { 4730 imgName := "bldargtest" 4731 envKey := "foo" 4732 dockerfile := fmt.Sprintf(`FROM busybox 4733 ARG %s 4734 RUN env`, envKey) 4735 4736 result := buildImage(imgName, withDockerfile(dockerfile)) 4737 result.Assert(c, icmd.Success) 4738 if strings.Count(result.Combined(), envKey) != 1 { 4739 c.Fatalf("unexpected number of occurrences of the arg in output: %q expected: 1", result.Combined()) 4740 } 4741 } 4742 4743 func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) { 4744 volName := "testname:/foo" 4745 4746 if testEnv.DaemonPlatform() == "windows" { 4747 volName = "testname:C:\\foo" 4748 } 4749 dockerCmd(c, "run", "-v", volName, "busybox", "sh", "-c", "touch /foo/oops") 4750 4751 dockerFile := `FROM busybox 4752 VOLUME ` + volName + ` 4753 RUN ls /foo/oops 4754 ` 4755 buildImage("test", withDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4756 ExitCode: 1, 4757 }) 4758 } 4759 4760 func (s *DockerSuite) TestBuildTagEvent(c *check.C) { 4761 since := daemonUnixTime(c) 4762 4763 dockerFile := `FROM busybox 4764 RUN echo events 4765 ` 4766 buildImageSuccessfully(c, "test", withDockerfile(dockerFile)) 4767 4768 until := daemonUnixTime(c) 4769 out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "type=image") 4770 events := strings.Split(strings.TrimSpace(out), "\n") 4771 actions := eventActionsByIDAndType(c, events, "test:latest", "image") 4772 var foundTag bool 4773 for _, a := range actions { 4774 if a == "tag" { 4775 foundTag = true 4776 break 4777 } 4778 } 4779 4780 c.Assert(foundTag, checker.True, check.Commentf("No tag event found:\n%s", out)) 4781 } 4782 4783 // #15780 4784 func (s *DockerSuite) TestBuildMultipleTags(c *check.C) { 4785 dockerfile := ` 4786 FROM busybox 4787 MAINTAINER test-15780 4788 ` 4789 buildImageSuccessfully(c, "tag1", withBuildFlags("-t", "tag2:v2", "-t", "tag1:latest", "-t", "tag1"), withDockerfile(dockerfile)) 4790 4791 id1 := getIDByName(c, "tag1") 4792 id2 := getIDByName(c, "tag2:v2") 4793 c.Assert(id1, check.Equals, id2) 4794 } 4795 4796 // #17290 4797 func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) { 4798 name := "testbuildbrokensymlink" 4799 ctx := fakeContext(c, ` 4800 FROM busybox 4801 COPY . ./`, 4802 map[string]string{ 4803 "foo": "bar", 4804 }) 4805 defer ctx.Close() 4806 4807 err := os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink")) 4808 c.Assert(err, checker.IsNil) 4809 4810 // warm up cache 4811 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 4812 4813 // add new file to context, should invalidate cache 4814 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644) 4815 c.Assert(err, checker.IsNil) 4816 4817 result := buildImage(name, withExternalBuildContext(ctx)) 4818 result.Assert(c, icmd.Success) 4819 if strings.Contains(result.Combined(), "Using cache") { 4820 c.Fatal("2nd build used cache on ADD, it shouldn't") 4821 } 4822 } 4823 4824 func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) { 4825 name := "testbuildbrokensymlink" 4826 ctx := fakeContext(c, ` 4827 FROM busybox 4828 COPY asymlink target`, 4829 map[string]string{ 4830 "foo": "bar", 4831 }) 4832 defer ctx.Close() 4833 4834 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4835 c.Assert(err, checker.IsNil) 4836 4837 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 4838 4839 out, _ := dockerCmd(c, "run", "--rm", name, "cat", "target") 4840 c.Assert(out, checker.Matches, "bar") 4841 4842 // change target file should invalidate cache 4843 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4844 c.Assert(err, checker.IsNil) 4845 4846 result := buildImage(name, withExternalBuildContext(ctx)) 4847 result.Assert(c, icmd.Success) 4848 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4849 4850 out, _ = dockerCmd(c, "run", "--rm", name, "cat", "target") 4851 c.Assert(out, checker.Matches, "baz") 4852 } 4853 4854 func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) { 4855 name := "testbuildbrokensymlink" 4856 ctx := fakeContext(c, ` 4857 FROM busybox 4858 COPY asymlink /`, 4859 map[string]string{ 4860 "foo/abc": "bar", 4861 "foo/def": "baz", 4862 }) 4863 defer ctx.Close() 4864 4865 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4866 c.Assert(err, checker.IsNil) 4867 4868 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 4869 4870 out, _ := dockerCmd(c, "run", "--rm", name, "cat", "abc", "def") 4871 c.Assert(out, checker.Matches, "barbaz") 4872 4873 // change target file should invalidate cache 4874 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644) 4875 c.Assert(err, checker.IsNil) 4876 4877 result := buildImage(name, withExternalBuildContext(ctx)) 4878 result.Assert(c, icmd.Success) 4879 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4880 4881 out, _ = dockerCmd(c, "run", "--rm", name, "cat", "abc", "def") 4882 c.Assert(out, checker.Matches, "barbax") 4883 4884 } 4885 4886 // TestBuildSymlinkBasename tests that target file gets basename from symlink, 4887 // not from the target file. 4888 func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) { 4889 name := "testbuildbrokensymlink" 4890 ctx := fakeContext(c, ` 4891 FROM busybox 4892 COPY asymlink /`, 4893 map[string]string{ 4894 "foo": "bar", 4895 }) 4896 defer ctx.Close() 4897 4898 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4899 c.Assert(err, checker.IsNil) 4900 4901 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 4902 4903 out, _ := dockerCmd(c, "run", "--rm", name, "cat", "asymlink") 4904 c.Assert(out, checker.Matches, "bar") 4905 } 4906 4907 // #17827 4908 func (s *DockerSuite) TestBuildCacheRootSource(c *check.C) { 4909 name := "testbuildrootsource" 4910 ctx := fakeContext(c, ` 4911 FROM busybox 4912 COPY / /data`, 4913 map[string]string{ 4914 "foo": "bar", 4915 }) 4916 defer ctx.Close() 4917 4918 // warm up cache 4919 buildImageSuccessfully(c, name, withExternalBuildContext(ctx)) 4920 4921 // change file, should invalidate cache 4922 err := ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4923 c.Assert(err, checker.IsNil) 4924 4925 result := buildImage(name, withExternalBuildContext(ctx)) 4926 result.Assert(c, icmd.Success) 4927 4928 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4929 } 4930 4931 // #19375 4932 func (s *DockerSuite) TestBuildFailsGitNotCallable(c *check.C) { 4933 buildImage("gitnotcallable", withEnvironmentVariales("PATH="), 4934 withBuildContextPath("github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4935 ExitCode: 1, 4936 Err: "unable to prepare context: unable to find 'git': ", 4937 }) 4938 4939 buildImage("gitnotcallable", withEnvironmentVariales("PATH="), 4940 withBuildContextPath("https://github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4941 ExitCode: 1, 4942 Err: "unable to prepare context: unable to find 'git': ", 4943 }) 4944 } 4945 4946 // TestBuildWorkdirWindowsPath tests that a Windows style path works as a workdir 4947 func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) { 4948 testRequires(c, DaemonIsWindows) 4949 name := "testbuildworkdirwindowspath" 4950 buildImageSuccessfully(c, name, withDockerfile(` 4951 FROM `+testEnv.MinimalBaseImage()+` 4952 RUN mkdir C:\\work 4953 WORKDIR C:\\work 4954 RUN if "%CD%" NEQ "C:\work" exit -1 4955 `)) 4956 } 4957 4958 func (s *DockerSuite) TestBuildLabel(c *check.C) { 4959 name := "testbuildlabel" 4960 testLabel := "foo" 4961 4962 buildImageSuccessfully(c, name, withBuildFlags("--label", testLabel), 4963 withDockerfile(` 4964 FROM `+minimalBaseImage()+` 4965 LABEL default foo 4966 `)) 4967 4968 var labels map[string]string 4969 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4970 if _, ok := labels[testLabel]; !ok { 4971 c.Fatal("label not found in image") 4972 } 4973 } 4974 4975 func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) { 4976 name := "testbuildlabel" 4977 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo=bar"), 4978 withDockerfile("FROM busybox")) 4979 4980 var labels map[string]string 4981 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4982 v, ok := labels["foo"] 4983 if !ok { 4984 c.Fatal("label `foo` not found in image") 4985 } 4986 c.Assert(v, checker.Equals, "bar") 4987 } 4988 4989 func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) { 4990 name := "testbuildlabelcachecommit" 4991 testLabel := "foo" 4992 4993 buildImageSuccessfully(c, name, withDockerfile(` 4994 FROM `+minimalBaseImage()+` 4995 LABEL default foo 4996 `)) 4997 buildImageSuccessfully(c, name, withBuildFlags("--label", testLabel), 4998 withDockerfile(` 4999 FROM `+minimalBaseImage()+` 5000 LABEL default foo 5001 `)) 5002 5003 var labels map[string]string 5004 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 5005 if _, ok := labels[testLabel]; !ok { 5006 c.Fatal("label not found in image") 5007 } 5008 } 5009 5010 func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) { 5011 name := "testbuildlabelmultiple" 5012 testLabels := map[string]string{ 5013 "foo": "bar", 5014 "123": "456", 5015 } 5016 labelArgs := []string{} 5017 for k, v := range testLabels { 5018 labelArgs = append(labelArgs, "--label", k+"="+v) 5019 } 5020 5021 buildImageSuccessfully(c, name, withBuildFlags(labelArgs...), 5022 withDockerfile(` 5023 FROM `+minimalBaseImage()+` 5024 LABEL default foo 5025 `)) 5026 5027 var labels map[string]string 5028 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 5029 for k, v := range testLabels { 5030 if x, ok := labels[k]; !ok || x != v { 5031 c.Fatalf("label %s=%s not found in image", k, v) 5032 } 5033 } 5034 } 5035 5036 func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) { 5037 dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 5038 baseImage := privateRegistryURL + "/baseimage" 5039 5040 buildImageSuccessfully(c, baseImage, withDockerfile(` 5041 FROM busybox 5042 ENV env1 val1 5043 `)) 5044 5045 dockerCmd(c, "push", baseImage) 5046 dockerCmd(c, "rmi", baseImage) 5047 5048 buildImageSuccessfully(c, baseImage, withDockerfile(fmt.Sprintf(` 5049 FROM %s 5050 ENV env2 val2 5051 `, baseImage))) 5052 } 5053 5054 func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) { 5055 osPath := os.Getenv("PATH") 5056 defer os.Setenv("PATH", osPath) 5057 5058 workingDir, err := os.Getwd() 5059 c.Assert(err, checker.IsNil) 5060 absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth")) 5061 c.Assert(err, checker.IsNil) 5062 testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute) 5063 5064 os.Setenv("PATH", testPath) 5065 5066 repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL) 5067 5068 tmp, err := ioutil.TempDir("", "integration-cli-") 5069 c.Assert(err, checker.IsNil) 5070 5071 externalAuthConfig := `{ "credsStore": "shell-test" }` 5072 5073 configPath := filepath.Join(tmp, "config.json") 5074 err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) 5075 c.Assert(err, checker.IsNil) 5076 5077 dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 5078 5079 b, err := ioutil.ReadFile(configPath) 5080 c.Assert(err, checker.IsNil) 5081 c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":") 5082 5083 dockerCmd(c, "--config", tmp, "tag", "busybox", repoName) 5084 dockerCmd(c, "--config", tmp, "push", repoName) 5085 5086 // make sure the image is pulled when building 5087 dockerCmd(c, "rmi", repoName) 5088 5089 icmd.RunCmd(icmd.Cmd{ 5090 Command: []string{dockerBinary, "--config", tmp, "build", "-"}, 5091 Stdin: strings.NewReader(fmt.Sprintf("FROM %s", repoName)), 5092 }).Assert(c, icmd.Success) 5093 } 5094 5095 // Test cases in #22036 5096 func (s *DockerSuite) TestBuildLabelsOverride(c *check.C) { 5097 // Command line option labels will always override 5098 name := "scratchy" 5099 expected := `{"bar":"from-flag","foo":"from-flag"}` 5100 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5101 withDockerfile(`FROM `+minimalBaseImage()+` 5102 LABEL foo=from-dockerfile`)) 5103 res := inspectFieldJSON(c, name, "Config.Labels") 5104 if res != expected { 5105 c.Fatalf("Labels %s, expected %s", res, expected) 5106 } 5107 5108 name = "from" 5109 expected = `{"foo":"from-dockerfile"}` 5110 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 5111 LABEL foo from-dockerfile`)) 5112 res = inspectFieldJSON(c, name, "Config.Labels") 5113 if res != expected { 5114 c.Fatalf("Labels %s, expected %s", res, expected) 5115 } 5116 5117 // Command line option label will override even via `FROM` 5118 name = "new" 5119 expected = `{"bar":"from-dockerfile2","foo":"new"}` 5120 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo=new"), 5121 withDockerfile(`FROM from 5122 LABEL bar from-dockerfile2`)) 5123 res = inspectFieldJSON(c, name, "Config.Labels") 5124 if res != expected { 5125 c.Fatalf("Labels %s, expected %s", res, expected) 5126 } 5127 5128 // Command line option without a value set (--label foo, --label bar=) 5129 // will be treated as --label foo="", --label bar="" 5130 name = "scratchy2" 5131 expected = `{"bar":"","foo":""}` 5132 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo", "--label", "bar="), 5133 withDockerfile(`FROM `+minimalBaseImage()+` 5134 LABEL foo=from-dockerfile`)) 5135 res = inspectFieldJSON(c, name, "Config.Labels") 5136 if res != expected { 5137 c.Fatalf("Labels %s, expected %s", res, expected) 5138 } 5139 5140 // Command line option without a value set (--label foo, --label bar=) 5141 // will be treated as --label foo="", --label bar="" 5142 // This time is for inherited images 5143 name = "new2" 5144 expected = `{"bar":"","foo":""}` 5145 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo=", "--label", "bar"), 5146 withDockerfile(`FROM from 5147 LABEL bar from-dockerfile2`)) 5148 res = inspectFieldJSON(c, name, "Config.Labels") 5149 if res != expected { 5150 c.Fatalf("Labels %s, expected %s", res, expected) 5151 } 5152 5153 // Command line option labels with only `FROM` 5154 name = "scratchy" 5155 expected = `{"bar":"from-flag","foo":"from-flag"}` 5156 buildImageSuccessfully(c, name, withBuildFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5157 withDockerfile(`FROM `+minimalBaseImage())) 5158 res = inspectFieldJSON(c, name, "Config.Labels") 5159 if res != expected { 5160 c.Fatalf("Labels %s, expected %s", res, expected) 5161 } 5162 5163 // Command line option labels with env var 5164 name = "scratchz" 5165 expected = `{"bar":"$PATH"}` 5166 buildImageSuccessfully(c, name, withBuildFlags("--label", "bar=$PATH"), 5167 withDockerfile(`FROM `+minimalBaseImage())) 5168 res = inspectFieldJSON(c, name, "Config.Labels") 5169 if res != expected { 5170 c.Fatalf("Labels %s, expected %s", res, expected) 5171 } 5172 } 5173 5174 // Test case for #22855 5175 func (s *DockerSuite) TestBuildDeleteCommittedFile(c *check.C) { 5176 name := "test-delete-committed-file" 5177 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 5178 RUN echo test > file 5179 RUN test -e file 5180 RUN rm file 5181 RUN sh -c "! test -e file"`)) 5182 } 5183 5184 // #20083 5185 func (s *DockerSuite) TestBuildDockerignoreComment(c *check.C) { 5186 // TODO Windows: Figure out why this test is flakey on TP5. If you add 5187 // something like RUN sleep 5, or even RUN ls /tmp after the ADD line, 5188 // it is more reliable, but that's not a good fix. 5189 testRequires(c, DaemonIsLinux) 5190 5191 name := "testbuilddockerignorecleanpaths" 5192 dockerfile := ` 5193 FROM busybox 5194 ADD . /tmp/ 5195 RUN sh -c "(ls -la /tmp/#1)" 5196 RUN sh -c "(! ls -la /tmp/#2)" 5197 RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (ls /tmp/dir1/foo)"` 5198 buildImageSuccessfully(c, name, withBuildContext(c, 5199 withFile("Dockerfile", dockerfile), 5200 withFile("foo", "foo"), 5201 withFile("foo2", "foo2"), 5202 withFile("dir1/foo", "foo in dir1"), 5203 withFile("#1", "# file 1"), 5204 withFile("#2", "# file 2"), 5205 withFile(".dockerignore", `# Visual C++ cache files 5206 # because we have git ;-) 5207 # The above comment is from #20083 5208 foo 5209 #dir1/foo 5210 foo2 5211 # The following is considered as comment as # is at the beginning 5212 #1 5213 # The following is not considered as comment as # is not at the beginning 5214 #2 5215 `))) 5216 } 5217 5218 // Test case for #23221 5219 func (s *DockerSuite) TestBuildWithUTF8BOM(c *check.C) { 5220 name := "test-with-utf8-bom" 5221 dockerfile := []byte(`FROM busybox`) 5222 bomDockerfile := append([]byte{0xEF, 0xBB, 0xBF}, dockerfile...) 5223 buildImageSuccessfully(c, name, withBuildContext(c, 5224 withFile("Dockerfile", string(bomDockerfile)), 5225 )) 5226 } 5227 5228 // Test case for UTF-8 BOM in .dockerignore, related to #23221 5229 func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *check.C) { 5230 name := "test-with-utf8-bom-dockerignore" 5231 dockerfile := ` 5232 FROM busybox 5233 ADD . /tmp/ 5234 RUN ls -la /tmp 5235 RUN sh -c "! ls /tmp/Dockerfile" 5236 RUN ls /tmp/.dockerignore` 5237 dockerignore := []byte("./Dockerfile\n") 5238 bomDockerignore := append([]byte{0xEF, 0xBB, 0xBF}, dockerignore...) 5239 buildImageSuccessfully(c, name, withBuildContext(c, 5240 withFile("Dockerfile", dockerfile), 5241 withFile(".dockerignore", string(bomDockerignore)), 5242 )) 5243 } 5244 5245 // #22489 Shell test to confirm config gets updated correctly 5246 func (s *DockerSuite) TestBuildShellUpdatesConfig(c *check.C) { 5247 name := "testbuildshellupdatesconfig" 5248 5249 buildImageSuccessfully(c, name, withDockerfile(`FROM `+minimalBaseImage()+` 5250 SHELL ["foo", "-bar"]`)) 5251 expected := `["foo","-bar","#(nop) ","SHELL [foo -bar]"]` 5252 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 5253 if res != expected { 5254 c.Fatalf("%s, expected %s", res, expected) 5255 } 5256 res = inspectFieldJSON(c, name, "ContainerConfig.Shell") 5257 if res != `["foo","-bar"]` { 5258 c.Fatalf(`%s, expected ["foo","-bar"]`, res) 5259 } 5260 } 5261 5262 // #22489 Changing the shell multiple times and CMD after. 5263 func (s *DockerSuite) TestBuildShellMultiple(c *check.C) { 5264 name := "testbuildshellmultiple" 5265 5266 result := buildImage(name, withDockerfile(`FROM busybox 5267 RUN echo defaultshell 5268 SHELL ["echo"] 5269 RUN echoshell 5270 SHELL ["ls"] 5271 RUN -l 5272 CMD -l`)) 5273 result.Assert(c, icmd.Success) 5274 5275 // Must contain 'defaultshell' twice 5276 if len(strings.Split(result.Combined(), "defaultshell")) != 3 { 5277 c.Fatalf("defaultshell should have appeared twice in %s", result.Combined()) 5278 } 5279 5280 // Must contain 'echoshell' twice 5281 if len(strings.Split(result.Combined(), "echoshell")) != 3 { 5282 c.Fatalf("echoshell should have appeared twice in %s", result.Combined()) 5283 } 5284 5285 // Must contain "total " (part of ls -l) 5286 if !strings.Contains(result.Combined(), "total ") { 5287 c.Fatalf("%s should have contained 'total '", result.Combined()) 5288 } 5289 5290 // A container started from the image uses the shell-form CMD. 5291 // Last shell is ls. CMD is -l. So should contain 'total '. 5292 outrun, _ := dockerCmd(c, "run", "--rm", name) 5293 if !strings.Contains(outrun, "total ") { 5294 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5295 } 5296 } 5297 5298 // #22489. Changed SHELL with ENTRYPOINT 5299 func (s *DockerSuite) TestBuildShellEntrypoint(c *check.C) { 5300 name := "testbuildshellentrypoint" 5301 5302 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 5303 SHELL ["ls"] 5304 ENTRYPOINT -l`)) 5305 // A container started from the image uses the shell-form ENTRYPOINT. 5306 // Shell is ls. ENTRYPOINT is -l. So should contain 'total '. 5307 outrun, _ := dockerCmd(c, "run", "--rm", name) 5308 if !strings.Contains(outrun, "total ") { 5309 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5310 } 5311 } 5312 5313 // #22489 Shell test to confirm shell is inherited in a subsequent build 5314 func (s *DockerSuite) TestBuildShellInherited(c *check.C) { 5315 name1 := "testbuildshellinherited1" 5316 buildImageSuccessfully(c, name1, withDockerfile(`FROM busybox 5317 SHELL ["ls"]`)) 5318 name2 := "testbuildshellinherited2" 5319 buildImage(name2, withDockerfile(`FROM `+name1+` 5320 RUN -l`)).Assert(c, icmd.Expected{ 5321 // ls -l has "total " followed by some number in it, ls without -l does not. 5322 Out: "total ", 5323 }) 5324 } 5325 5326 // #22489 Shell test to confirm non-JSON doesn't work 5327 func (s *DockerSuite) TestBuildShellNotJSON(c *check.C) { 5328 name := "testbuildshellnotjson" 5329 5330 buildImage(name, withDockerfile(`FROM `+minimalBaseImage()+` 5331 sHeLl exec -form`, // Casing explicit to ensure error is upper-cased. 5332 )).Assert(c, icmd.Expected{ 5333 ExitCode: 1, 5334 Err: "SHELL requires the arguments to be in JSON form", 5335 }) 5336 } 5337 5338 // #22489 Windows shell test to confirm native is powershell if executing a PS command 5339 // This would error if the default shell were still cmd. 5340 func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) { 5341 testRequires(c, DaemonIsWindows) 5342 name := "testbuildshellpowershell" 5343 buildImage(name, withDockerfile(`FROM `+minimalBaseImage()+` 5344 SHELL ["powershell", "-command"] 5345 RUN Write-Host John`)).Assert(c, icmd.Expected{ 5346 Out: "\nJohn\n", 5347 }) 5348 } 5349 5350 // Verify that escape is being correctly applied to words when escape directive is not \. 5351 // Tests WORKDIR, ADD 5352 func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) { 5353 testRequires(c, DaemonIsWindows) 5354 name := "testbuildescapenotbackslashwordtesta" 5355 buildImage(name, withDockerfile(`# escape= `+"`"+` 5356 FROM `+minimalBaseImage()+` 5357 WORKDIR c:\windows 5358 RUN dir /w`)).Assert(c, icmd.Expected{ 5359 Out: "[System32]", 5360 }) 5361 5362 name = "testbuildescapenotbackslashwordtestb" 5363 buildImage(name, withDockerfile(`# escape= `+"`"+` 5364 FROM `+minimalBaseImage()+` 5365 SHELL ["powershell.exe"] 5366 WORKDIR c:\foo 5367 ADD Dockerfile c:\foo\ 5368 RUN dir Dockerfile`)).Assert(c, icmd.Expected{ 5369 Out: "-a----", 5370 }) 5371 } 5372 5373 // #22868. Make sure shell-form CMD is marked as escaped in the config of the image 5374 func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) { 5375 testRequires(c, DaemonIsWindows) 5376 name := "testbuildcmdshellescaped" 5377 buildImageSuccessfully(c, name, withDockerfile(` 5378 FROM `+minimalBaseImage()+` 5379 CMD "ipconfig" 5380 `)) 5381 res := inspectFieldJSON(c, name, "Config.ArgsEscaped") 5382 if res != "true" { 5383 c.Fatalf("CMD did not update Config.ArgsEscaped on image: %v", res) 5384 } 5385 dockerCmd(c, "run", "--name", "inspectme", name) 5386 dockerCmd(c, "wait", "inspectme") 5387 res = inspectFieldJSON(c, name, "Config.Cmd") 5388 5389 if res != `["cmd","/S","/C","\"ipconfig\""]` { 5390 c.Fatalf("CMD was not escaped Config.Cmd: got %v", res) 5391 } 5392 } 5393 5394 // Test case for #24912. 5395 func (s *DockerSuite) TestBuildStepsWithProgress(c *check.C) { 5396 name := "testbuildstepswithprogress" 5397 totalRun := 5 5398 result := buildImage(name, withDockerfile("FROM busybox\n"+strings.Repeat("RUN echo foo\n", totalRun))) 5399 result.Assert(c, icmd.Success) 5400 c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun)) 5401 for i := 2; i <= 1+totalRun; i++ { 5402 c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun)) 5403 } 5404 } 5405 5406 func (s *DockerSuite) TestBuildWithFailure(c *check.C) { 5407 name := "testbuildwithfailure" 5408 5409 // First test case can only detect `nobody` in runtime so all steps will show up 5410 dockerfile := "FROM busybox\nRUN nobody" 5411 result := buildImage(name, withDockerfile(dockerfile)) 5412 c.Assert(result.Error, checker.NotNil) 5413 c.Assert(result.Stdout(), checker.Contains, "Step 1/2 : FROM busybox") 5414 c.Assert(result.Stdout(), checker.Contains, "Step 2/2 : RUN nobody") 5415 5416 // Second test case `FFOM` should have been detected before build runs so no steps 5417 dockerfile = "FFOM nobody\nRUN nobody" 5418 result = buildImage(name, withDockerfile(dockerfile)) 5419 c.Assert(result.Error, checker.NotNil) 5420 c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 1/2 : FROM busybox") 5421 c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 2/2 : RUN nobody") 5422 } 5423 5424 func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) { 5425 dockerfile := ` 5426 FROM busybox 5427 RUN echo "test" 5428 ENTRYPOINT ["sh"]` 5429 ctx := fakeContext(c, dockerfile, map[string]string{ 5430 "Dockerfile": dockerfile, 5431 }) 5432 defer ctx.Close() 5433 5434 buildImageSuccessfully(c, "build1", withExternalBuildContext(ctx)) 5435 id1 := getIDByName(c, "build1") 5436 5437 // rebuild with cache-from 5438 result := buildImage("build2", withBuildFlags("--cache-from=build1"), withExternalBuildContext(ctx)) 5439 result.Assert(c, icmd.Success) 5440 id2 := getIDByName(c, "build2") 5441 c.Assert(id1, checker.Equals, id2) 5442 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2) 5443 } 5444 5445 func (s *DockerSuite) TestBuildCacheFrom(c *check.C) { 5446 testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows 5447 dockerfile := ` 5448 FROM busybox 5449 ENV FOO=bar 5450 ADD baz / 5451 RUN touch bax` 5452 ctx := fakeContext(c, dockerfile, map[string]string{ 5453 "Dockerfile": dockerfile, 5454 "baz": "baz", 5455 }) 5456 defer ctx.Close() 5457 5458 buildImageSuccessfully(c, "build1", withExternalBuildContext(ctx)) 5459 id1 := getIDByName(c, "build1") 5460 5461 // rebuild with cache-from 5462 result := buildImage("build2", withBuildFlags("--cache-from=build1"), withExternalBuildContext(ctx)) 5463 result.Assert(c, icmd.Success) 5464 id2 := getIDByName(c, "build2") 5465 c.Assert(id1, checker.Equals, id2) 5466 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5467 dockerCmd(c, "rmi", "build2") 5468 5469 // no cache match with unknown source 5470 result = buildImage("build2", withBuildFlags("--cache-from=nosuchtag"), withExternalBuildContext(ctx)) 5471 result.Assert(c, icmd.Success) 5472 id2 = getIDByName(c, "build2") 5473 c.Assert(id1, checker.Not(checker.Equals), id2) 5474 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 0) 5475 dockerCmd(c, "rmi", "build2") 5476 5477 // clear parent images 5478 tempDir, err := ioutil.TempDir("", "test-build-cache-from-") 5479 if err != nil { 5480 c.Fatalf("failed to create temporary directory: %s", tempDir) 5481 } 5482 defer os.RemoveAll(tempDir) 5483 tempFile := filepath.Join(tempDir, "img.tar") 5484 dockerCmd(c, "save", "-o", tempFile, "build1") 5485 dockerCmd(c, "rmi", "build1") 5486 dockerCmd(c, "load", "-i", tempFile) 5487 parentID, _ := dockerCmd(c, "inspect", "-f", "{{.Parent}}", "build1") 5488 c.Assert(strings.TrimSpace(parentID), checker.Equals, "") 5489 5490 // cache still applies without parents 5491 result = buildImage("build2", withBuildFlags("--cache-from=build1"), withExternalBuildContext(ctx)) 5492 result.Assert(c, icmd.Success) 5493 id2 = getIDByName(c, "build2") 5494 c.Assert(id1, checker.Equals, id2) 5495 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5496 history1, _ := dockerCmd(c, "history", "-q", "build2") 5497 5498 // Retry, no new intermediate images 5499 result = buildImage("build3", withBuildFlags("--cache-from=build1"), withExternalBuildContext(ctx)) 5500 result.Assert(c, icmd.Success) 5501 id3 := getIDByName(c, "build3") 5502 c.Assert(id1, checker.Equals, id3) 5503 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5504 history2, _ := dockerCmd(c, "history", "-q", "build3") 5505 5506 c.Assert(history1, checker.Equals, history2) 5507 dockerCmd(c, "rmi", "build2") 5508 dockerCmd(c, "rmi", "build3") 5509 dockerCmd(c, "rmi", "build1") 5510 dockerCmd(c, "load", "-i", tempFile) 5511 5512 // Modify file, everything up to last command and layers are reused 5513 dockerfile = ` 5514 FROM busybox 5515 ENV FOO=bar 5516 ADD baz / 5517 RUN touch newfile` 5518 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644) 5519 c.Assert(err, checker.IsNil) 5520 5521 result = buildImage("build2", withBuildFlags("--cache-from=build1"), withExternalBuildContext(ctx)) 5522 result.Assert(c, icmd.Success) 5523 id2 = getIDByName(c, "build2") 5524 c.Assert(id1, checker.Not(checker.Equals), id2) 5525 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2) 5526 5527 layers1Str, _ := dockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build1") 5528 layers2Str, _ := dockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build2") 5529 5530 var layers1 []string 5531 var layers2 []string 5532 c.Assert(json.Unmarshal([]byte(layers1Str), &layers1), checker.IsNil) 5533 c.Assert(json.Unmarshal([]byte(layers2Str), &layers2), checker.IsNil) 5534 5535 c.Assert(len(layers1), checker.Equals, len(layers2)) 5536 for i := 0; i < len(layers1)-1; i++ { 5537 c.Assert(layers1[i], checker.Equals, layers2[i]) 5538 } 5539 c.Assert(layers1[len(layers1)-1], checker.Not(checker.Equals), layers2[len(layers1)-1]) 5540 } 5541 5542 func (s *DockerSuite) TestBuildNetNone(c *check.C) { 5543 testRequires(c, DaemonIsLinux) 5544 name := "testbuildnetnone" 5545 buildImage(name, withBuildFlags("--network=none"), withDockerfile(` 5546 FROM busybox 5547 RUN ping -c 1 8.8.8.8 5548 `)).Assert(c, icmd.Expected{ 5549 ExitCode: 1, 5550 Out: "unreachable", 5551 }) 5552 } 5553 5554 func (s *DockerSuite) TestBuildNetContainer(c *check.C) { 5555 testRequires(c, DaemonIsLinux) 5556 5557 id, _ := dockerCmd(c, "run", "--hostname", "foobar", "-d", "busybox", "nc", "-ll", "-p", "1234", "-e", "hostname") 5558 5559 name := "testbuildnetcontainer" 5560 buildImageSuccessfully(c, name, withBuildFlags("--network=container:"+strings.TrimSpace(id)), 5561 withDockerfile(` 5562 FROM busybox 5563 RUN nc localhost 1234 > /otherhost 5564 `)) 5565 5566 host, _ := dockerCmd(c, "run", "testbuildnetcontainer", "cat", "/otherhost") 5567 c.Assert(strings.TrimSpace(host), check.Equals, "foobar") 5568 } 5569 5570 func (s *DockerSuite) TestBuildSquashParent(c *check.C) { 5571 testRequires(c, ExperimentalDaemon) 5572 dockerFile := ` 5573 FROM busybox 5574 RUN echo hello > /hello 5575 RUN echo world >> /hello 5576 RUN echo hello > /remove_me 5577 ENV HELLO world 5578 RUN rm /remove_me 5579 ` 5580 // build and get the ID that we can use later for history comparison 5581 name := "test" 5582 buildImageSuccessfully(c, name, withDockerfile(dockerFile)) 5583 origID := getIDByName(c, name) 5584 5585 // build with squash 5586 buildImageSuccessfully(c, name, withBuildFlags("--squash"), withDockerfile(dockerFile)) 5587 id := getIDByName(c, name) 5588 5589 out, _ := dockerCmd(c, "run", "--rm", id, "/bin/sh", "-c", "cat /hello") 5590 c.Assert(strings.TrimSpace(out), checker.Equals, "hello\nworld") 5591 5592 dockerCmd(c, "run", "--rm", id, "/bin/sh", "-c", "[ ! -f /remove_me ]") 5593 dockerCmd(c, "run", "--rm", id, "/bin/sh", "-c", `[ "$(echo $HELLO)" == "world" ]`) 5594 5595 // make sure the ID produced is the ID of the tag we specified 5596 inspectID := inspectImage(c, "test", ".ID") 5597 c.Assert(inspectID, checker.Equals, id) 5598 5599 origHistory, _ := dockerCmd(c, "history", origID) 5600 testHistory, _ := dockerCmd(c, "history", "test") 5601 5602 splitOrigHistory := strings.Split(strings.TrimSpace(origHistory), "\n") 5603 splitTestHistory := strings.Split(strings.TrimSpace(testHistory), "\n") 5604 c.Assert(len(splitTestHistory), checker.Equals, len(splitOrigHistory)+1) 5605 5606 out = inspectImage(c, id, "len .RootFS.Layers") 5607 c.Assert(strings.TrimSpace(out), checker.Equals, "3") 5608 } 5609 5610 func (s *DockerSuite) TestBuildContChar(c *check.C) { 5611 name := "testbuildcontchar" 5612 5613 buildImage(name, withDockerfile(`FROM busybox\`)).Assert(c, icmd.Expected{ 5614 Out: "Step 1/1 : FROM busybox", 5615 }) 5616 5617 result := buildImage(name, withDockerfile(`FROM busybox 5618 RUN echo hi \`)) 5619 result.Assert(c, icmd.Success) 5620 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5621 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi\n") 5622 5623 result = buildImage(name, withDockerfile(`FROM busybox 5624 RUN echo hi \\`)) 5625 result.Assert(c, icmd.Success) 5626 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5627 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\n") 5628 5629 result = buildImage(name, withDockerfile(`FROM busybox 5630 RUN echo hi \\\`)) 5631 result.Assert(c, icmd.Success) 5632 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5633 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\\\n") 5634 } 5635 5636 // TestBuildOpaqueDirectory tests that a build succeeds which 5637 // creates opaque directories. 5638 // See https://github.com/docker/docker/issues/25244 5639 func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) { 5640 testRequires(c, DaemonIsLinux) 5641 dockerFile := ` 5642 FROM busybox 5643 RUN mkdir /dir1 && touch /dir1/f1 5644 RUN rm -rf /dir1 && mkdir /dir1 && touch /dir1/f2 5645 RUN touch /dir1/f3 5646 RUN [ -f /dir1/f2 ] 5647 ` 5648 // Test that build succeeds, last command fails if opaque directory 5649 // was not handled correctly 5650 buildImageSuccessfully(c, "testopaquedirectory", withDockerfile(dockerFile)) 5651 } 5652 5653 // Windows test for USER in dockerfile 5654 func (s *DockerSuite) TestBuildWindowsUser(c *check.C) { 5655 testRequires(c, DaemonIsWindows) 5656 name := "testbuildwindowsuser" 5657 buildImage(name, withDockerfile(`FROM `+testEnv.MinimalBaseImage()+` 5658 RUN net user user /add 5659 USER user 5660 RUN set username 5661 `)).Assert(c, icmd.Expected{ 5662 Out: "USERNAME=user", 5663 }) 5664 } 5665 5666 // Verifies if COPY file . when WORKDIR is set to a non-existing directory, 5667 // the directory is created and the file is copied into the directory, 5668 // as opposed to the file being copied as a file with the name of the 5669 // directory. Fix for 27545 (found on Windows, but regression good for Linux too). 5670 // Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514. 5671 func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) { 5672 name := "testbuildcopyfiledotwithworkdir" 5673 buildImageSuccessfully(c, name, withBuildContext(c, 5674 withFile("Dockerfile", `FROM busybox 5675 WORKDIR /foo 5676 COPY file . 5677 RUN ["cat", "/foo/file"] 5678 `), 5679 withFile("file", "content"), 5680 )) 5681 } 5682 5683 // Case-insensitive environment variables on Windows 5684 func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) { 5685 testRequires(c, DaemonIsWindows) 5686 name := "testbuildwindowsenvcaseinsensitive" 5687 buildImageSuccessfully(c, name, withDockerfile(` 5688 FROM `+testEnv.MinimalBaseImage()+` 5689 ENV FOO=bar foo=baz 5690 `)) 5691 res := inspectFieldJSON(c, name, "Config.Env") 5692 if res != `["foo=baz"]` { // Should not have FOO=bar in it - takes the last one processed. And only one entry as deduped. 5693 c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res) 5694 } 5695 } 5696 5697 // Test case for 29667 5698 func (s *DockerSuite) TestBuildWorkdirImageCmd(c *check.C) { 5699 image := "testworkdirimagecmd" 5700 buildImageSuccessfully(c, image, withDockerfile(` 5701 FROM busybox 5702 WORKDIR /foo/bar 5703 `)) 5704 out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 5705 5706 // The Windows busybox image has a blank `cmd` 5707 lookingFor := `["sh"]` 5708 if testEnv.DaemonPlatform() == "windows" { 5709 lookingFor = "null" 5710 } 5711 c.Assert(strings.TrimSpace(out), checker.Equals, lookingFor) 5712 5713 image = "testworkdirlabelimagecmd" 5714 buildImageSuccessfully(c, image, withDockerfile(` 5715 FROM busybox 5716 WORKDIR /foo/bar 5717 LABEL a=b 5718 `)) 5719 5720 out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 5721 c.Assert(strings.TrimSpace(out), checker.Equals, lookingFor) 5722 } 5723 5724 // Test case for 28902/28909 5725 func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) { 5726 testRequires(c, DaemonIsLinux) 5727 name := "testbuildworkdircmd" 5728 dockerFile := ` 5729 FROM busybox 5730 WORKDIR / 5731 ` 5732 buildImageSuccessfully(c, name, withDockerfile(dockerFile)) 5733 result := buildImage(name, withDockerfile(dockerFile)) 5734 result.Assert(c, icmd.Success) 5735 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1) 5736 } 5737 5738 // FIXME(vdemeester) should be a unit test 5739 func (s *DockerSuite) TestBuildLineErrorOnBuild(c *check.C) { 5740 name := "test_build_line_error_onbuild" 5741 buildImage(name, withDockerfile(`FROM busybox 5742 ONBUILD 5743 `)).Assert(c, icmd.Expected{ 5744 ExitCode: 1, 5745 Err: "Dockerfile parse error line 2: ONBUILD requires at least one argument", 5746 }) 5747 } 5748 5749 // FIXME(vdemeester) should be a unit test 5750 func (s *DockerSuite) TestBuildLineErrorUknownInstruction(c *check.C) { 5751 name := "test_build_line_error_unknown_instruction" 5752 buildImage(name, withDockerfile(`FROM busybox 5753 RUN echo hello world 5754 NOINSTRUCTION echo ba 5755 RUN echo hello 5756 ERROR 5757 `)).Assert(c, icmd.Expected{ 5758 ExitCode: 1, 5759 Err: "Dockerfile parse error line 3: Unknown instruction: NOINSTRUCTION", 5760 }) 5761 } 5762 5763 // FIXME(vdemeester) should be a unit test 5764 func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) { 5765 name := "test_build_line_error_with_empty_lines" 5766 buildImage(name, withDockerfile(` 5767 FROM busybox 5768 5769 RUN echo hello world 5770 5771 NOINSTRUCTION echo ba 5772 5773 CMD ["/bin/init"] 5774 `)).Assert(c, icmd.Expected{ 5775 ExitCode: 1, 5776 Err: "Dockerfile parse error line 6: Unknown instruction: NOINSTRUCTION", 5777 }) 5778 } 5779 5780 // FIXME(vdemeester) should be a unit test 5781 func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) { 5782 name := "test_build_line_error_with_comments" 5783 buildImage(name, withDockerfile(`FROM busybox 5784 # This will print hello world 5785 # and then ba 5786 RUN echo hello world 5787 NOINSTRUCTION echo ba 5788 `)).Assert(c, icmd.Expected{ 5789 ExitCode: 1, 5790 Err: "Dockerfile parse error line 5: Unknown instruction: NOINSTRUCTION", 5791 }) 5792 }