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