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