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