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