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