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