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