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