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