github.com/afbjorklund/moby@v20.10.5+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/pkg/archive" 23 "github.com/docker/docker/pkg/system" 24 "github.com/docker/docker/testutil" 25 "github.com/docker/docker/testutil/fakecontext" 26 "github.com/docker/docker/testutil/fakegit" 27 "github.com/docker/docker/testutil/fakestorage" 28 "github.com/moby/buildkit/frontend/dockerfile/command" 29 digest "github.com/opencontainers/go-digest" 30 "gotest.tools/v3/assert" 31 "gotest.tools/v3/assert/cmp" 32 "gotest.tools/v3/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: 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, 1) 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, 1) 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 || !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 || !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. 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 2193 buildImage(name, build.WithBuildContext(c, 2194 build.WithFile("Dockerfile", `FROM `+minimalBaseImage()+` 2195 ADD foo /usr/local/bar`), 2196 build.WithFile("bar", "hello"))).Assert(c, icmd.Expected{ 2197 ExitCode: 1, 2198 Err: "stat foo: file does not exist", 2199 }) 2200 } 2201 2202 func (s *DockerSuite) TestBuildInheritance(c *testing.T) { 2203 testRequires(c, DaemonIsLinux) 2204 name := "testbuildinheritance" 2205 2206 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM scratch 2207 EXPOSE 2375`)) 2208 ports1 := inspectField(c, name, "Config.ExposedPorts") 2209 2210 buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`FROM %s 2211 ENTRYPOINT ["/bin/echo"]`, name))) 2212 2213 res := inspectField(c, name, "Config.Entrypoint") 2214 if expected := "[/bin/echo]"; res != expected { 2215 c.Fatalf("Entrypoint %s, expected %s", res, expected) 2216 } 2217 ports2 := inspectField(c, name, "Config.ExposedPorts") 2218 if ports1 != ports2 { 2219 c.Fatalf("Ports must be same: %s != %s", ports1, ports2) 2220 } 2221 } 2222 2223 func (s *DockerSuite) TestBuildFails(c *testing.T) { 2224 name := "testbuildfails" 2225 buildImage(name, build.WithDockerfile(`FROM busybox 2226 RUN sh -c "exit 23"`)).Assert(c, icmd.Expected{ 2227 ExitCode: 23, 2228 Err: "returned a non-zero code: 23", 2229 }) 2230 } 2231 2232 func (s *DockerSuite) TestBuildOnBuild(c *testing.T) { 2233 name := "testbuildonbuild" 2234 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 2235 ONBUILD RUN touch foobar`)) 2236 buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`FROM %s 2237 RUN [ -f foobar ]`, name))) 2238 } 2239 2240 // gh #2446 2241 func (s *DockerSuite) TestBuildAddToSymlinkDest(c *testing.T) { 2242 makeLink := `ln -s /foo /bar` 2243 if testEnv.OSType == "windows" { 2244 makeLink = `mklink /D C:\bar C:\foo` 2245 } 2246 name := "testbuildaddtosymlinkdest" 2247 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2248 build.WithFile("Dockerfile", ` 2249 FROM busybox 2250 RUN sh -c "mkdir /foo" 2251 RUN `+makeLink+` 2252 ADD foo /bar/ 2253 RUN sh -c "[ -f /bar/foo ]" 2254 RUN sh -c "[ -f /foo/foo ]"`), 2255 build.WithFile("foo", "hello"), 2256 )) 2257 } 2258 2259 func (s *DockerSuite) TestBuildEscapeWhitespace(c *testing.T) { 2260 name := "testbuildescapewhitespace" 2261 2262 buildImageSuccessfully(c, name, build.WithDockerfile(` 2263 # ESCAPE=\ 2264 FROM busybox 2265 MAINTAINER "Docker \ 2266 IO <io@\ 2267 docker.com>" 2268 `)) 2269 2270 res := inspectField(c, name, "Author") 2271 if res != "\"Docker IO <io@docker.com>\"" { 2272 c.Fatalf("Parsed string did not match the escaped string. Got: %q", res) 2273 } 2274 2275 } 2276 2277 func (s *DockerSuite) TestBuildVerifyIntString(c *testing.T) { 2278 // Verify that strings that look like ints are still passed as strings 2279 name := "testbuildstringing" 2280 2281 buildImageSuccessfully(c, name, build.WithDockerfile(` 2282 FROM busybox 2283 MAINTAINER 123`)) 2284 2285 out, _ := dockerCmd(c, "inspect", name) 2286 if !strings.Contains(out, "\"123\"") { 2287 c.Fatalf("Output does not contain the int as a string:\n%s", out) 2288 } 2289 2290 } 2291 2292 func (s *DockerSuite) TestBuildDockerignore(c *testing.T) { 2293 name := "testbuilddockerignore" 2294 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2295 build.WithFile("Dockerfile", ` 2296 FROM busybox 2297 ADD . /bla 2298 RUN sh -c "[[ -f /bla/src/x.go ]]" 2299 RUN sh -c "[[ -f /bla/Makefile ]]" 2300 RUN sh -c "[[ ! -e /bla/src/_vendor ]]" 2301 RUN sh -c "[[ ! -e /bla/.gitignore ]]" 2302 RUN sh -c "[[ ! -e /bla/README.md ]]" 2303 RUN sh -c "[[ ! -e /bla/dir/foo ]]" 2304 RUN sh -c "[[ ! -e /bla/foo ]]" 2305 RUN sh -c "[[ ! -e /bla/.git ]]" 2306 RUN sh -c "[[ ! -e v.cc ]]" 2307 RUN sh -c "[[ ! -e src/v.cc ]]" 2308 RUN sh -c "[[ ! -e src/_vendor/v.cc ]]"`), 2309 build.WithFile("Makefile", "all:"), 2310 build.WithFile(".git/HEAD", "ref: foo"), 2311 build.WithFile("src/x.go", "package main"), 2312 build.WithFile("src/_vendor/v.go", "package main"), 2313 build.WithFile("src/_vendor/v.cc", "package main"), 2314 build.WithFile("src/v.cc", "package main"), 2315 build.WithFile("v.cc", "package main"), 2316 build.WithFile("dir/foo", ""), 2317 build.WithFile(".gitignore", ""), 2318 build.WithFile("README.md", "readme"), 2319 build.WithFile(".dockerignore", ` 2320 .git 2321 pkg 2322 .gitignore 2323 src/_vendor 2324 *.md 2325 **/*.cc 2326 dir`), 2327 )) 2328 } 2329 2330 func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *testing.T) { 2331 name := "testbuilddockerignorecleanpaths" 2332 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2333 build.WithFile("Dockerfile", ` 2334 FROM busybox 2335 ADD . /tmp/ 2336 RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (! ls /tmp/dir1/foo)"`), 2337 build.WithFile("foo", "foo"), 2338 build.WithFile("foo2", "foo2"), 2339 build.WithFile("dir1/foo", "foo in dir1"), 2340 build.WithFile(".dockerignore", "./foo\ndir1//foo\n./dir1/../foo2"), 2341 )) 2342 } 2343 2344 func (s *DockerSuite) TestBuildDockerignoreExceptions(c *testing.T) { 2345 name := "testbuilddockerignoreexceptions" 2346 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2347 build.WithFile("Dockerfile", ` 2348 FROM busybox 2349 ADD . /bla 2350 RUN sh -c "[[ -f /bla/src/x.go ]]" 2351 RUN sh -c "[[ -f /bla/Makefile ]]" 2352 RUN sh -c "[[ ! -e /bla/src/_vendor ]]" 2353 RUN sh -c "[[ ! -e /bla/.gitignore ]]" 2354 RUN sh -c "[[ ! -e /bla/README.md ]]" 2355 RUN sh -c "[[ -e /bla/dir/dir/foo ]]" 2356 RUN sh -c "[[ ! -e /bla/dir/foo1 ]]" 2357 RUN sh -c "[[ -f /bla/dir/e ]]" 2358 RUN sh -c "[[ -f /bla/dir/e-dir/foo ]]" 2359 RUN sh -c "[[ ! -e /bla/foo ]]" 2360 RUN sh -c "[[ ! -e /bla/.git ]]" 2361 RUN sh -c "[[ -e /bla/dir/a.cc ]]"`), 2362 build.WithFile("Makefile", "all:"), 2363 build.WithFile(".git/HEAD", "ref: foo"), 2364 build.WithFile("src/x.go", "package main"), 2365 build.WithFile("src/_vendor/v.go", "package main"), 2366 build.WithFile("dir/foo", ""), 2367 build.WithFile("dir/foo1", ""), 2368 build.WithFile("dir/dir/f1", ""), 2369 build.WithFile("dir/dir/foo", ""), 2370 build.WithFile("dir/e", ""), 2371 build.WithFile("dir/e-dir/foo", ""), 2372 build.WithFile(".gitignore", ""), 2373 build.WithFile("README.md", "readme"), 2374 build.WithFile("dir/a.cc", "hello"), 2375 build.WithFile(".dockerignore", ` 2376 .git 2377 pkg 2378 .gitignore 2379 src/_vendor 2380 *.md 2381 dir 2382 !dir/e* 2383 !dir/dir/foo 2384 **/*.cc 2385 !**/*.cc`), 2386 )) 2387 } 2388 2389 func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *testing.T) { 2390 name := "testbuilddockerignoredockerfile" 2391 dockerfile := ` 2392 FROM busybox 2393 ADD . /tmp/ 2394 RUN sh -c "! ls /tmp/Dockerfile" 2395 RUN ls /tmp/.dockerignore` 2396 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2397 build.WithFile("Dockerfile", dockerfile), 2398 build.WithFile(".dockerignore", "Dockerfile\n"), 2399 )) 2400 // FIXME(vdemeester) why twice ? 2401 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2402 build.WithFile("Dockerfile", dockerfile), 2403 build.WithFile(".dockerignore", "./Dockerfile\n"), 2404 )) 2405 } 2406 2407 func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *testing.T) { 2408 name := "testbuilddockerignoredockerfile" 2409 dockerfile := ` 2410 FROM busybox 2411 ADD . /tmp/ 2412 RUN ls /tmp/Dockerfile 2413 RUN sh -c "! ls /tmp/MyDockerfile" 2414 RUN ls /tmp/.dockerignore` 2415 buildImageSuccessfully(c, name, cli.WithFlags("-f", "MyDockerfile"), build.WithBuildContext(c, 2416 build.WithFile("Dockerfile", "Should not use me"), 2417 build.WithFile("MyDockerfile", dockerfile), 2418 build.WithFile(".dockerignore", "MyDockerfile\n"), 2419 )) 2420 // FIXME(vdemeester) why twice ? 2421 buildImageSuccessfully(c, name, cli.WithFlags("-f", "MyDockerfile"), build.WithBuildContext(c, 2422 build.WithFile("Dockerfile", "Should not use me"), 2423 build.WithFile("MyDockerfile", dockerfile), 2424 build.WithFile(".dockerignore", "./MyDockerfile\n"), 2425 )) 2426 } 2427 2428 func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *testing.T) { 2429 name := "testbuilddockerignoredockerignore" 2430 dockerfile := ` 2431 FROM busybox 2432 ADD . /tmp/ 2433 RUN sh -c "! ls /tmp/.dockerignore" 2434 RUN ls /tmp/Dockerfile` 2435 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2436 build.WithFile("Dockerfile", dockerfile), 2437 build.WithFile(".dockerignore", ".dockerignore\n"), 2438 )) 2439 } 2440 2441 func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *testing.T) { 2442 name := "testbuilddockerignoretouchdockerfile" 2443 dockerfile := ` 2444 FROM busybox 2445 ADD . /tmp/` 2446 ctx := fakecontext.New(c, "", 2447 fakecontext.WithDockerfile(dockerfile), 2448 fakecontext.WithFiles(map[string]string{ 2449 ".dockerignore": "Dockerfile\n", 2450 })) 2451 defer ctx.Close() 2452 2453 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 2454 id1 := getIDByName(c, name) 2455 2456 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 2457 id2 := getIDByName(c, name) 2458 if id1 != id2 { 2459 c.Fatalf("Didn't use the cache - 1") 2460 } 2461 2462 // Now make sure touching Dockerfile doesn't invalidate the cache 2463 if err := ctx.Add("Dockerfile", dockerfile+"\n# hi"); err != nil { 2464 c.Fatalf("Didn't add Dockerfile: %s", err) 2465 } 2466 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 2467 id2 = getIDByName(c, name) 2468 if id1 != id2 { 2469 c.Fatalf("Didn't use the cache - 2") 2470 } 2471 2472 // One more time but just 'touch' it instead of changing the content 2473 if err := ctx.Add("Dockerfile", dockerfile+"\n# hi"); err != nil { 2474 c.Fatalf("Didn't add Dockerfile: %s", err) 2475 } 2476 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 2477 id2 = getIDByName(c, name) 2478 if id1 != id2 { 2479 c.Fatalf("Didn't use the cache - 3") 2480 } 2481 } 2482 2483 func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *testing.T) { 2484 name := "testbuilddockerignorewholedir" 2485 2486 dockerfile := ` 2487 FROM busybox 2488 COPY . / 2489 RUN sh -c "[[ ! -e /.gitignore ]]" 2490 RUN sh -c "[[ ! -e /Makefile ]]"` 2491 2492 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2493 build.WithFile("Dockerfile", dockerfile), 2494 build.WithFile(".dockerignore", "*\n"), 2495 build.WithFile("Makefile", "all:"), 2496 build.WithFile(".gitignore", ""), 2497 )) 2498 } 2499 2500 func (s *DockerSuite) TestBuildDockerignoringOnlyDotfiles(c *testing.T) { 2501 name := "testbuilddockerignorewholedir" 2502 2503 dockerfile := ` 2504 FROM busybox 2505 COPY . / 2506 RUN sh -c "[[ ! -e /.gitignore ]]" 2507 RUN sh -c "[[ -f /Makefile ]]"` 2508 2509 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2510 build.WithFile("Dockerfile", dockerfile), 2511 build.WithFile(".dockerignore", ".*"), 2512 build.WithFile("Makefile", "all:"), 2513 build.WithFile(".gitignore", ""), 2514 )) 2515 } 2516 2517 func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *testing.T) { 2518 name := "testbuilddockerignorebadexclusion" 2519 buildImage(name, build.WithBuildContext(c, 2520 build.WithFile("Dockerfile", ` 2521 FROM busybox 2522 COPY . / 2523 RUN sh -c "[[ ! -e /.gitignore ]]" 2524 RUN sh -c "[[ -f /Makefile ]]"`), 2525 build.WithFile("Makefile", "all:"), 2526 build.WithFile(".gitignore", ""), 2527 build.WithFile(".dockerignore", "!\n"), 2528 )).Assert(c, icmd.Expected{ 2529 ExitCode: 1, 2530 Err: `illegal exclusion pattern: "!"`, 2531 }) 2532 } 2533 2534 func (s *DockerSuite) TestBuildDockerignoringWildTopDir(c *testing.T) { 2535 dockerfile := ` 2536 FROM busybox 2537 COPY . / 2538 RUN sh -c "[[ ! -e /.dockerignore ]]" 2539 RUN sh -c "[[ ! -e /Dockerfile ]]" 2540 RUN sh -c "[[ ! -e /file1 ]]" 2541 RUN sh -c "[[ ! -e /dir ]]"` 2542 2543 // All of these should result in ignoring all files 2544 for _, variant := range []string{"**", "**/", "**/**", "*"} { 2545 buildImageSuccessfully(c, "noname", build.WithBuildContext(c, 2546 build.WithFile("Dockerfile", dockerfile), 2547 build.WithFile("file1", ""), 2548 build.WithFile("dir/file1", ""), 2549 build.WithFile(".dockerignore", variant), 2550 )) 2551 2552 dockerCmd(c, "rmi", "noname") 2553 } 2554 } 2555 2556 func (s *DockerSuite) TestBuildDockerignoringWildDirs(c *testing.T) { 2557 dockerfile := ` 2558 FROM busybox 2559 COPY . / 2560 #RUN sh -c "[[ -e /.dockerignore ]]" 2561 RUN sh -c "[[ -e /Dockerfile ]] && \ 2562 [[ ! -e /file0 ]] && \ 2563 [[ ! -e /dir1/file0 ]] && \ 2564 [[ ! -e /dir2/file0 ]] && \ 2565 [[ ! -e /file1 ]] && \ 2566 [[ ! -e /dir1/file1 ]] && \ 2567 [[ ! -e /dir1/dir2/file1 ]] && \ 2568 [[ ! -e /dir1/file2 ]] && \ 2569 [[ -e /dir1/dir2/file2 ]] && \ 2570 [[ ! -e /dir1/dir2/file4 ]] && \ 2571 [[ ! -e /dir1/dir2/file5 ]] && \ 2572 [[ ! -e /dir1/dir2/file6 ]] && \ 2573 [[ ! -e /dir1/dir3/file7 ]] && \ 2574 [[ ! -e /dir1/dir3/file8 ]] && \ 2575 [[ -e /dir1/dir3 ]] && \ 2576 [[ -e /dir1/dir4 ]] && \ 2577 [[ ! -e 'dir1/dir5/fileAA' ]] && \ 2578 [[ -e 'dir1/dir5/fileAB' ]] && \ 2579 [[ -e 'dir1/dir5/fileB' ]]" # "." in pattern means nothing 2580 2581 RUN echo all done!` 2582 2583 dockerignore := ` 2584 **/file0 2585 **/*file1 2586 **/dir1/file2 2587 dir1/**/file4 2588 **/dir2/file5 2589 **/dir1/dir2/file6 2590 dir1/dir3/** 2591 **/dir4/** 2592 **/file?A 2593 **/file\?B 2594 **/dir5/file. 2595 ` 2596 2597 buildImageSuccessfully(c, "noname", build.WithBuildContext(c, 2598 build.WithFile("Dockerfile", dockerfile), 2599 build.WithFile(".dockerignore", dockerignore), 2600 build.WithFile("dir1/file0", ""), 2601 build.WithFile("dir1/dir2/file0", ""), 2602 build.WithFile("file1", ""), 2603 build.WithFile("dir1/file1", ""), 2604 build.WithFile("dir1/dir2/file1", ""), 2605 build.WithFile("dir1/file2", ""), 2606 build.WithFile("dir1/dir2/file2", ""), // remains 2607 build.WithFile("dir1/dir2/file4", ""), 2608 build.WithFile("dir1/dir2/file5", ""), 2609 build.WithFile("dir1/dir2/file6", ""), 2610 build.WithFile("dir1/dir3/file7", ""), 2611 build.WithFile("dir1/dir3/file8", ""), 2612 build.WithFile("dir1/dir4/file9", ""), 2613 build.WithFile("dir1/dir5/fileAA", ""), 2614 build.WithFile("dir1/dir5/fileAB", ""), 2615 build.WithFile("dir1/dir5/fileB", ""), 2616 )) 2617 } 2618 2619 func (s *DockerSuite) TestBuildLineBreak(c *testing.T) { 2620 testRequires(c, DaemonIsLinux) 2621 name := "testbuildlinebreak" 2622 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 2623 RUN sh -c 'echo root:testpass \ 2624 > /tmp/passwd' 2625 RUN mkdir -p /var/run/sshd 2626 RUN sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]" 2627 RUN sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`)) 2628 } 2629 2630 func (s *DockerSuite) TestBuildEOLInLine(c *testing.T) { 2631 testRequires(c, DaemonIsLinux) 2632 name := "testbuildeolinline" 2633 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 2634 RUN sh -c 'echo root:testpass > /tmp/passwd' 2635 RUN echo "foo \n bar"; echo "baz" 2636 RUN mkdir -p /var/run/sshd 2637 RUN sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]" 2638 RUN sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`)) 2639 } 2640 2641 func (s *DockerSuite) TestBuildCommentsShebangs(c *testing.T) { 2642 testRequires(c, DaemonIsLinux) 2643 name := "testbuildcomments" 2644 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 2645 # This is an ordinary comment. 2646 RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh 2647 RUN [ ! -x /hello.sh ] 2648 # comment with line break \ 2649 RUN chmod +x /hello.sh 2650 RUN [ -x /hello.sh ] 2651 RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ] 2652 RUN [ "$(/hello.sh)" = "hello world" ]`)) 2653 } 2654 2655 func (s *DockerSuite) TestBuildUsersAndGroups(c *testing.T) { 2656 testRequires(c, DaemonIsLinux) 2657 name := "testbuildusers" 2658 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 2659 2660 # Make sure our defaults work 2661 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)" = '0:0/root:root' ] 2662 2663 # 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) 2664 USER root 2665 RUN [ "$(id -G):$(id -Gn)" = '0 10:root wheel' ] 2666 2667 # Setup dockerio user and group 2668 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd && \ 2669 echo 'dockerio:x:1001:' >> /etc/group 2670 2671 # Make sure we can switch to our user and all the information is exactly as we expect it to be 2672 USER dockerio 2673 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2674 2675 # Switch back to root and double check that worked exactly as we might expect it to 2676 USER root 2677 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0 10:root wheel' ] && \ 2678 # Add a "supplementary" group for our dockerio user 2679 echo 'supplementary:x:1002:dockerio' >> /etc/group 2680 2681 # ... and then go verify that we get it like we expect 2682 USER dockerio 2683 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ] 2684 USER 1001 2685 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ] 2686 2687 # super test the new "user:group" syntax 2688 USER dockerio:dockerio 2689 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2690 USER 1001:dockerio 2691 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2692 USER dockerio:1001 2693 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2694 USER 1001:1001 2695 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ] 2696 USER dockerio:supplementary 2697 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2698 USER dockerio:1002 2699 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2700 USER 1001:supplementary 2701 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2702 USER 1001:1002 2703 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ] 2704 2705 # make sure unknown uid/gid still works properly 2706 USER 1042:1043 2707 RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/1042:1043/1043:1043' ]`)) 2708 } 2709 2710 // FIXME(vdemeester) rename this test (and probably "merge" it with the one below TestBuildEnvUsage2) 2711 func (s *DockerSuite) TestBuildEnvUsage(c *testing.T) { 2712 // /docker/world/hello is not owned by the correct user 2713 testRequires(c, NotUserNamespace) 2714 testRequires(c, DaemonIsLinux) 2715 name := "testbuildenvusage" 2716 dockerfile := `FROM busybox 2717 ENV HOME /root 2718 ENV PATH $HOME/bin:$PATH 2719 ENV PATH /tmp:$PATH 2720 RUN [ "$PATH" = "/tmp:$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ] 2721 ENV FOO /foo/baz 2722 ENV BAR /bar 2723 ENV BAZ $BAR 2724 ENV FOOPATH $PATH:$FOO 2725 RUN [ "$BAR" = "$BAZ" ] 2726 RUN [ "$FOOPATH" = "$PATH:/foo/baz" ] 2727 ENV FROM hello/docker/world 2728 ENV TO /docker/world/hello 2729 ADD $FROM $TO 2730 RUN [ "$(cat $TO)" = "hello" ] 2731 ENV abc=def 2732 ENV ghi=$abc 2733 RUN [ "$ghi" = "def" ] 2734 ` 2735 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2736 build.WithFile("Dockerfile", dockerfile), 2737 build.WithFile("hello/docker/world", "hello"), 2738 )) 2739 } 2740 2741 // FIXME(vdemeester) rename this test (and probably "merge" it with the one above TestBuildEnvUsage) 2742 func (s *DockerSuite) TestBuildEnvUsage2(c *testing.T) { 2743 // /docker/world/hello is not owned by the correct user 2744 testRequires(c, NotUserNamespace) 2745 testRequires(c, DaemonIsLinux) 2746 name := "testbuildenvusage2" 2747 dockerfile := `FROM busybox 2748 ENV abc=def def="hello world" 2749 RUN [ "$abc,$def" = "def,hello world" ] 2750 ENV def=hello\ world v1=abc v2="hi there" v3='boogie nights' v4="with'quotes too" 2751 RUN [ "$def,$v1,$v2,$v3,$v4" = "hello world,abc,hi there,boogie nights,with'quotes too" ] 2752 ENV abc=zzz FROM=hello/docker/world 2753 ENV abc=zzz TO=/docker/world/hello 2754 ADD $FROM $TO 2755 RUN [ "$abc,$(cat $TO)" = "zzz,hello" ] 2756 ENV abc 'yyy' 2757 RUN [ $abc = 'yyy' ] 2758 ENV abc= 2759 RUN [ "$abc" = "" ] 2760 2761 # use grep to make sure if the builder substitutes \$foo by mistake 2762 # we don't get a false positive 2763 ENV abc=\$foo 2764 RUN [ "$abc" = "\$foo" ] && (echo "$abc" | grep foo) 2765 ENV abc \$foo 2766 RUN [ "$abc" = "\$foo" ] && (echo "$abc" | grep foo) 2767 2768 ENV abc=\'foo\' abc2=\"foo\" 2769 RUN [ "$abc,$abc2" = "'foo',\"foo\"" ] 2770 ENV abc "foo" 2771 RUN [ "$abc" = "foo" ] 2772 ENV abc 'foo' 2773 RUN [ "$abc" = 'foo' ] 2774 ENV abc \'foo\' 2775 RUN [ "$abc" = "'foo'" ] 2776 ENV abc \"foo\" 2777 RUN [ "$abc" = '"foo"' ] 2778 2779 ENV abc=ABC 2780 RUN [ "$abc" = "ABC" ] 2781 ENV def1=${abc:-DEF} def2=${ccc:-DEF} 2782 ENV def3=${ccc:-${def2}xx} def4=${abc:+ALT} def5=${def2:+${abc}:} def6=${ccc:-\$abc:} def7=${ccc:-\${abc}:} 2783 RUN [ "$def1,$def2,$def3,$def4,$def5,$def6,$def7" = 'ABC,DEF,DEFxx,ALT,ABC:,$abc:,${abc:}' ] 2784 ENV mypath=${mypath:+$mypath:}/home 2785 ENV mypath=${mypath:+$mypath:}/away 2786 RUN [ "$mypath" = '/home:/away' ] 2787 2788 ENV e1=bar 2789 ENV e2=$e1 e3=$e11 e4=\$e1 e5=\$e11 2790 RUN [ "$e0,$e1,$e2,$e3,$e4,$e5" = ',bar,bar,,$e1,$e11' ] 2791 2792 ENV ee1 bar 2793 ENV ee2 $ee1 2794 ENV ee3 $ee11 2795 ENV ee4 \$ee1 2796 ENV ee5 \$ee11 2797 RUN [ "$ee1,$ee2,$ee3,$ee4,$ee5" = 'bar,bar,,$ee1,$ee11' ] 2798 2799 ENV eee1="foo" eee2='foo' 2800 ENV eee3 "foo" 2801 ENV eee4 'foo' 2802 RUN [ "$eee1,$eee2,$eee3,$eee4" = 'foo,foo,foo,foo' ] 2803 2804 ` 2805 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2806 build.WithFile("Dockerfile", dockerfile), 2807 build.WithFile("hello/docker/world", "hello"), 2808 )) 2809 } 2810 2811 func (s *DockerSuite) TestBuildAddScript(c *testing.T) { 2812 testRequires(c, DaemonIsLinux) 2813 name := "testbuildaddscript" 2814 dockerfile := ` 2815 FROM busybox 2816 ADD test /test 2817 RUN ["chmod","+x","/test"] 2818 RUN ["/test"] 2819 RUN [ "$(cat /testfile)" = 'test!' ]` 2820 2821 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2822 build.WithFile("Dockerfile", dockerfile), 2823 build.WithFile("test", "#!/bin/sh\necho 'test!' > /testfile"), 2824 )) 2825 } 2826 2827 func (s *DockerSuite) TestBuildAddTar(c *testing.T) { 2828 // /test/foo is not owned by the correct user 2829 testRequires(c, NotUserNamespace) 2830 name := "testbuildaddtar" 2831 2832 ctx := func() *fakecontext.Fake { 2833 dockerfile := ` 2834 FROM busybox 2835 ADD test.tar / 2836 RUN cat /test/foo | grep Hi 2837 ADD test.tar /test.tar 2838 RUN cat /test.tar/test/foo | grep Hi 2839 ADD test.tar /unlikely-to-exist 2840 RUN cat /unlikely-to-exist/test/foo | grep Hi 2841 ADD test.tar /unlikely-to-exist-trailing-slash/ 2842 RUN cat /unlikely-to-exist-trailing-slash/test/foo | grep Hi 2843 RUN sh -c "mkdir /existing-directory" #sh -c is needed on Windows to use the correct mkdir 2844 ADD test.tar /existing-directory 2845 RUN cat /existing-directory/test/foo | grep Hi 2846 ADD test.tar /existing-directory-trailing-slash/ 2847 RUN cat /existing-directory-trailing-slash/test/foo | grep Hi` 2848 tmpDir, err := ioutil.TempDir("", "fake-context") 2849 assert.NilError(c, err) 2850 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2851 if err != nil { 2852 c.Fatalf("failed to create test.tar archive: %v", err) 2853 } 2854 defer testTar.Close() 2855 2856 tw := tar.NewWriter(testTar) 2857 2858 if err := tw.WriteHeader(&tar.Header{ 2859 Name: "test/foo", 2860 Size: 2, 2861 }); err != nil { 2862 c.Fatalf("failed to write tar file header: %v", err) 2863 } 2864 if _, err := tw.Write([]byte("Hi")); err != nil { 2865 c.Fatalf("failed to write tar file content: %v", err) 2866 } 2867 if err := tw.Close(); err != nil { 2868 c.Fatalf("failed to close tar archive: %v", err) 2869 } 2870 2871 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2872 c.Fatalf("failed to open destination dockerfile: %v", err) 2873 } 2874 return fakecontext.New(c, tmpDir) 2875 }() 2876 defer ctx.Close() 2877 2878 buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx)) 2879 } 2880 2881 func (s *DockerSuite) TestBuildAddBrokenTar(c *testing.T) { 2882 name := "testbuildaddbrokentar" 2883 2884 ctx := func() *fakecontext.Fake { 2885 dockerfile := ` 2886 FROM busybox 2887 ADD test.tar /` 2888 tmpDir, err := ioutil.TempDir("", "fake-context") 2889 assert.NilError(c, err) 2890 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2891 if err != nil { 2892 c.Fatalf("failed to create test.tar archive: %v", err) 2893 } 2894 defer testTar.Close() 2895 2896 tw := tar.NewWriter(testTar) 2897 2898 if err := tw.WriteHeader(&tar.Header{ 2899 Name: "test/foo", 2900 Size: 2, 2901 }); err != nil { 2902 c.Fatalf("failed to write tar file header: %v", err) 2903 } 2904 if _, err := tw.Write([]byte("Hi")); err != nil { 2905 c.Fatalf("failed to write tar file content: %v", err) 2906 } 2907 if err := tw.Close(); err != nil { 2908 c.Fatalf("failed to close tar archive: %v", err) 2909 } 2910 2911 // Corrupt the tar by removing one byte off the end 2912 stat, err := testTar.Stat() 2913 if err != nil { 2914 c.Fatalf("failed to stat tar archive: %v", err) 2915 } 2916 if err := testTar.Truncate(stat.Size() - 1); err != nil { 2917 c.Fatalf("failed to truncate tar archive: %v", err) 2918 } 2919 2920 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2921 c.Fatalf("failed to open destination dockerfile: %v", err) 2922 } 2923 return fakecontext.New(c, tmpDir) 2924 }() 2925 defer ctx.Close() 2926 2927 buildImage(name, build.WithExternalBuildContext(ctx)).Assert(c, icmd.Expected{ 2928 ExitCode: 1, 2929 }) 2930 } 2931 2932 func (s *DockerSuite) TestBuildAddNonTar(c *testing.T) { 2933 name := "testbuildaddnontar" 2934 2935 // Should not try to extract test.tar 2936 buildImageSuccessfully(c, name, build.WithBuildContext(c, 2937 build.WithFile("Dockerfile", ` 2938 FROM busybox 2939 ADD test.tar / 2940 RUN test -f /test.tar`), 2941 build.WithFile("test.tar", "not_a_tar_file"), 2942 )) 2943 } 2944 2945 func (s *DockerSuite) TestBuildAddTarXz(c *testing.T) { 2946 // /test/foo is not owned by the correct user 2947 testRequires(c, NotUserNamespace) 2948 testRequires(c, DaemonIsLinux) 2949 name := "testbuildaddtarxz" 2950 2951 ctx := func() *fakecontext.Fake { 2952 dockerfile := ` 2953 FROM busybox 2954 ADD test.tar.xz / 2955 RUN cat /test/foo | grep Hi` 2956 tmpDir, err := ioutil.TempDir("", "fake-context") 2957 assert.NilError(c, err) 2958 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 2959 if err != nil { 2960 c.Fatalf("failed to create test.tar archive: %v", err) 2961 } 2962 defer testTar.Close() 2963 2964 tw := tar.NewWriter(testTar) 2965 2966 if err := tw.WriteHeader(&tar.Header{ 2967 Name: "test/foo", 2968 Size: 2, 2969 }); err != nil { 2970 c.Fatalf("failed to write tar file header: %v", err) 2971 } 2972 if _, err := tw.Write([]byte("Hi")); err != nil { 2973 c.Fatalf("failed to write tar file content: %v", err) 2974 } 2975 if err := tw.Close(); err != nil { 2976 c.Fatalf("failed to close tar archive: %v", err) 2977 } 2978 2979 icmd.RunCmd(icmd.Cmd{ 2980 Command: []string{"xz", "-k", "test.tar"}, 2981 Dir: tmpDir, 2982 }).Assert(c, icmd.Success) 2983 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 2984 c.Fatalf("failed to open destination dockerfile: %v", err) 2985 } 2986 return fakecontext.New(c, tmpDir) 2987 }() 2988 2989 defer ctx.Close() 2990 2991 buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx)) 2992 } 2993 2994 func (s *DockerSuite) TestBuildAddTarXzGz(c *testing.T) { 2995 testRequires(c, DaemonIsLinux) 2996 name := "testbuildaddtarxzgz" 2997 2998 ctx := func() *fakecontext.Fake { 2999 dockerfile := ` 3000 FROM busybox 3001 ADD test.tar.xz.gz / 3002 RUN ls /test.tar.xz.gz` 3003 tmpDir, err := ioutil.TempDir("", "fake-context") 3004 assert.NilError(c, err) 3005 testTar, err := os.Create(filepath.Join(tmpDir, "test.tar")) 3006 if err != nil { 3007 c.Fatalf("failed to create test.tar archive: %v", err) 3008 } 3009 defer testTar.Close() 3010 3011 tw := tar.NewWriter(testTar) 3012 3013 if err := tw.WriteHeader(&tar.Header{ 3014 Name: "test/foo", 3015 Size: 2, 3016 }); err != nil { 3017 c.Fatalf("failed to write tar file header: %v", err) 3018 } 3019 if _, err := tw.Write([]byte("Hi")); err != nil { 3020 c.Fatalf("failed to write tar file content: %v", err) 3021 } 3022 if err := tw.Close(); err != nil { 3023 c.Fatalf("failed to close tar archive: %v", err) 3024 } 3025 3026 icmd.RunCmd(icmd.Cmd{ 3027 Command: []string{"xz", "-k", "test.tar"}, 3028 Dir: tmpDir, 3029 }).Assert(c, icmd.Success) 3030 3031 icmd.RunCmd(icmd.Cmd{ 3032 Command: []string{"gzip", "test.tar.xz"}, 3033 Dir: tmpDir, 3034 }) 3035 if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { 3036 c.Fatalf("failed to open destination dockerfile: %v", err) 3037 } 3038 return fakecontext.New(c, tmpDir) 3039 }() 3040 3041 defer ctx.Close() 3042 3043 buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx)) 3044 } 3045 3046 // FIXME(vdemeester) most of the from git tests could be moved to `docker/cli` e2e tests 3047 func (s *DockerSuite) TestBuildFromGit(c *testing.T) { 3048 name := "testbuildfromgit" 3049 git := fakegit.New(c, "repo", map[string]string{ 3050 "Dockerfile": `FROM busybox 3051 ADD first /first 3052 RUN [ -f /first ] 3053 MAINTAINER docker`, 3054 "first": "test git data", 3055 }, true) 3056 defer git.Close() 3057 3058 buildImageSuccessfully(c, name, build.WithContextPath(git.RepoURL)) 3059 3060 res := inspectField(c, name, "Author") 3061 if res != "docker" { 3062 c.Fatalf("Maintainer should be docker, got %s", res) 3063 } 3064 } 3065 3066 func (s *DockerSuite) TestBuildFromGitWithContext(c *testing.T) { 3067 name := "testbuildfromgit" 3068 git := fakegit.New(c, "repo", map[string]string{ 3069 "docker/Dockerfile": `FROM busybox 3070 ADD first /first 3071 RUN [ -f /first ] 3072 MAINTAINER docker`, 3073 "docker/first": "test git data", 3074 }, true) 3075 defer git.Close() 3076 3077 buildImageSuccessfully(c, name, build.WithContextPath(fmt.Sprintf("%s#master:docker", git.RepoURL))) 3078 3079 res := inspectField(c, name, "Author") 3080 if res != "docker" { 3081 c.Fatalf("Maintainer should be docker, got %s", res) 3082 } 3083 } 3084 3085 func (s *DockerSuite) TestBuildFromGitWithF(c *testing.T) { 3086 name := "testbuildfromgitwithf" 3087 git := fakegit.New(c, "repo", map[string]string{ 3088 "myApp/myDockerfile": `FROM busybox 3089 RUN echo hi from Dockerfile`, 3090 }, true) 3091 defer git.Close() 3092 3093 buildImage(name, cli.WithFlags("-f", "myApp/myDockerfile"), build.WithContextPath(git.RepoURL)).Assert(c, icmd.Expected{ 3094 Out: "hi from Dockerfile", 3095 }) 3096 } 3097 3098 func (s *DockerSuite) TestBuildFromRemoteTarball(c *testing.T) { 3099 name := "testbuildfromremotetarball" 3100 3101 buffer := new(bytes.Buffer) 3102 tw := tar.NewWriter(buffer) 3103 defer tw.Close() 3104 3105 dockerfile := []byte(`FROM busybox 3106 MAINTAINER docker`) 3107 if err := tw.WriteHeader(&tar.Header{ 3108 Name: "Dockerfile", 3109 Size: int64(len(dockerfile)), 3110 }); err != nil { 3111 c.Fatalf("failed to write tar file header: %v", err) 3112 } 3113 if _, err := tw.Write(dockerfile); err != nil { 3114 c.Fatalf("failed to write tar file content: %v", err) 3115 } 3116 if err := tw.Close(); err != nil { 3117 c.Fatalf("failed to close tar archive: %v", err) 3118 } 3119 3120 server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{ 3121 "testT.tar": buffer, 3122 })) 3123 defer server.Close() 3124 3125 cli.BuildCmd(c, name, build.WithContextPath(server.URL()+"/testT.tar")) 3126 3127 res := inspectField(c, name, "Author") 3128 if res != "docker" { 3129 c.Fatalf("Maintainer should be docker, got %s", res) 3130 } 3131 } 3132 3133 func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *testing.T) { 3134 name := "testbuildcmdcleanuponentrypoint" 3135 3136 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 3137 CMD ["test"] 3138 ENTRYPOINT ["echo"]`)) 3139 buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`FROM %s 3140 ENTRYPOINT ["cat"]`, name))) 3141 3142 res := inspectField(c, name, "Config.Cmd") 3143 if res != "[]" { 3144 c.Fatalf("Cmd %s, expected nil", res) 3145 } 3146 res = inspectField(c, name, "Config.Entrypoint") 3147 if expected := "[cat]"; res != expected { 3148 c.Fatalf("Entrypoint %s, expected %s", res, expected) 3149 } 3150 } 3151 3152 func (s *DockerSuite) TestBuildClearCmd(c *testing.T) { 3153 name := "testbuildclearcmd" 3154 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 3155 ENTRYPOINT ["/bin/bash"] 3156 CMD []`)) 3157 3158 res := inspectFieldJSON(c, name, "Config.Cmd") 3159 if res != "[]" { 3160 c.Fatalf("Cmd %s, expected %s", res, "[]") 3161 } 3162 } 3163 3164 func (s *DockerSuite) TestBuildEmptyCmd(c *testing.T) { 3165 // Skip on Windows. Base image on Windows has a CMD set in the image. 3166 testRequires(c, DaemonIsLinux) 3167 3168 name := "testbuildemptycmd" 3169 buildImageSuccessfully(c, name, build.WithDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")) 3170 3171 res := inspectFieldJSON(c, name, "Config.Cmd") 3172 if res != "null" { 3173 c.Fatalf("Cmd %s, expected %s", res, "null") 3174 } 3175 } 3176 3177 func (s *DockerSuite) TestBuildOnBuildOutput(c *testing.T) { 3178 name := "testbuildonbuildparent" 3179 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nONBUILD RUN echo foo\n")) 3180 3181 buildImage(name, build.WithDockerfile("FROM "+name+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{ 3182 Out: "# Executing 1 build trigger", 3183 }) 3184 } 3185 3186 // FIXME(vdemeester) should be a unit test 3187 func (s *DockerSuite) TestBuildInvalidTag(c *testing.T) { 3188 name := "abcd:" + testutil.GenerateRandomAlphaOnlyString(200) 3189 buildImage(name, build.WithDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{ 3190 ExitCode: 125, 3191 Err: "invalid reference format", 3192 }) 3193 } 3194 3195 func (s *DockerSuite) TestBuildCmdShDashC(c *testing.T) { 3196 name := "testbuildcmdshc" 3197 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD echo cmd\n")) 3198 3199 res := inspectFieldJSON(c, name, "Config.Cmd") 3200 expected := `["/bin/sh","-c","echo cmd"]` 3201 if testEnv.OSType == "windows" { 3202 expected = `["cmd /S /C echo cmd"]` 3203 } 3204 if res != expected { 3205 c.Fatalf("Expected value %s not in Config.Cmd: %s", expected, res) 3206 } 3207 3208 } 3209 3210 func (s *DockerSuite) TestBuildCmdSpaces(c *testing.T) { 3211 // Test to make sure that when we strcat arrays we take into account 3212 // the arg separator to make sure ["echo","hi"] and ["echo hi"] don't 3213 // look the same 3214 name := "testbuildcmdspaces" 3215 3216 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD [\"echo hi\"]\n")) 3217 id1 := getIDByName(c, name) 3218 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD [\"echo\", \"hi\"]\n")) 3219 id2 := getIDByName(c, name) 3220 3221 if id1 == id2 { 3222 c.Fatal("Should not have resulted in the same CMD") 3223 } 3224 3225 // Now do the same with ENTRYPOINT 3226 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENTRYPOINT [\"echo hi\"]\n")) 3227 id1 = getIDByName(c, name) 3228 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENTRYPOINT [\"echo\", \"hi\"]\n")) 3229 id2 = getIDByName(c, name) 3230 3231 if id1 == id2 { 3232 c.Fatal("Should not have resulted in the same ENTRYPOINT") 3233 } 3234 } 3235 3236 func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *testing.T) { 3237 name := "testbuildcmdjson" 3238 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD [\"echo\", \"cmd\"]")) 3239 3240 res := inspectFieldJSON(c, name, "Config.Cmd") 3241 expected := `["echo","cmd"]` 3242 if res != expected { 3243 c.Fatalf("Expected value %s not in Config.Cmd: %s", expected, res) 3244 } 3245 } 3246 3247 func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChild(c *testing.T) { 3248 buildImageSuccessfully(c, "parent", build.WithDockerfile(` 3249 FROM busybox 3250 ENTRYPOINT exit 130 3251 `)) 3252 3253 icmd.RunCommand(dockerBinary, "run", "parent").Assert(c, icmd.Expected{ 3254 ExitCode: 130, 3255 }) 3256 3257 buildImageSuccessfully(c, "child", build.WithDockerfile(` 3258 FROM parent 3259 ENTRYPOINT exit 5 3260 `)) 3261 3262 icmd.RunCommand(dockerBinary, "run", "child").Assert(c, icmd.Expected{ 3263 ExitCode: 5, 3264 }) 3265 } 3266 3267 func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChildInspect(c *testing.T) { 3268 var ( 3269 name = "testbuildepinherit" 3270 name2 = "testbuildepinherit2" 3271 expected = `["/bin/sh","-c","echo quux"]` 3272 ) 3273 3274 if testEnv.OSType == "windows" { 3275 expected = `["cmd /S /C echo quux"]` 3276 } 3277 3278 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENTRYPOINT /foo/bar")) 3279 buildImageSuccessfully(c, name2, build.WithDockerfile(fmt.Sprintf("FROM %s\nENTRYPOINT echo quux", name))) 3280 3281 res := inspectFieldJSON(c, name2, "Config.Entrypoint") 3282 if res != expected { 3283 c.Fatalf("Expected value %s not in Config.Entrypoint: %s", expected, res) 3284 } 3285 3286 icmd.RunCommand(dockerBinary, "run", name2).Assert(c, icmd.Expected{ 3287 Out: "quux", 3288 }) 3289 } 3290 3291 func (s *DockerSuite) TestBuildRunShEntrypoint(c *testing.T) { 3292 name := "testbuildentrypoint" 3293 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3294 ENTRYPOINT echo`)) 3295 dockerCmd(c, "run", "--rm", name) 3296 } 3297 3298 func (s *DockerSuite) TestBuildExoticShellInterpolation(c *testing.T) { 3299 testRequires(c, DaemonIsLinux) 3300 name := "testbuildexoticshellinterpolation" 3301 3302 buildImageSuccessfully(c, name, build.WithDockerfile(` 3303 FROM busybox 3304 3305 ENV SOME_VAR a.b.c 3306 3307 RUN [ "$SOME_VAR" = 'a.b.c' ] 3308 RUN [ "${SOME_VAR}" = 'a.b.c' ] 3309 RUN [ "${SOME_VAR%.*}" = 'a.b' ] 3310 RUN [ "${SOME_VAR%%.*}" = 'a' ] 3311 RUN [ "${SOME_VAR#*.}" = 'b.c' ] 3312 RUN [ "${SOME_VAR##*.}" = 'c' ] 3313 RUN [ "${SOME_VAR/c/d}" = 'a.b.d' ] 3314 RUN [ "${#SOME_VAR}" = '5' ] 3315 3316 RUN [ "${SOME_UNSET_VAR:-$SOME_VAR}" = 'a.b.c' ] 3317 RUN [ "${SOME_VAR:+Version: ${SOME_VAR}}" = 'Version: a.b.c' ] 3318 RUN [ "${SOME_UNSET_VAR:+${SOME_VAR}}" = '' ] 3319 RUN [ "${SOME_UNSET_VAR:-${SOME_VAR:-d.e.f}}" = 'a.b.c' ] 3320 `)) 3321 } 3322 3323 func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *testing.T) { 3324 // This testcase is supposed to generate an error because the 3325 // JSON array we're passing in on the CMD uses single quotes instead 3326 // of double quotes (per the JSON spec). This means we interpret it 3327 // as a "string" instead of "JSON array" and pass it on to "sh -c" and 3328 // it should barf on it. 3329 name := "testbuildsinglequotefails" 3330 expectedExitCode := 2 3331 3332 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3333 CMD [ '/bin/sh', '-c', 'echo hi' ]`)) 3334 3335 icmd.RunCommand(dockerBinary, "run", "--rm", name).Assert(c, icmd.Expected{ 3336 ExitCode: expectedExitCode, 3337 }) 3338 } 3339 3340 func (s *DockerSuite) TestBuildVerboseOut(c *testing.T) { 3341 name := "testbuildverboseout" 3342 expected := "\n123\n" 3343 3344 if testEnv.OSType == "windows" { 3345 expected = "\n123\r\n" 3346 } 3347 3348 buildImage(name, build.WithDockerfile(`FROM busybox 3349 RUN echo 123`)).Assert(c, icmd.Expected{ 3350 Out: expected, 3351 }) 3352 } 3353 3354 func (s *DockerSuite) TestBuildWithTabs(c *testing.T) { 3355 name := "testbuildwithtabs" 3356 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nRUN echo\tone\t\ttwo")) 3357 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 3358 expected1 := `["/bin/sh","-c","echo\tone\t\ttwo"]` 3359 expected2 := `["/bin/sh","-c","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3360 if testEnv.OSType == "windows" { 3361 expected1 = `["cmd /S /C echo\tone\t\ttwo"]` 3362 expected2 = `["cmd /S /C echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3363 } 3364 if res != expected1 && res != expected2 { 3365 c.Fatalf("Missing tabs.\nGot: %s\nExp: %s or %s", res, expected1, expected2) 3366 } 3367 } 3368 3369 func (s *DockerSuite) TestBuildLabels(c *testing.T) { 3370 name := "testbuildlabel" 3371 expected := `{"License":"GPL","Vendor":"Acme"}` 3372 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3373 LABEL Vendor=Acme 3374 LABEL License GPL`)) 3375 res := inspectFieldJSON(c, name, "Config.Labels") 3376 if res != expected { 3377 c.Fatalf("Labels %s, expected %s", res, expected) 3378 } 3379 } 3380 3381 func (s *DockerSuite) TestBuildLabelsCache(c *testing.T) { 3382 name := "testbuildlabelcache" 3383 3384 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3385 LABEL Vendor=Acme`)) 3386 id1 := getIDByName(c, name) 3387 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3388 LABEL Vendor=Acme`)) 3389 id2 := getIDByName(c, name) 3390 if id1 != id2 { 3391 c.Fatalf("Build 2 should have worked & used cache(%s,%s)", id1, id2) 3392 } 3393 3394 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3395 LABEL Vendor=Acme1`)) 3396 id2 = getIDByName(c, name) 3397 if id1 == id2 { 3398 c.Fatalf("Build 3 should have worked & NOT used cache(%s,%s)", id1, id2) 3399 } 3400 3401 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3402 LABEL Vendor Acme`)) 3403 id2 = getIDByName(c, name) 3404 if id1 != id2 { 3405 c.Fatalf("Build 4 should have worked & used cache(%s,%s)", id1, id2) 3406 } 3407 3408 // Now make sure the cache isn't used by mistake 3409 buildImageSuccessfully(c, name, build.WithoutCache, build.WithDockerfile(`FROM busybox 3410 LABEL f1=b1 f2=b2`)) 3411 3412 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3413 LABEL f1=b1 f2=b2`)) 3414 id2 = getIDByName(c, name) 3415 if id1 == id2 { 3416 c.Fatalf("Build 6 should have worked & NOT used the cache(%s,%s)", id1, id2) 3417 } 3418 3419 } 3420 3421 // FIXME(vdemeester) port to docker/cli e2e tests (api tests should test suppressOutput option though) 3422 func (s *DockerSuite) TestBuildNotVerboseSuccess(c *testing.T) { 3423 // This test makes sure that -q works correctly when build is successful: 3424 // stdout has only the image ID (long image ID) and stderr is empty. 3425 outRegexp := regexp.MustCompile(`^(sha256:|)[a-z0-9]{64}\n$`) 3426 buildFlags := cli.WithFlags("-q") 3427 3428 tt := []struct { 3429 Name string 3430 BuildFunc func(string) *icmd.Result 3431 }{ 3432 { 3433 Name: "quiet_build_stdin_success", 3434 BuildFunc: func(name string) *icmd.Result { 3435 return buildImage(name, buildFlags, build.WithDockerfile("FROM busybox")) 3436 }, 3437 }, 3438 { 3439 Name: "quiet_build_ctx_success", 3440 BuildFunc: func(name string) *icmd.Result { 3441 return buildImage(name, buildFlags, build.WithBuildContext(c, 3442 build.WithFile("Dockerfile", "FROM busybox"), 3443 build.WithFile("quiet_build_success_fctx", "test"), 3444 )) 3445 }, 3446 }, 3447 { 3448 Name: "quiet_build_git_success", 3449 BuildFunc: func(name string) *icmd.Result { 3450 git := fakegit.New(c, "repo", map[string]string{ 3451 "Dockerfile": "FROM busybox", 3452 }, true) 3453 return buildImage(name, buildFlags, build.WithContextPath(git.RepoURL)) 3454 }, 3455 }, 3456 } 3457 3458 for _, te := range tt { 3459 result := te.BuildFunc(te.Name) 3460 result.Assert(c, icmd.Success) 3461 if outRegexp.Find([]byte(result.Stdout())) == nil { 3462 c.Fatalf("Test %s expected stdout to match the [%v] regexp, but it is [%v]", te.Name, outRegexp, result.Stdout()) 3463 } 3464 3465 if result.Stderr() != "" { 3466 c.Fatalf("Test %s expected stderr to be empty, but it is [%#v]", te.Name, result.Stderr()) 3467 } 3468 } 3469 3470 } 3471 3472 // FIXME(vdemeester) migrate to docker/cli tests 3473 func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *testing.T) { 3474 // This test makes sure that -q works correctly when build fails by 3475 // comparing between the stderr output in quiet mode and in stdout 3476 // and stderr output in verbose mode 3477 testRequires(c, Network) 3478 testName := "quiet_build_not_exists_image" 3479 dockerfile := "FROM busybox11" 3480 quietResult := buildImage(testName, cli.WithFlags("-q"), build.WithDockerfile(dockerfile)) 3481 quietResult.Assert(c, icmd.Expected{ 3482 ExitCode: 1, 3483 }) 3484 result := buildImage(testName, build.WithDockerfile(dockerfile)) 3485 result.Assert(c, icmd.Expected{ 3486 ExitCode: 1, 3487 }) 3488 if quietResult.Stderr() != result.Combined() { 3489 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", testName, quietResult.Stderr(), result.Combined())) 3490 } 3491 } 3492 3493 // FIXME(vdemeester) migrate to docker/cli tests 3494 func (s *DockerSuite) TestBuildNotVerboseFailure(c *testing.T) { 3495 // This test makes sure that -q works correctly when build fails by 3496 // comparing between the stderr output in quiet mode and in stdout 3497 // and stderr output in verbose mode 3498 testCases := []struct { 3499 testName string 3500 dockerfile string 3501 }{ 3502 {"quiet_build_no_from_at_the_beginning", "RUN whoami"}, 3503 {"quiet_build_unknown_instr", "FROMD busybox"}, 3504 } 3505 3506 for _, tc := range testCases { 3507 quietResult := buildImage(tc.testName, cli.WithFlags("-q"), build.WithDockerfile(tc.dockerfile)) 3508 quietResult.Assert(c, icmd.Expected{ 3509 ExitCode: 1, 3510 }) 3511 result := buildImage(tc.testName, build.WithDockerfile(tc.dockerfile)) 3512 result.Assert(c, icmd.Expected{ 3513 ExitCode: 1, 3514 }) 3515 if quietResult.Stderr() != result.Combined() { 3516 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", tc.testName, quietResult.Stderr(), result.Combined())) 3517 } 3518 } 3519 } 3520 3521 // FIXME(vdemeester) migrate to docker/cli tests 3522 func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *testing.T) { 3523 // This test ensures that when given a wrong URL, stderr in quiet mode and 3524 // stderr in verbose mode are identical. 3525 // TODO(vdemeester) with cobra, stdout has a carriage return too much so this test should not check stdout 3526 URL := "http://something.invalid" 3527 name := "quiet_build_wrong_remote" 3528 quietResult := buildImage(name, cli.WithFlags("-q"), build.WithContextPath(URL)) 3529 quietResult.Assert(c, icmd.Expected{ 3530 ExitCode: 1, 3531 }) 3532 result := buildImage(name, build.WithContextPath(URL)) 3533 result.Assert(c, icmd.Expected{ 3534 ExitCode: 1, 3535 }) 3536 3537 // An error message should contain name server IP and port, like this: 3538 // "dial tcp: lookup something.invalid on 172.29.128.11:53: no such host" 3539 // The IP:port need to be removed in order to not trigger a test failur 3540 // when more than one nameserver is configured. 3541 // While at it, also strip excessive newlines. 3542 normalize := func(msg string) string { 3543 return strings.TrimSpace(regexp.MustCompile("[1-9][0-9.]+:[0-9]+").ReplaceAllLiteralString(msg, "<ip:port>")) 3544 } 3545 3546 if normalize(quietResult.Stderr()) != normalize(result.Combined()) { 3547 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", name, quietResult.Stderr(), result.Combined())) 3548 } 3549 } 3550 3551 // FIXME(vdemeester) migrate to docker/cli tests 3552 func (s *DockerSuite) TestBuildStderr(c *testing.T) { 3553 // This test just makes sure that no non-error output goes 3554 // to stderr 3555 name := "testbuildstderr" 3556 result := buildImage(name, build.WithDockerfile("FROM busybox\nRUN echo one")) 3557 result.Assert(c, icmd.Success) 3558 3559 // Windows to non-Windows should have a security warning 3560 if runtime.GOOS == "windows" && testEnv.OSType != "windows" && !strings.Contains(result.Stdout(), "SECURITY WARNING:") { 3561 c.Fatalf("Stdout contains unexpected output: %q", result.Stdout()) 3562 } 3563 3564 // Stderr should always be empty 3565 if result.Stderr() != "" { 3566 c.Fatalf("Stderr should have been empty, instead it's: %q", result.Stderr()) 3567 } 3568 } 3569 3570 func (s *DockerSuite) TestBuildChownSingleFile(c *testing.T) { 3571 testRequires(c, UnixCli, DaemonIsLinux) // test uses chown: not available on windows 3572 3573 name := "testbuildchownsinglefile" 3574 3575 ctx := fakecontext.New(c, "", 3576 fakecontext.WithDockerfile(` 3577 FROM busybox 3578 COPY test / 3579 RUN ls -l /test 3580 RUN [ $(ls -l /test | awk '{print $3":"$4}') = 'root:root' ] 3581 `), 3582 fakecontext.WithFiles(map[string]string{ 3583 "test": "test", 3584 })) 3585 defer ctx.Close() 3586 3587 if err := os.Chown(filepath.Join(ctx.Dir, "test"), 4242, 4242); err != nil { 3588 c.Fatal(err) 3589 } 3590 3591 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 3592 } 3593 3594 func (s *DockerSuite) TestBuildSymlinkBreakout(c *testing.T) { 3595 name := "testbuildsymlinkbreakout" 3596 tmpdir, err := ioutil.TempDir("", name) 3597 assert.NilError(c, err) 3598 3599 // See https://github.com/moby/moby/pull/37770 for reason for next line. 3600 tmpdir, err = system.GetLongPathName(tmpdir) 3601 assert.NilError(c, err) 3602 3603 defer os.RemoveAll(tmpdir) 3604 ctx := filepath.Join(tmpdir, "context") 3605 if err := os.MkdirAll(ctx, 0755); err != nil { 3606 c.Fatal(err) 3607 } 3608 if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(` 3609 from busybox 3610 add symlink.tar / 3611 add inject /symlink/ 3612 `), 0644); err != nil { 3613 c.Fatal(err) 3614 } 3615 inject := filepath.Join(ctx, "inject") 3616 if err := ioutil.WriteFile(inject, nil, 0644); err != nil { 3617 c.Fatal(err) 3618 } 3619 f, err := os.Create(filepath.Join(ctx, "symlink.tar")) 3620 if err != nil { 3621 c.Fatal(err) 3622 } 3623 w := tar.NewWriter(f) 3624 w.WriteHeader(&tar.Header{ 3625 Name: "symlink2", 3626 Typeflag: tar.TypeSymlink, 3627 Linkname: "/../../../../../../../../../../../../../../", 3628 Uid: os.Getuid(), 3629 Gid: os.Getgid(), 3630 }) 3631 w.WriteHeader(&tar.Header{ 3632 Name: "symlink", 3633 Typeflag: tar.TypeSymlink, 3634 Linkname: filepath.Join("symlink2", tmpdir), 3635 Uid: os.Getuid(), 3636 Gid: os.Getgid(), 3637 }) 3638 w.Close() 3639 f.Close() 3640 3641 buildImageSuccessfully(c, name, build.WithoutCache, build.WithExternalBuildContext(fakecontext.New(c, ctx))) 3642 if _, err := os.Lstat(filepath.Join(tmpdir, "inject")); err == nil { 3643 c.Fatal("symlink breakout - inject") 3644 } else if !os.IsNotExist(err) { 3645 c.Fatalf("unexpected error: %v", err) 3646 } 3647 } 3648 3649 func (s *DockerSuite) TestBuildXZHost(c *testing.T) { 3650 // /usr/local/sbin/xz gets permission denied for the user 3651 testRequires(c, NotUserNamespace) 3652 testRequires(c, DaemonIsLinux) 3653 name := "testbuildxzhost" 3654 3655 buildImageSuccessfully(c, name, build.WithBuildContext(c, 3656 build.WithFile("Dockerfile", ` 3657 FROM busybox 3658 ADD xz /usr/local/sbin/ 3659 RUN chmod 755 /usr/local/sbin/xz 3660 ADD test.xz / 3661 RUN [ ! -e /injected ]`), 3662 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"), 3663 build.WithFile("xz", "#!/bin/sh\ntouch /injected"), 3664 )) 3665 } 3666 3667 func (s *DockerSuite) TestBuildVolumesRetainContents(c *testing.T) { 3668 // /foo/file gets permission denied for the user 3669 testRequires(c, NotUserNamespace) 3670 testRequires(c, DaemonIsLinux) // TODO Windows: Issue #20127 3671 var ( 3672 name = "testbuildvolumescontent" 3673 expected = "some text" 3674 volName = "/foo" 3675 ) 3676 3677 if testEnv.OSType == "windows" { 3678 volName = "C:/foo" 3679 } 3680 3681 buildImageSuccessfully(c, name, build.WithBuildContext(c, 3682 build.WithFile("Dockerfile", ` 3683 FROM busybox 3684 COPY content /foo/file 3685 VOLUME `+volName+` 3686 CMD cat /foo/file`), 3687 build.WithFile("content", expected), 3688 )) 3689 3690 out, _ := dockerCmd(c, "run", "--rm", name) 3691 if out != expected { 3692 c.Fatalf("expected file contents for /foo/file to be %q but received %q", expected, out) 3693 } 3694 3695 } 3696 3697 func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *testing.T) { 3698 testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows 3699 testRequires(c, DaemonIsLinux) 3700 3701 // If Dockerfile is not present, use dockerfile 3702 buildImage("test1", build.WithBuildContext(c, 3703 build.WithFile("dockerfile", `FROM busybox 3704 RUN echo from dockerfile`), 3705 )).Assert(c, icmd.Expected{ 3706 Out: "from dockerfile", 3707 }) 3708 3709 // Prefer Dockerfile in place of dockerfile 3710 buildImage("test1", build.WithBuildContext(c, 3711 build.WithFile("dockerfile", `FROM busybox 3712 RUN echo from dockerfile`), 3713 build.WithFile("Dockerfile", `FROM busybox 3714 RUN echo from Dockerfile`), 3715 )).Assert(c, icmd.Expected{ 3716 Out: "from Dockerfile", 3717 }) 3718 } 3719 3720 // FIXME(vdemeester) should migrate to docker/cli tests 3721 func (s *DockerSuite) TestBuildFromURLWithF(c *testing.T) { 3722 server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"baz": `FROM busybox 3723 RUN echo from baz 3724 COPY * /tmp/ 3725 RUN find /tmp/`})) 3726 defer server.Close() 3727 3728 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox 3729 RUN echo from Dockerfile`)) 3730 defer ctx.Close() 3731 3732 // Make sure that -f is ignored and that we don't use the Dockerfile 3733 // that's in the current dir 3734 result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", server.URL()+"/baz"), func(cmd *icmd.Cmd) func() { 3735 cmd.Dir = ctx.Dir 3736 return nil 3737 }) 3738 3739 if !strings.Contains(result.Combined(), "from baz") || 3740 strings.Contains(result.Combined(), "/tmp/baz") || 3741 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3742 c.Fatalf("Missing proper output: %s", result.Combined()) 3743 } 3744 3745 } 3746 3747 // FIXME(vdemeester) should migrate to docker/cli tests 3748 func (s *DockerSuite) TestBuildFromStdinWithF(c *testing.T) { 3749 testRequires(c, DaemonIsLinux) // TODO Windows: This test is flaky; no idea why 3750 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox 3751 RUN echo "from Dockerfile"`)) 3752 defer ctx.Close() 3753 3754 // Make sure that -f is ignored and that we don't use the Dockerfile 3755 // that's in the current dir 3756 result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", "-"), func(cmd *icmd.Cmd) func() { 3757 cmd.Dir = ctx.Dir 3758 cmd.Stdin = strings.NewReader(`FROM busybox 3759 RUN echo "from baz" 3760 COPY * /tmp/ 3761 RUN sh -c "find /tmp/" # sh -c is needed on Windows to use the correct find`) 3762 return nil 3763 }) 3764 3765 if !strings.Contains(result.Combined(), "from baz") || 3766 strings.Contains(result.Combined(), "/tmp/baz") || 3767 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3768 c.Fatalf("Missing proper output: %s", result.Combined()) 3769 } 3770 3771 } 3772 3773 func (s *DockerSuite) TestBuildFromOfficialNames(c *testing.T) { 3774 name := "testbuildfromofficial" 3775 fromNames := []string{ 3776 "busybox", 3777 "docker.io/busybox", 3778 "index.docker.io/busybox", 3779 "library/busybox", 3780 "docker.io/library/busybox", 3781 "index.docker.io/library/busybox", 3782 } 3783 for idx, fromName := range fromNames { 3784 imgName := fmt.Sprintf("%s%d", name, idx) 3785 buildImageSuccessfully(c, imgName, build.WithDockerfile("FROM "+fromName)) 3786 dockerCmd(c, "rmi", imgName) 3787 } 3788 } 3789 3790 // FIXME(vdemeester) should be a unit test 3791 func (s *DockerSuite) TestBuildSpaces(c *testing.T) { 3792 // Test to make sure that leading/trailing spaces on a command 3793 // doesn't change the error msg we get 3794 name := "testspaces" 3795 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile("FROM busybox\nCOPY\n")) 3796 defer ctx.Close() 3797 3798 result1 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx)) 3799 result1.Assert(c, icmd.Expected{ 3800 ExitCode: 1, 3801 }) 3802 3803 ctx.Add("Dockerfile", "FROM busybox\nCOPY ") 3804 result2 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx)) 3805 result2.Assert(c, icmd.Expected{ 3806 ExitCode: 1, 3807 }) 3808 3809 removeLogTimestamps := func(s string) string { 3810 return regexp.MustCompile(`time="(.*?)"`).ReplaceAllString(s, `time=[TIMESTAMP]`) 3811 } 3812 3813 // Skip over the times 3814 e1 := removeLogTimestamps(result1.Error.Error()) 3815 e2 := removeLogTimestamps(result2.Error.Error()) 3816 3817 // Ignore whitespace since that's what were verifying doesn't change stuff 3818 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3819 c.Fatalf("Build 2's error wasn't the same as build 1's\n1:%s\n2:%s", result1.Error, result2.Error) 3820 } 3821 3822 ctx.Add("Dockerfile", "FROM busybox\n COPY") 3823 result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx)) 3824 result2.Assert(c, icmd.Expected{ 3825 ExitCode: 1, 3826 }) 3827 3828 // Skip over the times 3829 e1 = removeLogTimestamps(result1.Error.Error()) 3830 e2 = removeLogTimestamps(result2.Error.Error()) 3831 3832 // Ignore whitespace since that's what were verifying doesn't change stuff 3833 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3834 c.Fatalf("Build 3's error wasn't the same as build 1's\n1:%s\n3:%s", result1.Error, result2.Error) 3835 } 3836 3837 ctx.Add("Dockerfile", "FROM busybox\n COPY ") 3838 result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx)) 3839 result2.Assert(c, icmd.Expected{ 3840 ExitCode: 1, 3841 }) 3842 3843 // Skip over the times 3844 e1 = removeLogTimestamps(result1.Error.Error()) 3845 e2 = removeLogTimestamps(result2.Error.Error()) 3846 3847 // Ignore whitespace since that's what were verifying doesn't change stuff 3848 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3849 c.Fatalf("Build 4's error wasn't the same as build 1's\n1:%s\n4:%s", result1.Error, result2.Error) 3850 } 3851 3852 } 3853 3854 func (s *DockerSuite) TestBuildSpacesWithQuotes(c *testing.T) { 3855 // Test to make sure that spaces in quotes aren't lost 3856 name := "testspacesquotes" 3857 3858 dockerfile := `FROM busybox 3859 RUN echo " \ 3860 foo "` 3861 3862 expected := "\n foo \n" 3863 // Windows uses the builtin echo, which preserves quotes 3864 if testEnv.OSType == "windows" { 3865 expected = "\" foo \"" 3866 } 3867 3868 buildImage(name, build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3869 Out: expected, 3870 }) 3871 } 3872 3873 // #4393 3874 func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *testing.T) { 3875 testRequires(c, DaemonIsLinux) // TODO Windows: This should error out 3876 buildImage("docker-test-errcreatevolumewithfile", build.WithDockerfile(` 3877 FROM busybox 3878 RUN touch /foo 3879 VOLUME /foo 3880 `)).Assert(c, icmd.Expected{ 3881 ExitCode: 1, 3882 Err: "file exists", 3883 }) 3884 } 3885 3886 // FIXME(vdemeester) should be a unit test 3887 func (s *DockerSuite) TestBuildMissingArgs(c *testing.T) { 3888 // Test to make sure that all Dockerfile commands (except the ones listed 3889 // in skipCmds) will generate an error if no args are provided. 3890 // Note: INSERT is deprecated so we exclude it because of that. 3891 skipCmds := map[string]struct{}{ 3892 "CMD": {}, 3893 "RUN": {}, 3894 "ENTRYPOINT": {}, 3895 "INSERT": {}, 3896 } 3897 3898 if testEnv.OSType == "windows" { 3899 skipCmds = map[string]struct{}{ 3900 "CMD": {}, 3901 "RUN": {}, 3902 "ENTRYPOINT": {}, 3903 "INSERT": {}, 3904 "STOPSIGNAL": {}, 3905 "ARG": {}, 3906 "USER": {}, 3907 "EXPOSE": {}, 3908 } 3909 } 3910 3911 for cmd := range command.Commands { 3912 cmd = strings.ToUpper(cmd) 3913 if _, ok := skipCmds[cmd]; ok { 3914 continue 3915 } 3916 var dockerfile string 3917 if cmd == "FROM" { 3918 dockerfile = cmd 3919 } else { 3920 // Add FROM to make sure we don't complain about it missing 3921 dockerfile = "FROM busybox\n" + cmd 3922 } 3923 3924 buildImage("args", build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3925 ExitCode: 1, 3926 Err: cmd + " requires", 3927 }) 3928 } 3929 3930 } 3931 3932 func (s *DockerSuite) TestBuildEmptyScratch(c *testing.T) { 3933 testRequires(c, DaemonIsLinux) 3934 buildImage("sc", build.WithDockerfile("FROM scratch")).Assert(c, icmd.Expected{ 3935 ExitCode: 1, 3936 Err: "No image was generated", 3937 }) 3938 } 3939 3940 func (s *DockerSuite) TestBuildDotDotFile(c *testing.T) { 3941 buildImageSuccessfully(c, "sc", build.WithBuildContext(c, 3942 build.WithFile("Dockerfile", "FROM busybox\n"), 3943 build.WithFile("..gitme", ""), 3944 )) 3945 } 3946 3947 func (s *DockerSuite) TestBuildRUNoneJSON(c *testing.T) { 3948 testRequires(c, DaemonIsLinux) // No hello-world Windows image 3949 name := "testbuildrunonejson" 3950 3951 buildImage(name, build.WithDockerfile(`FROM hello-world:frozen 3952 RUN [ "/hello" ]`)).Assert(c, icmd.Expected{ 3953 Out: "Hello from Docker", 3954 }) 3955 } 3956 3957 func (s *DockerSuite) TestBuildEmptyStringVolume(c *testing.T) { 3958 name := "testbuildemptystringvolume" 3959 3960 buildImage(name, build.WithDockerfile(` 3961 FROM busybox 3962 ENV foo="" 3963 VOLUME $foo 3964 `)).Assert(c, icmd.Expected{ 3965 ExitCode: 1, 3966 }) 3967 } 3968 3969 func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *testing.T) { 3970 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 3971 3972 cgroupParent := "test" 3973 data, err := ioutil.ReadFile("/proc/self/cgroup") 3974 if err != nil { 3975 c.Fatalf("failed to read '/proc/self/cgroup - %v", err) 3976 } 3977 selfCgroupPaths := ParseCgroupPaths(string(data)) 3978 _, found := selfCgroupPaths["memory"] 3979 if !found { 3980 c.Fatalf("unable to find self memory cgroup path. CgroupsPath: %v", selfCgroupPaths) 3981 } 3982 result := buildImage("buildcgroupparent", 3983 cli.WithFlags("--cgroup-parent", cgroupParent), 3984 build.WithDockerfile(` 3985 FROM busybox 3986 RUN cat /proc/self/cgroup 3987 `)) 3988 result.Assert(c, icmd.Success) 3989 m, err := regexp.MatchString(fmt.Sprintf("memory:.*/%s/.*", cgroupParent), result.Combined()) 3990 assert.NilError(c, err) 3991 if !m { 3992 c.Fatalf("There is no expected memory cgroup with parent /%s/: %s", cgroupParent, result.Combined()) 3993 } 3994 } 3995 3996 // FIXME(vdemeester) could be a unit test 3997 func (s *DockerSuite) TestBuildNoDupOutput(c *testing.T) { 3998 // Check to make sure our build output prints the Dockerfile cmd 3999 // property - there was a bug that caused it to be duplicated on the 4000 // Step X line 4001 name := "testbuildnodupoutput" 4002 result := buildImage(name, build.WithDockerfile(` 4003 FROM busybox 4004 RUN env`)) 4005 result.Assert(c, icmd.Success) 4006 exp := "\nStep 2/2 : RUN env\n" 4007 if !strings.Contains(result.Combined(), exp) { 4008 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4009 } 4010 } 4011 4012 // GH15826 4013 // FIXME(vdemeester) could be a unit test 4014 func (s *DockerSuite) TestBuildStartsFromOne(c *testing.T) { 4015 // Explicit check to ensure that build starts from step 1 rather than 0 4016 name := "testbuildstartsfromone" 4017 result := buildImage(name, build.WithDockerfile(`FROM busybox`)) 4018 result.Assert(c, icmd.Success) 4019 exp := "\nStep 1/1 : FROM busybox\n" 4020 if !strings.Contains(result.Combined(), exp) { 4021 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4022 } 4023 } 4024 4025 func (s *DockerSuite) TestBuildRUNErrMsg(c *testing.T) { 4026 // Test to make sure the bad command is quoted with just "s and 4027 // not as a Go []string 4028 name := "testbuildbadrunerrmsg" 4029 shell := "/bin/sh -c" 4030 exitCode := 127 4031 if testEnv.OSType == "windows" { 4032 shell = "cmd /S /C" 4033 // architectural - Windows has to start the container to determine the exe is bad, Linux does not 4034 exitCode = 1 4035 } 4036 exp := fmt.Sprintf(`The command '%s badEXE a1 \& a2 a3' returned a non-zero code: %d`, shell, exitCode) 4037 4038 buildImage(name, build.WithDockerfile(` 4039 FROM busybox 4040 RUN badEXE a1 \& a2 a3`)).Assert(c, icmd.Expected{ 4041 ExitCode: exitCode, 4042 Err: exp, 4043 }) 4044 } 4045 4046 // Issue #15634: COPY fails when path starts with "null" 4047 func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *testing.T) { 4048 name := "testbuildnullstringinaddcopyvolume" 4049 volName := "nullvolume" 4050 if testEnv.OSType == "windows" { 4051 volName = `C:\\nullvolume` 4052 } 4053 4054 buildImageSuccessfully(c, name, build.WithBuildContext(c, 4055 build.WithFile("Dockerfile", ` 4056 FROM busybox 4057 4058 ADD null / 4059 COPY nullfile / 4060 VOLUME `+volName+` 4061 `), 4062 build.WithFile("null", "test1"), 4063 build.WithFile("nullfile", "test2"), 4064 )) 4065 } 4066 4067 func (s *DockerSuite) TestBuildStopSignal(c *testing.T) { 4068 testRequires(c, DaemonIsLinux) // Windows does not support STOPSIGNAL yet 4069 imgName := "test_build_stop_signal" 4070 buildImageSuccessfully(c, imgName, build.WithDockerfile(`FROM busybox 4071 STOPSIGNAL SIGKILL`)) 4072 res := inspectFieldJSON(c, imgName, "Config.StopSignal") 4073 if res != `"SIGKILL"` { 4074 c.Fatalf("Signal %s, expected SIGKILL", res) 4075 } 4076 4077 containerName := "test-container-stop-signal" 4078 dockerCmd(c, "run", "-d", "--name", containerName, imgName, "top") 4079 res = inspectFieldJSON(c, containerName, "Config.StopSignal") 4080 if res != `"SIGKILL"` { 4081 c.Fatalf("Signal %s, expected SIGKILL", res) 4082 } 4083 } 4084 4085 func (s *DockerSuite) TestBuildBuildTimeArg(c *testing.T) { 4086 imgName := "bldargtest" 4087 envKey := "foo" 4088 envVal := "bar" 4089 var dockerfile string 4090 if testEnv.OSType == "windows" { 4091 // Bugs in Windows busybox port - use the default base image and native cmd stuff 4092 dockerfile = fmt.Sprintf(`FROM `+minimalBaseImage()+` 4093 ARG %s 4094 RUN echo %%%s%% 4095 CMD setlocal enableextensions && if defined %s (echo %%%s%%)`, envKey, envKey, envKey, envKey) 4096 } else { 4097 dockerfile = fmt.Sprintf(`FROM busybox 4098 ARG %s 4099 RUN echo $%s 4100 CMD echo $%s`, envKey, envKey, envKey) 4101 4102 } 4103 buildImage(imgName, 4104 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4105 build.WithDockerfile(dockerfile), 4106 ).Assert(c, icmd.Expected{ 4107 Out: envVal, 4108 }) 4109 4110 containerName := "bldargCont" 4111 out, _ := dockerCmd(c, "run", "--name", containerName, imgName) 4112 out = strings.Trim(out, " \r\n'") 4113 if out != "" { 4114 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4115 } 4116 } 4117 4118 func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *testing.T) { 4119 imgName := "bldargtest" 4120 envKey := "foo" 4121 envVal := "bar" 4122 envDef := "bar1" 4123 dockerfile := fmt.Sprintf(`FROM busybox 4124 ARG %s=%s`, envKey, envDef) 4125 buildImage(imgName, 4126 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4127 build.WithDockerfile(dockerfile), 4128 ).Assert(c, icmd.Expected{ 4129 Out: envVal, 4130 }) 4131 4132 out, _ := dockerCmd(c, "history", "--no-trunc", imgName) 4133 outputTabs := strings.Split(out, "\n")[1] 4134 if !strings.Contains(outputTabs, envDef) { 4135 c.Fatalf("failed to find arg default in image history output: %q expected: %q", outputTabs, envDef) 4136 } 4137 } 4138 4139 func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *testing.T) { 4140 imgName := "bldargtest" 4141 envKey := "foo" 4142 envVal := "bar" 4143 proxy := "HTTP_PROXY=http://user:password@proxy.example.com" 4144 explicitProxyKey := "http_proxy" 4145 explicitProxyVal := "http://user:password@someproxy.example.com" 4146 dockerfile := fmt.Sprintf(`FROM busybox 4147 ARG %s 4148 ARG %s 4149 RUN echo "Testing Build Args!"`, envKey, explicitProxyKey) 4150 4151 buildImage := func(imgName string) string { 4152 cli.BuildCmd(c, imgName, 4153 cli.WithFlags("--build-arg", "https_proxy=https://proxy.example.com", 4154 "--build-arg", fmt.Sprintf("%s=%s", envKey, envVal), 4155 "--build-arg", fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal), 4156 "--build-arg", proxy), 4157 build.WithDockerfile(dockerfile), 4158 ) 4159 return getIDByName(c, imgName) 4160 } 4161 4162 origID := buildImage(imgName) 4163 result := cli.DockerCmd(c, "history", "--no-trunc", imgName) 4164 out := result.Stdout() 4165 4166 if strings.Contains(out, proxy) { 4167 c.Fatalf("failed to exclude proxy settings from history!") 4168 } 4169 if strings.Contains(out, "https_proxy") { 4170 c.Fatalf("failed to exclude proxy settings from history!") 4171 } 4172 result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", envKey, envVal)}) 4173 result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal)}) 4174 4175 cacheID := buildImage(imgName + "-two") 4176 assert.Equal(c, origID, cacheID) 4177 } 4178 4179 func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *testing.T) { 4180 imgName := "bldargtest" 4181 envKey := "foo" 4182 envVal := "bar" 4183 dockerfile := fmt.Sprintf(`FROM busybox 4184 ARG %s 4185 RUN echo $%s`, envKey, envKey) 4186 buildImageSuccessfully(c, imgName, 4187 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4188 build.WithDockerfile(dockerfile), 4189 ) 4190 origImgID := getIDByName(c, imgName) 4191 4192 imgNameCache := "bldargtestcachehit" 4193 buildImageSuccessfully(c, imgNameCache, 4194 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4195 build.WithDockerfile(dockerfile), 4196 ) 4197 newImgID := getIDByName(c, imgName) 4198 if newImgID != origImgID { 4199 c.Fatalf("build didn't use cache! expected image id: %q built image id: %q", origImgID, newImgID) 4200 } 4201 } 4202 4203 func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *testing.T) { 4204 imgName := "bldargtest" 4205 envKey := "foo" 4206 envVal := "bar" 4207 extraEnvKey := "foo1" 4208 extraEnvVal := "bar1" 4209 dockerfile := fmt.Sprintf(`FROM busybox 4210 ARG %s 4211 ARG %s 4212 RUN echo $%s`, envKey, extraEnvKey, envKey) 4213 buildImageSuccessfully(c, imgName, 4214 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4215 build.WithDockerfile(dockerfile), 4216 ) 4217 origImgID := getIDByName(c, imgName) 4218 4219 imgNameCache := "bldargtestcachemiss" 4220 buildImageSuccessfully(c, imgNameCache, 4221 cli.WithFlags( 4222 "--build-arg", fmt.Sprintf("%s=%s", envKey, envVal), 4223 "--build-arg", fmt.Sprintf("%s=%s", extraEnvKey, extraEnvVal), 4224 ), 4225 build.WithDockerfile(dockerfile), 4226 ) 4227 newImgID := getIDByName(c, imgNameCache) 4228 4229 if newImgID == origImgID { 4230 c.Fatalf("build used cache, expected a miss!") 4231 } 4232 } 4233 4234 func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *testing.T) { 4235 imgName := "bldargtest" 4236 envKey := "foo" 4237 envVal := "bar" 4238 newEnvVal := "bar1" 4239 dockerfile := fmt.Sprintf(`FROM busybox 4240 ARG %s 4241 RUN echo $%s`, envKey, envKey) 4242 buildImageSuccessfully(c, imgName, 4243 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4244 build.WithDockerfile(dockerfile), 4245 ) 4246 origImgID := getIDByName(c, imgName) 4247 4248 imgNameCache := "bldargtestcachemiss" 4249 buildImageSuccessfully(c, imgNameCache, 4250 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, newEnvVal)), 4251 build.WithDockerfile(dockerfile), 4252 ) 4253 newImgID := getIDByName(c, imgNameCache) 4254 if newImgID == origImgID { 4255 c.Fatalf("build used cache, expected a miss!") 4256 } 4257 } 4258 4259 func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *testing.T) { 4260 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4261 imgName := "bldargtest" 4262 envKey := "foo" 4263 envVal := "bar" 4264 envValOverride := "barOverride" 4265 dockerfile := fmt.Sprintf(`FROM busybox 4266 ARG %s 4267 ENV %s %s 4268 RUN echo $%s 4269 CMD echo $%s 4270 `, envKey, envKey, envValOverride, envKey, envKey) 4271 4272 result := buildImage(imgName, 4273 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4274 build.WithDockerfile(dockerfile), 4275 ) 4276 result.Assert(c, icmd.Success) 4277 if strings.Count(result.Combined(), envValOverride) != 2 { 4278 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4279 } 4280 4281 containerName := "bldargCont" 4282 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4283 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4284 } 4285 } 4286 4287 // FIXME(vdemeester) might be useful to merge with the one above ? 4288 func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *testing.T) { 4289 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4290 imgName := "bldargtest" 4291 envKey := "foo" 4292 envVal := "bar" 4293 envValOverride := "barOverride" 4294 dockerfile := fmt.Sprintf(`FROM busybox 4295 ENV %s %s 4296 ARG %s 4297 RUN echo $%s 4298 CMD echo $%s 4299 `, envKey, envValOverride, envKey, envKey, envKey) 4300 result := buildImage(imgName, 4301 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4302 build.WithDockerfile(dockerfile), 4303 ) 4304 result.Assert(c, icmd.Success) 4305 if strings.Count(result.Combined(), envValOverride) != 2 { 4306 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4307 } 4308 4309 containerName := "bldargCont" 4310 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4311 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4312 } 4313 } 4314 4315 func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *testing.T) { 4316 imgName := "bldvarstest" 4317 4318 wdVar := "WDIR" 4319 wdVal := "/tmp/" 4320 addVar := "AFILE" 4321 addVal := "addFile" 4322 copyVar := "CFILE" 4323 copyVal := "copyFile" 4324 envVar := "foo" 4325 envVal := "bar" 4326 exposeVar := "EPORT" 4327 exposeVal := "9999" 4328 userVar := "USER" 4329 userVal := "testUser" 4330 volVar := "VOL" 4331 volVal := "/testVol/" 4332 if DaemonIsWindows() { 4333 volVal = "C:\\testVol" 4334 wdVal = "C:\\tmp" 4335 } 4336 4337 buildImageSuccessfully(c, imgName, 4338 cli.WithFlags( 4339 "--build-arg", fmt.Sprintf("%s=%s", wdVar, wdVal), 4340 "--build-arg", fmt.Sprintf("%s=%s", addVar, addVal), 4341 "--build-arg", fmt.Sprintf("%s=%s", copyVar, copyVal), 4342 "--build-arg", fmt.Sprintf("%s=%s", envVar, envVal), 4343 "--build-arg", fmt.Sprintf("%s=%s", exposeVar, exposeVal), 4344 "--build-arg", fmt.Sprintf("%s=%s", userVar, userVal), 4345 "--build-arg", fmt.Sprintf("%s=%s", volVar, volVal), 4346 ), 4347 build.WithBuildContext(c, 4348 build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox 4349 ARG %s 4350 WORKDIR ${%s} 4351 ARG %s 4352 ADD ${%s} testDir/ 4353 ARG %s 4354 COPY $%s testDir/ 4355 ARG %s 4356 ENV %s=${%s} 4357 ARG %s 4358 EXPOSE $%s 4359 ARG %s 4360 USER $%s 4361 ARG %s 4362 VOLUME ${%s}`, 4363 wdVar, wdVar, addVar, addVar, copyVar, copyVar, envVar, envVar, 4364 envVar, exposeVar, exposeVar, userVar, userVar, volVar, volVar)), 4365 build.WithFile(addVal, "some stuff"), 4366 build.WithFile(copyVal, "some stuff"), 4367 ), 4368 ) 4369 4370 res := inspectField(c, imgName, "Config.WorkingDir") 4371 assert.Equal(c, filepath.ToSlash(res), filepath.ToSlash(wdVal)) 4372 4373 var resArr []string 4374 inspectFieldAndUnmarshall(c, imgName, "Config.Env", &resArr) 4375 4376 found := false 4377 for _, v := range resArr { 4378 if fmt.Sprintf("%s=%s", envVar, envVal) == v { 4379 found = true 4380 break 4381 } 4382 } 4383 if !found { 4384 c.Fatalf("Config.Env value mismatch. Expected <key=value> to exist: %s=%s, got: %v", 4385 envVar, envVal, resArr) 4386 } 4387 4388 var resMap map[string]interface{} 4389 inspectFieldAndUnmarshall(c, imgName, "Config.ExposedPorts", &resMap) 4390 if _, ok := resMap[fmt.Sprintf("%s/tcp", exposeVal)]; !ok { 4391 c.Fatalf("Config.ExposedPorts value mismatch. Expected exposed port: %s/tcp, got: %v", exposeVal, resMap) 4392 } 4393 4394 res = inspectField(c, imgName, "Config.User") 4395 if res != userVal { 4396 c.Fatalf("Config.User value mismatch. Expected: %s, got: %s", userVal, res) 4397 } 4398 4399 inspectFieldAndUnmarshall(c, imgName, "Config.Volumes", &resMap) 4400 if _, ok := resMap[volVal]; !ok { 4401 c.Fatalf("Config.Volumes value mismatch. Expected volume: %s, got: %v", volVal, resMap) 4402 } 4403 } 4404 4405 func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *testing.T) { 4406 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4407 imgName := "bldvarstest" 4408 envKey := "foo" 4409 envVal := "bar" 4410 envKey1 := "foo1" 4411 envValOverride := "barOverride" 4412 dockerfile := fmt.Sprintf(`FROM busybox 4413 ARG %s 4414 ENV %s %s 4415 ENV %s ${%s} 4416 RUN echo $%s 4417 CMD echo $%s`, envKey, envKey, envValOverride, envKey1, envKey, envKey1, envKey1) 4418 result := buildImage(imgName, 4419 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4420 build.WithDockerfile(dockerfile), 4421 ) 4422 result.Assert(c, icmd.Success) 4423 if strings.Count(result.Combined(), envValOverride) != 2 { 4424 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4425 } 4426 4427 containerName := "bldargCont" 4428 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4429 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4430 } 4431 } 4432 4433 func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *testing.T) { 4434 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4435 imgName := "bldargtest" 4436 envKey := "foo" 4437 envVal := "bar" 4438 dockerfile := fmt.Sprintf(`FROM busybox 4439 RUN echo $%s 4440 ARG %s 4441 CMD echo $%s`, envKey, envKey, envKey) 4442 result := buildImage(imgName, 4443 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4444 build.WithDockerfile(dockerfile), 4445 ) 4446 result.Assert(c, icmd.Success) 4447 if strings.Contains(result.Combined(), envVal) { 4448 c.Fatalf("able to access environment variable in output: %q expected to be missing", result.Combined()) 4449 } 4450 4451 containerName := "bldargCont" 4452 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4453 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4454 } 4455 } 4456 4457 func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *testing.T) { 4458 testRequires(c, DaemonIsLinux) // Windows does not support --build-arg 4459 imgName := "bldargtest" 4460 envKey := "HTTP_PROXY" 4461 envVal := "bar" 4462 dockerfile := fmt.Sprintf(`FROM busybox 4463 RUN echo $%s 4464 CMD echo $%s`, envKey, envKey) 4465 4466 result := buildImage(imgName, 4467 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4468 build.WithDockerfile(dockerfile), 4469 ) 4470 result.Assert(c, icmd.Success) 4471 if !strings.Contains(result.Combined(), envVal) { 4472 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envVal) 4473 } 4474 containerName := "bldargCont" 4475 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4476 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4477 } 4478 } 4479 4480 func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *testing.T) { 4481 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4482 imgName := "bldargtest" 4483 envKey := "foo" 4484 envVal := "bar" 4485 envValOverride := "barOverride" 4486 dockerfile := fmt.Sprintf(`FROM busybox 4487 ARG %s=%s 4488 ENV %s $%s 4489 RUN echo $%s 4490 CMD echo $%s`, envKey, envVal, envKey, envKey, envKey, envKey) 4491 result := buildImage(imgName, 4492 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envValOverride)), 4493 build.WithDockerfile(dockerfile), 4494 ) 4495 result.Assert(c, icmd.Success) 4496 if strings.Count(result.Combined(), envValOverride) != 1 { 4497 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4498 } 4499 4500 containerName := "bldargCont" 4501 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4502 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4503 } 4504 } 4505 4506 func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *testing.T) { 4507 imgName := "bldargtest" 4508 envKey := "foo" 4509 envVal := "bar" 4510 dockerfile := fmt.Sprintf(`FROM busybox 4511 RUN echo $%s 4512 CMD echo $%s`, envKey, envKey) 4513 warnStr := "[Warning] One or more build-args" 4514 buildImage(imgName, 4515 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4516 build.WithDockerfile(dockerfile), 4517 ).Assert(c, icmd.Expected{ 4518 Out: warnStr, 4519 }) 4520 } 4521 4522 func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *testing.T) { 4523 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4524 dockerfile := `FROM busybox 4525 ARG FOO1=fromfile 4526 ARG FOO2=fromfile 4527 ARG FOO3=fromfile 4528 ARG FOO4=fromfile 4529 ARG FOO5 4530 ARG FOO6 4531 ARG FO10 4532 RUN env 4533 RUN [ "$FOO1" = "fromcmd" ] 4534 RUN [ "$FOO2" = "" ] 4535 RUN [ "$FOO3" = "fromenv" ] 4536 RUN [ "$FOO4" = "fromfile" ] 4537 RUN [ "$FOO5" = "fromcmd" ] 4538 # The following should not exist at all in the env 4539 RUN [ "$(env | grep FOO6)" = "" ] 4540 RUN [ "$(env | grep FOO7)" = "" ] 4541 RUN [ "$(env | grep FOO8)" = "" ] 4542 RUN [ "$(env | grep FOO9)" = "" ] 4543 RUN [ "$FO10" = "" ] 4544 ` 4545 result := buildImage("testbuildtimeargenv", 4546 cli.WithFlags( 4547 "--build-arg", fmt.Sprintf("FOO1=fromcmd"), 4548 "--build-arg", fmt.Sprintf("FOO2="), 4549 "--build-arg", fmt.Sprintf("FOO3"), // set in env 4550 "--build-arg", fmt.Sprintf("FOO4"), // not set in env 4551 "--build-arg", fmt.Sprintf("FOO5=fromcmd"), 4552 // FOO6 is not set at all 4553 "--build-arg", fmt.Sprintf("FOO7=fromcmd"), // should produce a warning 4554 "--build-arg", fmt.Sprintf("FOO8="), // should produce a warning 4555 "--build-arg", fmt.Sprintf("FOO9"), // should produce a warning 4556 "--build-arg", fmt.Sprintf("FO10"), // not set in env, empty value 4557 ), 4558 cli.WithEnvironmentVariables(append(os.Environ(), 4559 "FOO1=fromenv", 4560 "FOO2=fromenv", 4561 "FOO3=fromenv")...), 4562 build.WithBuildContext(c, 4563 build.WithFile("Dockerfile", dockerfile), 4564 ), 4565 ) 4566 result.Assert(c, icmd.Success) 4567 4568 // Now check to make sure we got a warning msg about unused build-args 4569 i := strings.Index(result.Combined(), "[Warning]") 4570 if i < 0 { 4571 c.Fatalf("Missing the build-arg warning in %q", result.Combined()) 4572 } 4573 4574 out := result.Combined()[i:] // "out" should contain just the warning message now 4575 4576 // These were specified on a --build-arg but no ARG was in the Dockerfile 4577 assert.Assert(c, strings.Contains(out, "FOO7")) 4578 assert.Assert(c, strings.Contains(out, "FOO8")) 4579 assert.Assert(c, strings.Contains(out, "FOO9")) 4580 } 4581 4582 func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *testing.T) { 4583 imgName := "bldargtest" 4584 envKey := "foo" 4585 envKey1 := "foo1" 4586 envKey2 := "foo2" 4587 envKey3 := "foo3" 4588 dockerfile := fmt.Sprintf(`FROM busybox 4589 ARG %s="" 4590 ARG %s='' 4591 ARG %s="''" 4592 ARG %s='""' 4593 RUN [ "$%s" != "$%s" ] 4594 RUN [ "$%s" != "$%s" ] 4595 RUN [ "$%s" != "$%s" ] 4596 RUN [ "$%s" != "$%s" ] 4597 RUN [ "$%s" != "$%s" ]`, envKey, envKey1, envKey2, envKey3, 4598 envKey, envKey2, envKey, envKey3, envKey1, envKey2, envKey1, envKey3, 4599 envKey2, envKey3) 4600 buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile)) 4601 } 4602 4603 func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *testing.T) { 4604 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4605 imgName := "bldargtest" 4606 envKey := "foo" 4607 envKey1 := "foo1" 4608 envKey2 := "foo2" 4609 dockerfile := fmt.Sprintf(`FROM busybox 4610 ARG %s= 4611 ARG %s="" 4612 ARG %s='' 4613 RUN [ "$%s" = "$%s" ] 4614 RUN [ "$%s" = "$%s" ] 4615 RUN [ "$%s" = "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2) 4616 buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile)) 4617 } 4618 4619 func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *testing.T) { 4620 imgName := "bldargtest" 4621 envKey := "foo" 4622 dockerfile := fmt.Sprintf(`FROM busybox 4623 ARG %s 4624 RUN env`, envKey) 4625 4626 result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) 4627 result.Assert(c, icmd.Success) 4628 if strings.Count(result.Combined(), envKey) != 1 { 4629 c.Fatalf("unexpected number of occurrences of the arg in output: %q expected: 1", result.Combined()) 4630 } 4631 } 4632 4633 func (s *DockerSuite) TestBuildMultiStageArg(c *testing.T) { 4634 imgName := "multifrombldargtest" 4635 dockerfile := `FROM busybox 4636 ARG foo=abc 4637 LABEL multifromtest=1 4638 RUN env > /out 4639 FROM busybox 4640 ARG bar=def 4641 RUN env > /out` 4642 4643 result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) 4644 result.Assert(c, icmd.Success) 4645 4646 result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") 4647 parentID := strings.TrimSpace(result.Stdout()) 4648 4649 result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") 4650 assert.Assert(c, strings.Contains(result.Stdout(), "foo=abc")) 4651 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4652 assert.Assert(c, !strings.Contains(result.Stdout(), "foo")) 4653 assert.Assert(c, strings.Contains(result.Stdout(), "bar=def")) 4654 } 4655 4656 func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *testing.T) { 4657 imgName := "multifrombldargtest" 4658 dockerfile := `ARG tag=nosuchtag 4659 FROM busybox:${tag} 4660 LABEL multifromtest=1 4661 RUN env > /out 4662 FROM busybox:${tag} 4663 ARG tag 4664 RUN env > /out` 4665 4666 result := cli.BuildCmd(c, imgName, 4667 build.WithDockerfile(dockerfile), 4668 cli.WithFlags("--build-arg", fmt.Sprintf("tag=latest"))) 4669 result.Assert(c, icmd.Success) 4670 4671 result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") 4672 parentID := strings.TrimSpace(result.Stdout()) 4673 4674 result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") 4675 assert.Assert(c, !strings.Contains(result.Stdout(), "tag")) 4676 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4677 assert.Assert(c, strings.Contains(result.Stdout(), "tag=latest")) 4678 } 4679 4680 func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *testing.T) { 4681 imgName := "multifromunusedarg" 4682 dockerfile := `FROM busybox 4683 ARG foo 4684 FROM busybox 4685 ARG bar 4686 RUN env > /out` 4687 4688 result := cli.BuildCmd(c, imgName, 4689 build.WithDockerfile(dockerfile), 4690 cli.WithFlags("--build-arg", fmt.Sprintf("baz=abc"))) 4691 result.Assert(c, icmd.Success) 4692 assert.Assert(c, strings.Contains(result.Combined(), "[Warning]")) 4693 assert.Assert(c, strings.Contains(result.Combined(), "[baz] were not consumed")) 4694 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4695 assert.Assert(c, !strings.Contains(result.Stdout(), "bar")) 4696 assert.Assert(c, !strings.Contains(result.Stdout(), "baz")) 4697 } 4698 4699 func (s *DockerSuite) TestBuildNoNamedVolume(c *testing.T) { 4700 volName := "testname:/foo" 4701 4702 if testEnv.OSType == "windows" { 4703 volName = "testname:C:\\foo" 4704 } 4705 dockerCmd(c, "run", "-v", volName, "busybox", "sh", "-c", "touch /foo/oops") 4706 4707 dockerFile := `FROM busybox 4708 VOLUME ` + volName + ` 4709 RUN ls /foo/oops 4710 ` 4711 buildImage("test", build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4712 ExitCode: 1, 4713 }) 4714 } 4715 4716 func (s *DockerSuite) TestBuildTagEvent(c *testing.T) { 4717 since := daemonUnixTime(c) 4718 4719 dockerFile := `FROM busybox 4720 RUN echo events 4721 ` 4722 buildImageSuccessfully(c, "test", build.WithDockerfile(dockerFile)) 4723 4724 until := daemonUnixTime(c) 4725 out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "type=image") 4726 events := strings.Split(strings.TrimSpace(out), "\n") 4727 actions := eventActionsByIDAndType(c, events, "test:latest", "image") 4728 var foundTag bool 4729 for _, a := range actions { 4730 if a == "tag" { 4731 foundTag = true 4732 break 4733 } 4734 } 4735 4736 assert.Assert(c, foundTag, "No tag event found:\n%s", out) 4737 } 4738 4739 // #15780 4740 func (s *DockerSuite) TestBuildMultipleTags(c *testing.T) { 4741 dockerfile := ` 4742 FROM busybox 4743 MAINTAINER test-15780 4744 ` 4745 buildImageSuccessfully(c, "tag1", cli.WithFlags("-t", "tag2:v2", "-t", "tag1:latest", "-t", "tag1"), build.WithDockerfile(dockerfile)) 4746 4747 id1 := getIDByName(c, "tag1") 4748 id2 := getIDByName(c, "tag2:v2") 4749 assert.Equal(c, id1, id2) 4750 } 4751 4752 // #17290 4753 func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *testing.T) { 4754 name := "testbuildbrokensymlink" 4755 ctx := fakecontext.New(c, "", 4756 fakecontext.WithDockerfile(` 4757 FROM busybox 4758 COPY . ./`), 4759 fakecontext.WithFiles(map[string]string{ 4760 "foo": "bar", 4761 })) 4762 defer ctx.Close() 4763 4764 err := os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink")) 4765 assert.NilError(c, err) 4766 4767 // warm up cache 4768 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4769 4770 // add new file to context, should invalidate cache 4771 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644) 4772 assert.NilError(c, err) 4773 4774 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4775 if strings.Contains(result.Combined(), "Using cache") { 4776 c.Fatal("2nd build used cache on ADD, it shouldn't") 4777 } 4778 } 4779 4780 func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *testing.T) { 4781 name := "testbuildbrokensymlink" 4782 ctx := fakecontext.New(c, "", 4783 fakecontext.WithDockerfile(` 4784 FROM busybox 4785 COPY asymlink target`), 4786 fakecontext.WithFiles(map[string]string{ 4787 "foo": "bar", 4788 })) 4789 defer ctx.Close() 4790 4791 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4792 assert.NilError(c, err) 4793 4794 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4795 4796 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined() 4797 assert.Assert(c, cmp.Regexp("^bar$", out)) 4798 4799 // change target file should invalidate cache 4800 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4801 assert.NilError(c, err) 4802 4803 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4804 assert.Assert(c, !strings.Contains(result.Combined(), "Using cache")) 4805 out = cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined() 4806 assert.Assert(c, cmp.Regexp("^baz$", out)) 4807 4808 } 4809 4810 func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *testing.T) { 4811 name := "testbuildbrokensymlink" 4812 ctx := fakecontext.New(c, "", 4813 fakecontext.WithDockerfile(` 4814 FROM busybox 4815 COPY asymlink /`), 4816 fakecontext.WithFiles(map[string]string{ 4817 "foo/abc": "bar", 4818 "foo/def": "baz", 4819 })) 4820 defer ctx.Close() 4821 4822 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4823 assert.NilError(c, err) 4824 4825 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4826 4827 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined() 4828 assert.Assert(c, cmp.Regexp("^barbaz$", out)) 4829 4830 // change target file should invalidate cache 4831 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644) 4832 assert.NilError(c, err) 4833 4834 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4835 assert.Assert(c, !strings.Contains(result.Combined(), "Using cache")) 4836 out = cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined() 4837 assert.Assert(c, cmp.Regexp("^barbax$", out)) 4838 4839 } 4840 4841 // TestBuildSymlinkBasename tests that target file gets basename from symlink, 4842 // not from the target file. 4843 func (s *DockerSuite) TestBuildSymlinkBasename(c *testing.T) { 4844 name := "testbuildbrokensymlink" 4845 ctx := fakecontext.New(c, "", 4846 fakecontext.WithDockerfile(` 4847 FROM busybox 4848 COPY asymlink /`), 4849 fakecontext.WithFiles(map[string]string{ 4850 "foo": "bar", 4851 })) 4852 defer ctx.Close() 4853 4854 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4855 assert.NilError(c, err) 4856 4857 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4858 4859 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "asymlink").Combined() 4860 assert.Assert(c, cmp.Regexp("^bar$", out)) 4861 4862 } 4863 4864 // #17827 4865 func (s *DockerSuite) TestBuildCacheRootSource(c *testing.T) { 4866 name := "testbuildrootsource" 4867 ctx := fakecontext.New(c, "", 4868 fakecontext.WithDockerfile(` 4869 FROM busybox 4870 COPY / /data`), 4871 fakecontext.WithFiles(map[string]string{ 4872 "foo": "bar", 4873 })) 4874 defer ctx.Close() 4875 4876 // warm up cache 4877 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4878 4879 // change file, should invalidate cache 4880 err := ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4881 assert.NilError(c, err) 4882 4883 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4884 4885 assert.Assert(c, !strings.Contains(result.Combined(), "Using cache")) 4886 } 4887 4888 // #19375 4889 // FIXME(vdemeester) should migrate to docker/cli tests 4890 func (s *DockerSuite) TestBuildFailsGitNotCallable(c *testing.T) { 4891 buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="), 4892 build.WithContextPath("github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4893 ExitCode: 1, 4894 Err: "unable to prepare context: unable to find 'git': ", 4895 }) 4896 4897 buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="), 4898 build.WithContextPath("https://github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4899 ExitCode: 1, 4900 Err: "unable to prepare context: unable to find 'git': ", 4901 }) 4902 } 4903 4904 // TestBuildWorkdirWindowsPath tests that a Windows style path works as a workdir 4905 func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *testing.T) { 4906 testRequires(c, DaemonIsWindows) 4907 name := "testbuildworkdirwindowspath" 4908 buildImageSuccessfully(c, name, build.WithDockerfile(` 4909 FROM `+testEnv.PlatformDefaults.BaseImage+` 4910 RUN mkdir C:\\work 4911 WORKDIR C:\\work 4912 RUN if "%CD%" NEQ "C:\work" exit -1 4913 `)) 4914 } 4915 4916 func (s *DockerSuite) TestBuildLabel(c *testing.T) { 4917 name := "testbuildlabel" 4918 testLabel := "foo" 4919 4920 buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel), 4921 build.WithDockerfile(` 4922 FROM `+minimalBaseImage()+` 4923 LABEL default foo 4924 `)) 4925 4926 var labels map[string]string 4927 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4928 if _, ok := labels[testLabel]; !ok { 4929 c.Fatal("label not found in image") 4930 } 4931 } 4932 4933 func (s *DockerSuite) TestBuildLabelOneNode(c *testing.T) { 4934 name := "testbuildlabel" 4935 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=bar"), 4936 build.WithDockerfile("FROM busybox")) 4937 4938 var labels map[string]string 4939 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4940 v, ok := labels["foo"] 4941 if !ok { 4942 c.Fatal("label `foo` not found in image") 4943 } 4944 assert.Equal(c, v, "bar") 4945 } 4946 4947 func (s *DockerSuite) TestBuildLabelCacheCommit(c *testing.T) { 4948 name := "testbuildlabelcachecommit" 4949 testLabel := "foo" 4950 4951 buildImageSuccessfully(c, name, build.WithDockerfile(` 4952 FROM `+minimalBaseImage()+` 4953 LABEL default foo 4954 `)) 4955 buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel), 4956 build.WithDockerfile(` 4957 FROM `+minimalBaseImage()+` 4958 LABEL default foo 4959 `)) 4960 4961 var labels map[string]string 4962 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4963 if _, ok := labels[testLabel]; !ok { 4964 c.Fatal("label not found in image") 4965 } 4966 } 4967 4968 func (s *DockerSuite) TestBuildLabelMultiple(c *testing.T) { 4969 name := "testbuildlabelmultiple" 4970 testLabels := map[string]string{ 4971 "foo": "bar", 4972 "123": "456", 4973 } 4974 var labelArgs []string 4975 for k, v := range testLabels { 4976 labelArgs = append(labelArgs, "--label", k+"="+v) 4977 } 4978 4979 buildImageSuccessfully(c, name, cli.WithFlags(labelArgs...), 4980 build.WithDockerfile(` 4981 FROM `+minimalBaseImage()+` 4982 LABEL default foo 4983 `)) 4984 4985 var labels map[string]string 4986 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4987 for k, v := range testLabels { 4988 if x, ok := labels[k]; !ok || x != v { 4989 c.Fatalf("label %s=%s not found in image", k, v) 4990 } 4991 } 4992 } 4993 4994 func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *testing.T) { 4995 dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 4996 baseImage := privateRegistryURL + "/baseimage" 4997 4998 buildImageSuccessfully(c, baseImage, build.WithDockerfile(` 4999 FROM busybox 5000 ENV env1 val1 5001 `)) 5002 5003 dockerCmd(c, "push", baseImage) 5004 dockerCmd(c, "rmi", baseImage) 5005 5006 buildImageSuccessfully(c, baseImage, build.WithDockerfile(fmt.Sprintf(` 5007 FROM %s 5008 ENV env2 val2 5009 `, baseImage))) 5010 } 5011 5012 func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *testing.T) { 5013 osPath := os.Getenv("PATH") 5014 defer os.Setenv("PATH", osPath) 5015 5016 workingDir, err := os.Getwd() 5017 assert.NilError(c, err) 5018 absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth")) 5019 assert.NilError(c, err) 5020 testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute) 5021 5022 os.Setenv("PATH", testPath) 5023 5024 repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL) 5025 5026 tmp, err := ioutil.TempDir("", "integration-cli-") 5027 assert.NilError(c, err) 5028 5029 externalAuthConfig := `{ "credsStore": "shell-test" }` 5030 5031 configPath := filepath.Join(tmp, "config.json") 5032 err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) 5033 assert.NilError(c, err) 5034 5035 dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 5036 5037 b, err := ioutil.ReadFile(configPath) 5038 assert.NilError(c, err) 5039 assert.Assert(c, !strings.Contains(string(b), "\"auth\":")) 5040 dockerCmd(c, "--config", tmp, "tag", "busybox", repoName) 5041 dockerCmd(c, "--config", tmp, "push", repoName) 5042 5043 // make sure the image is pulled when building 5044 dockerCmd(c, "rmi", repoName) 5045 5046 icmd.RunCmd(icmd.Cmd{ 5047 Command: []string{dockerBinary, "--config", tmp, "build", "-"}, 5048 Stdin: strings.NewReader(fmt.Sprintf("FROM %s", repoName)), 5049 }).Assert(c, icmd.Success) 5050 } 5051 5052 // Test cases in #22036 5053 func (s *DockerSuite) TestBuildLabelsOverride(c *testing.T) { 5054 // Command line option labels will always override 5055 name := "scratchy" 5056 expected := `{"bar":"from-flag","foo":"from-flag"}` 5057 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5058 build.WithDockerfile(`FROM `+minimalBaseImage()+` 5059 LABEL foo=from-dockerfile`)) 5060 res := inspectFieldJSON(c, name, "Config.Labels") 5061 if res != expected { 5062 c.Fatalf("Labels %s, expected %s", res, expected) 5063 } 5064 5065 name = "from" 5066 expected = `{"foo":"from-dockerfile"}` 5067 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5068 LABEL foo from-dockerfile`)) 5069 res = inspectFieldJSON(c, name, "Config.Labels") 5070 if res != expected { 5071 c.Fatalf("Labels %s, expected %s", res, expected) 5072 } 5073 5074 // Command line option label will override even via `FROM` 5075 name = "new" 5076 expected = `{"bar":"from-dockerfile2","foo":"new"}` 5077 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=new"), 5078 build.WithDockerfile(`FROM from 5079 LABEL bar from-dockerfile2`)) 5080 res = inspectFieldJSON(c, name, "Config.Labels") 5081 if res != expected { 5082 c.Fatalf("Labels %s, expected %s", res, expected) 5083 } 5084 5085 // Command line option without a value set (--label foo, --label bar=) 5086 // will be treated as --label foo="", --label bar="" 5087 name = "scratchy2" 5088 expected = `{"bar":"","foo":""}` 5089 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo", "--label", "bar="), 5090 build.WithDockerfile(`FROM `+minimalBaseImage()+` 5091 LABEL foo=from-dockerfile`)) 5092 res = inspectFieldJSON(c, name, "Config.Labels") 5093 if res != expected { 5094 c.Fatalf("Labels %s, expected %s", res, expected) 5095 } 5096 5097 // Command line option without a value set (--label foo, --label bar=) 5098 // will be treated as --label foo="", --label bar="" 5099 // This time is for inherited images 5100 name = "new2" 5101 expected = `{"bar":"","foo":""}` 5102 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=", "--label", "bar"), 5103 build.WithDockerfile(`FROM from 5104 LABEL bar from-dockerfile2`)) 5105 res = inspectFieldJSON(c, name, "Config.Labels") 5106 if res != expected { 5107 c.Fatalf("Labels %s, expected %s", res, expected) 5108 } 5109 5110 // Command line option labels with only `FROM` 5111 name = "scratchy" 5112 expected = `{"bar":"from-flag","foo":"from-flag"}` 5113 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5114 build.WithDockerfile(`FROM `+minimalBaseImage())) 5115 res = inspectFieldJSON(c, name, "Config.Labels") 5116 if res != expected { 5117 c.Fatalf("Labels %s, expected %s", res, expected) 5118 } 5119 5120 // Command line option labels with env var 5121 name = "scratchz" 5122 expected = `{"bar":"$PATH"}` 5123 buildImageSuccessfully(c, name, cli.WithFlags("--label", "bar=$PATH"), 5124 build.WithDockerfile(`FROM `+minimalBaseImage())) 5125 res = inspectFieldJSON(c, name, "Config.Labels") 5126 if res != expected { 5127 c.Fatalf("Labels %s, expected %s", res, expected) 5128 } 5129 } 5130 5131 // Test case for #22855 5132 func (s *DockerSuite) TestBuildDeleteCommittedFile(c *testing.T) { 5133 name := "test-delete-committed-file" 5134 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 5135 RUN echo test > file 5136 RUN test -e file 5137 RUN rm file 5138 RUN sh -c "! test -e file"`)) 5139 } 5140 5141 // #20083 5142 func (s *DockerSuite) TestBuildDockerignoreComment(c *testing.T) { 5143 // TODO Windows: Figure out why this test is flakey on TP5. If you add 5144 // something like RUN sleep 5, or even RUN ls /tmp after the ADD line, 5145 // it is more reliable, but that's not a good fix. 5146 testRequires(c, DaemonIsLinux) 5147 5148 name := "testbuilddockerignorecleanpaths" 5149 dockerfile := ` 5150 FROM busybox 5151 ADD . /tmp/ 5152 RUN sh -c "(ls -la /tmp/#1)" 5153 RUN sh -c "(! ls -la /tmp/#2)" 5154 RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (ls /tmp/dir1/foo)"` 5155 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5156 build.WithFile("Dockerfile", dockerfile), 5157 build.WithFile("foo", "foo"), 5158 build.WithFile("foo2", "foo2"), 5159 build.WithFile("dir1/foo", "foo in dir1"), 5160 build.WithFile("#1", "# file 1"), 5161 build.WithFile("#2", "# file 2"), 5162 build.WithFile(".dockerignore", `# Visual C++ cache files 5163 # because we have git ;-) 5164 # The above comment is from #20083 5165 foo 5166 #dir1/foo 5167 foo2 5168 # The following is considered as comment as # is at the beginning 5169 #1 5170 # The following is not considered as comment as # is not at the beginning 5171 #2 5172 `))) 5173 } 5174 5175 // Test case for #23221 5176 func (s *DockerSuite) TestBuildWithUTF8BOM(c *testing.T) { 5177 name := "test-with-utf8-bom" 5178 dockerfile := []byte(`FROM busybox`) 5179 bomDockerfile := append([]byte{0xEF, 0xBB, 0xBF}, dockerfile...) 5180 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5181 build.WithFile("Dockerfile", string(bomDockerfile)), 5182 )) 5183 } 5184 5185 // Test case for UTF-8 BOM in .dockerignore, related to #23221 5186 func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *testing.T) { 5187 name := "test-with-utf8-bom-dockerignore" 5188 dockerfile := ` 5189 FROM busybox 5190 ADD . /tmp/ 5191 RUN ls -la /tmp 5192 RUN sh -c "! ls /tmp/Dockerfile" 5193 RUN ls /tmp/.dockerignore` 5194 dockerignore := []byte("./Dockerfile\n") 5195 bomDockerignore := append([]byte{0xEF, 0xBB, 0xBF}, dockerignore...) 5196 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5197 build.WithFile("Dockerfile", dockerfile), 5198 build.WithFile(".dockerignore", string(bomDockerignore)), 5199 )) 5200 } 5201 5202 // #22489 Shell test to confirm config gets updated correctly 5203 func (s *DockerSuite) TestBuildShellUpdatesConfig(c *testing.T) { 5204 name := "testbuildshellupdatesconfig" 5205 5206 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5207 SHELL ["foo", "-bar"]`)) 5208 expected := `["foo","-bar","#(nop) ","SHELL [foo -bar]"]` 5209 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 5210 if res != expected { 5211 c.Fatalf("%s, expected %s", res, expected) 5212 } 5213 res = inspectFieldJSON(c, name, "ContainerConfig.Shell") 5214 if res != `["foo","-bar"]` { 5215 c.Fatalf(`%s, expected ["foo","-bar"]`, res) 5216 } 5217 } 5218 5219 // #22489 Changing the shell multiple times and CMD after. 5220 func (s *DockerSuite) TestBuildShellMultiple(c *testing.T) { 5221 name := "testbuildshellmultiple" 5222 5223 result := buildImage(name, build.WithDockerfile(`FROM busybox 5224 RUN echo defaultshell 5225 SHELL ["echo"] 5226 RUN echoshell 5227 SHELL ["ls"] 5228 RUN -l 5229 CMD -l`)) 5230 result.Assert(c, icmd.Success) 5231 5232 // Must contain 'defaultshell' twice 5233 if len(strings.Split(result.Combined(), "defaultshell")) != 3 { 5234 c.Fatalf("defaultshell should have appeared twice in %s", result.Combined()) 5235 } 5236 5237 // Must contain 'echoshell' twice 5238 if len(strings.Split(result.Combined(), "echoshell")) != 3 { 5239 c.Fatalf("echoshell should have appeared twice in %s", result.Combined()) 5240 } 5241 5242 // Must contain "total " (part of ls -l) 5243 if !strings.Contains(result.Combined(), "total ") { 5244 c.Fatalf("%s should have contained 'total '", result.Combined()) 5245 } 5246 5247 // A container started from the image uses the shell-form CMD. 5248 // Last shell is ls. CMD is -l. So should contain 'total '. 5249 outrun, _ := dockerCmd(c, "run", "--rm", name) 5250 if !strings.Contains(outrun, "total ") { 5251 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5252 } 5253 } 5254 5255 // #22489. Changed SHELL with ENTRYPOINT 5256 func (s *DockerSuite) TestBuildShellEntrypoint(c *testing.T) { 5257 name := "testbuildshellentrypoint" 5258 5259 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 5260 SHELL ["ls"] 5261 ENTRYPOINT -l`)) 5262 // A container started from the image uses the shell-form ENTRYPOINT. 5263 // Shell is ls. ENTRYPOINT is -l. So should contain 'total '. 5264 outrun, _ := dockerCmd(c, "run", "--rm", name) 5265 if !strings.Contains(outrun, "total ") { 5266 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5267 } 5268 } 5269 5270 // #22489 Shell test to confirm shell is inherited in a subsequent build 5271 func (s *DockerSuite) TestBuildShellInherited(c *testing.T) { 5272 name1 := "testbuildshellinherited1" 5273 buildImageSuccessfully(c, name1, build.WithDockerfile(`FROM busybox 5274 SHELL ["ls"]`)) 5275 name2 := "testbuildshellinherited2" 5276 buildImage(name2, build.WithDockerfile(`FROM `+name1+` 5277 RUN -l`)).Assert(c, icmd.Expected{ 5278 // ls -l has "total " followed by some number in it, ls without -l does not. 5279 Out: "total ", 5280 }) 5281 } 5282 5283 // #22489 Shell test to confirm non-JSON doesn't work 5284 func (s *DockerSuite) TestBuildShellNotJSON(c *testing.T) { 5285 name := "testbuildshellnotjson" 5286 5287 buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5288 sHeLl exec -form`, // Casing explicit to ensure error is upper-cased. 5289 )).Assert(c, icmd.Expected{ 5290 ExitCode: 1, 5291 Err: "SHELL requires the arguments to be in JSON form", 5292 }) 5293 } 5294 5295 // #22489 Windows shell test to confirm native is powershell if executing a PS command 5296 // This would error if the default shell were still cmd. 5297 func (s *DockerSuite) TestBuildShellWindowsPowershell(c *testing.T) { 5298 testRequires(c, DaemonIsWindows) 5299 name := "testbuildshellpowershell" 5300 buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5301 SHELL ["powershell", "-command"] 5302 RUN Write-Host John`)).Assert(c, icmd.Expected{ 5303 Out: "\nJohn\n", 5304 }) 5305 } 5306 5307 // Verify that escape is being correctly applied to words when escape directive is not \. 5308 // Tests WORKDIR, ADD 5309 func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *testing.T) { 5310 testRequires(c, DaemonIsWindows) 5311 name := "testbuildescapenotbackslashwordtesta" 5312 buildImage(name, build.WithDockerfile(`# escape= `+"`"+` 5313 FROM `+minimalBaseImage()+` 5314 WORKDIR c:\windows 5315 RUN dir /w`)).Assert(c, icmd.Expected{ 5316 Out: "[System32]", 5317 }) 5318 5319 name = "testbuildescapenotbackslashwordtestb" 5320 buildImage(name, build.WithDockerfile(`# escape= `+"`"+` 5321 FROM `+minimalBaseImage()+` 5322 SHELL ["powershell.exe"] 5323 WORKDIR c:\foo 5324 ADD Dockerfile c:\foo\ 5325 RUN dir Dockerfile`)).Assert(c, icmd.Expected{ 5326 Out: "-a----", 5327 }) 5328 } 5329 5330 // #22868. Make sure shell-form CMD is not marked as escaped in the config of the image, 5331 // but an exec-form CMD is marked. 5332 func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *testing.T) { 5333 testRequires(c, DaemonIsWindows) 5334 name1 := "testbuildcmdshellescapedshellform" 5335 buildImageSuccessfully(c, name1, build.WithDockerfile(` 5336 FROM `+minimalBaseImage()+` 5337 CMD "ipconfig" 5338 `)) 5339 res := inspectFieldJSON(c, name1, "Config.ArgsEscaped") 5340 if res != "true" { 5341 c.Fatalf("CMD did not update Config.ArgsEscaped on image: %v", res) 5342 } 5343 dockerCmd(c, "run", "--name", "inspectme1", name1) 5344 dockerCmd(c, "wait", "inspectme1") 5345 res = inspectFieldJSON(c, name1, "Config.Cmd") 5346 5347 if res != `["cmd /S /C \"ipconfig\""]` { 5348 c.Fatalf("CMD incorrect in Config.Cmd: got %v", res) 5349 } 5350 5351 // Now in JSON/exec-form 5352 name2 := "testbuildcmdshellescapedexecform" 5353 buildImageSuccessfully(c, name2, build.WithDockerfile(` 5354 FROM `+minimalBaseImage()+` 5355 CMD ["ipconfig"] 5356 `)) 5357 res = inspectFieldJSON(c, name2, "Config.ArgsEscaped") 5358 if res != "false" { 5359 c.Fatalf("CMD set Config.ArgsEscaped on image: %v", res) 5360 } 5361 dockerCmd(c, "run", "--name", "inspectme2", name2) 5362 dockerCmd(c, "wait", "inspectme2") 5363 res = inspectFieldJSON(c, name2, "Config.Cmd") 5364 5365 if res != `["ipconfig"]` { 5366 c.Fatalf("CMD incorrect in Config.Cmd: got %v", res) 5367 } 5368 5369 } 5370 5371 // Test case for #24912. 5372 func (s *DockerSuite) TestBuildStepsWithProgress(c *testing.T) { 5373 name := "testbuildstepswithprogress" 5374 totalRun := 5 5375 result := buildImage(name, build.WithDockerfile("FROM busybox\n"+strings.Repeat("RUN echo foo\n", totalRun))) 5376 result.Assert(c, icmd.Success) 5377 assert.Assert(c, strings.Contains(result.Combined(), fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun))) 5378 for i := 2; i <= 1+totalRun; i++ { 5379 assert.Assert(c, strings.Contains(result.Combined(), fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun))) 5380 } 5381 } 5382 5383 func (s *DockerSuite) TestBuildWithFailure(c *testing.T) { 5384 name := "testbuildwithfailure" 5385 5386 // First test case can only detect `nobody` in runtime so all steps will show up 5387 dockerfile := "FROM busybox\nRUN nobody" 5388 result := buildImage(name, build.WithDockerfile(dockerfile)) 5389 assert.Assert(c, result.Error != nil) 5390 assert.Assert(c, strings.Contains(result.Stdout(), "Step 1/2 : FROM busybox")) 5391 assert.Assert(c, strings.Contains(result.Stdout(), "Step 2/2 : RUN nobody")) 5392 // Second test case `FFOM` should have been detected before build runs so no steps 5393 dockerfile = "FFOM nobody\nRUN nobody" 5394 result = buildImage(name, build.WithDockerfile(dockerfile)) 5395 assert.Assert(c, result.Error != nil) 5396 assert.Assert(c, !strings.Contains(result.Stdout(), "Step 1/2 : FROM busybox")) 5397 assert.Assert(c, !strings.Contains(result.Stdout(), "Step 2/2 : RUN nobody")) 5398 } 5399 5400 func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *testing.T) { 5401 dockerfile := ` 5402 FROM busybox 5403 RUN echo "test" 5404 ENTRYPOINT ["sh"]` 5405 ctx := fakecontext.New(c, "", 5406 fakecontext.WithDockerfile(dockerfile), 5407 fakecontext.WithFiles(map[string]string{ 5408 "Dockerfile": dockerfile, 5409 })) 5410 defer ctx.Close() 5411 5412 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5413 id1 := getIDByName(c, "build1") 5414 5415 // rebuild with cache-from 5416 result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5417 id2 := getIDByName(c, "build2") 5418 assert.Equal(c, id1, id2) 5419 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2) 5420 } 5421 5422 func (s *DockerSuite) TestBuildCacheFrom(c *testing.T) { 5423 testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows 5424 dockerfile := ` 5425 FROM busybox 5426 ENV FOO=bar 5427 ADD baz / 5428 RUN touch bax` 5429 ctx := fakecontext.New(c, "", 5430 fakecontext.WithDockerfile(dockerfile), 5431 fakecontext.WithFiles(map[string]string{ 5432 "Dockerfile": dockerfile, 5433 "baz": "baz", 5434 })) 5435 defer ctx.Close() 5436 5437 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5438 id1 := getIDByName(c, "build1") 5439 5440 // rebuild with cache-from 5441 result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5442 id2 := getIDByName(c, "build2") 5443 assert.Equal(c, id1, id2) 5444 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3) 5445 cli.DockerCmd(c, "rmi", "build2") 5446 5447 // no cache match with unknown source 5448 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=nosuchtag"), build.WithExternalBuildContext(ctx)) 5449 id2 = getIDByName(c, "build2") 5450 assert.Assert(c, id1 != id2) 5451 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 0) 5452 cli.DockerCmd(c, "rmi", "build2") 5453 5454 // clear parent images 5455 tempDir, err := ioutil.TempDir("", "test-build-cache-from-") 5456 if err != nil { 5457 c.Fatalf("failed to create temporary directory: %s", tempDir) 5458 } 5459 defer os.RemoveAll(tempDir) 5460 tempFile := filepath.Join(tempDir, "img.tar") 5461 cli.DockerCmd(c, "save", "-o", tempFile, "build1") 5462 cli.DockerCmd(c, "rmi", "build1") 5463 cli.DockerCmd(c, "load", "-i", tempFile) 5464 parentID := cli.DockerCmd(c, "inspect", "-f", "{{.Parent}}", "build1").Combined() 5465 assert.Equal(c, strings.TrimSpace(parentID), "") 5466 5467 // cache still applies without parents 5468 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5469 id2 = getIDByName(c, "build2") 5470 assert.Equal(c, id1, id2) 5471 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3) 5472 history1 := cli.DockerCmd(c, "history", "-q", "build2").Combined() 5473 5474 // Retry, no new intermediate images 5475 result = cli.BuildCmd(c, "build3", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5476 id3 := getIDByName(c, "build3") 5477 assert.Equal(c, id1, id3) 5478 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3) 5479 history2 := cli.DockerCmd(c, "history", "-q", "build3").Combined() 5480 5481 assert.Equal(c, history1, history2) 5482 cli.DockerCmd(c, "rmi", "build2") 5483 cli.DockerCmd(c, "rmi", "build3") 5484 cli.DockerCmd(c, "rmi", "build1") 5485 cli.DockerCmd(c, "load", "-i", tempFile) 5486 5487 // Modify file, everything up to last command and layers are reused 5488 dockerfile = ` 5489 FROM busybox 5490 ENV FOO=bar 5491 ADD baz / 5492 RUN touch newfile` 5493 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644) 5494 assert.NilError(c, err) 5495 5496 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5497 id2 = getIDByName(c, "build2") 5498 assert.Assert(c, id1 != id2) 5499 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2) 5500 5501 layers1Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build1").Combined() 5502 layers2Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build2").Combined() 5503 5504 var layers1 []string 5505 var layers2 []string 5506 assert.Assert(c, json.Unmarshal([]byte(layers1Str), &layers1) == nil) 5507 assert.Assert(c, json.Unmarshal([]byte(layers2Str), &layers2) == nil) 5508 5509 assert.Equal(c, len(layers1), len(layers2)) 5510 for i := 0; i < len(layers1)-1; i++ { 5511 assert.Equal(c, layers1[i], layers2[i]) 5512 } 5513 assert.Assert(c, layers1[len(layers1)-1] != layers2[len(layers1)-1]) 5514 } 5515 5516 func (s *DockerSuite) TestBuildMultiStageCache(c *testing.T) { 5517 testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows 5518 dockerfile := ` 5519 FROM busybox 5520 ADD baz / 5521 FROM busybox 5522 ADD baz /` 5523 ctx := fakecontext.New(c, "", 5524 fakecontext.WithDockerfile(dockerfile), 5525 fakecontext.WithFiles(map[string]string{ 5526 "Dockerfile": dockerfile, 5527 "baz": "baz", 5528 })) 5529 defer ctx.Close() 5530 5531 result := cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5532 // second part of dockerfile was a repeat of first so should be cached 5533 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 1) 5534 5535 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5536 // now both parts of dockerfile should be cached 5537 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2) 5538 } 5539 5540 func (s *DockerSuite) TestBuildNetNone(c *testing.T) { 5541 testRequires(c, DaemonIsLinux) 5542 name := "testbuildnetnone" 5543 buildImage(name, cli.WithFlags("--network=none"), build.WithDockerfile(` 5544 FROM busybox 5545 RUN ping -c 1 8.8.8.8 5546 `)).Assert(c, icmd.Expected{ 5547 ExitCode: 1, 5548 Out: "unreachable", 5549 }) 5550 } 5551 5552 func (s *DockerSuite) TestBuildNetContainer(c *testing.T) { 5553 testRequires(c, DaemonIsLinux) 5554 5555 id, _ := dockerCmd(c, "run", "--hostname", "foobar", "-d", "busybox", "nc", "-ll", "-p", "1234", "-e", "hostname") 5556 5557 name := "testbuildnetcontainer" 5558 buildImageSuccessfully(c, name, cli.WithFlags("--network=container:"+strings.TrimSpace(id)), 5559 build.WithDockerfile(` 5560 FROM busybox 5561 RUN nc localhost 1234 > /otherhost 5562 `)) 5563 5564 host, _ := dockerCmd(c, "run", "testbuildnetcontainer", "cat", "/otherhost") 5565 assert.Equal(c, strings.TrimSpace(host), "foobar") 5566 } 5567 5568 func (s *DockerSuite) TestBuildWithExtraHost(c *testing.T) { 5569 testRequires(c, DaemonIsLinux) 5570 5571 name := "testbuildwithextrahost" 5572 buildImageSuccessfully(c, name, 5573 cli.WithFlags( 5574 "--add-host", "foo:127.0.0.1", 5575 "--add-host", "bar:127.0.0.1", 5576 ), 5577 build.WithDockerfile(` 5578 FROM busybox 5579 RUN ping -c 1 foo 5580 RUN ping -c 1 bar 5581 `)) 5582 } 5583 5584 func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *testing.T) { 5585 testRequires(c, DaemonIsLinux) 5586 dockerfile := ` 5587 FROM busybox 5588 RUN ping -c 1 foo` 5589 5590 testCases := []struct { 5591 testName string 5592 dockerfile string 5593 buildFlag string 5594 }{ 5595 {"extra_host_missing_ip", dockerfile, "--add-host=foo"}, 5596 {"extra_host_missing_ip_with_delimiter", dockerfile, "--add-host=foo:"}, 5597 {"extra_host_missing_hostname", dockerfile, "--add-host=:127.0.0.1"}, 5598 {"extra_host_invalid_ipv4", dockerfile, "--add-host=foo:101.10.2"}, 5599 {"extra_host_invalid_ipv6", dockerfile, "--add-host=foo:2001::1::3F"}, 5600 } 5601 5602 for _, tc := range testCases { 5603 result := buildImage(tc.testName, cli.WithFlags(tc.buildFlag), build.WithDockerfile(tc.dockerfile)) 5604 result.Assert(c, icmd.Expected{ 5605 ExitCode: 125, 5606 }) 5607 } 5608 5609 } 5610 5611 func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *testing.T) { 5612 dockerfile := ` 5613 FROM busybox AS first 5614 COPY foo bar 5615 5616 FROM busybox 5617 %s 5618 COPY baz baz 5619 RUN echo mno > baz/cc 5620 5621 FROM busybox 5622 COPY bar / 5623 COPY --from=1 baz sub/ 5624 COPY --from=0 bar baz 5625 COPY --from=first bar bay` 5626 5627 ctx := fakecontext.New(c, "", 5628 fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, "")), 5629 fakecontext.WithFiles(map[string]string{ 5630 "foo": "abc", 5631 "bar": "def", 5632 "baz/aa": "ghi", 5633 "baz/bb": "jkl", 5634 })) 5635 defer ctx.Close() 5636 5637 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5638 5639 cli.DockerCmd(c, "run", "build1", "cat", "bar").Assert(c, icmd.Expected{Out: "def"}) 5640 cli.DockerCmd(c, "run", "build1", "cat", "sub/aa").Assert(c, icmd.Expected{Out: "ghi"}) 5641 cli.DockerCmd(c, "run", "build1", "cat", "sub/cc").Assert(c, icmd.Expected{Out: "mno"}) 5642 cli.DockerCmd(c, "run", "build1", "cat", "baz").Assert(c, icmd.Expected{Out: "abc"}) 5643 cli.DockerCmd(c, "run", "build1", "cat", "bay").Assert(c, icmd.Expected{Out: "abc"}) 5644 5645 result := cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5646 5647 // all commands should be cached 5648 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 7) 5649 assert.Equal(c, getIDByName(c, "build1"), getIDByName(c, "build2")) 5650 5651 err := ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(fmt.Sprintf(dockerfile, "COPY baz/aa foo")), 0644) 5652 assert.NilError(c, err) 5653 5654 // changing file in parent block should not affect last block 5655 result = cli.BuildCmd(c, "build3", build.WithExternalBuildContext(ctx)) 5656 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 5) 5657 5658 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("pqr"), 0644) 5659 assert.NilError(c, err) 5660 5661 // changing file in parent block should affect both first and last block 5662 result = cli.BuildCmd(c, "build4", build.WithExternalBuildContext(ctx)) 5663 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 5) 5664 5665 cli.DockerCmd(c, "run", "build4", "cat", "bay").Assert(c, icmd.Expected{Out: "pqr"}) 5666 cli.DockerCmd(c, "run", "build4", "cat", "baz").Assert(c, icmd.Expected{Out: "pqr"}) 5667 } 5668 5669 func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *testing.T) { 5670 testCases := []struct { 5671 dockerfile string 5672 expectedError string 5673 }{ 5674 { 5675 dockerfile: ` 5676 FROM busybox 5677 COPY --from=foo foo bar`, 5678 expectedError: "invalid from flag value foo", 5679 }, 5680 { 5681 dockerfile: ` 5682 FROM busybox 5683 COPY --from=0 foo bar`, 5684 expectedError: "invalid from flag value 0: refers to current build stage", 5685 }, 5686 { 5687 dockerfile: ` 5688 FROM busybox AS foo 5689 COPY --from=bar foo bar`, 5690 expectedError: "invalid from flag value bar", 5691 }, 5692 { 5693 dockerfile: ` 5694 FROM busybox AS 1 5695 COPY --from=1 foo bar`, 5696 expectedError: "invalid name for build stage", 5697 }, 5698 } 5699 5700 for _, tc := range testCases { 5701 ctx := fakecontext.New(c, "", 5702 fakecontext.WithDockerfile(tc.dockerfile), 5703 fakecontext.WithFiles(map[string]string{ 5704 "foo": "abc", 5705 })) 5706 5707 cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx)).Assert(c, icmd.Expected{ 5708 ExitCode: 1, 5709 Err: tc.expectedError, 5710 }) 5711 5712 ctx.Close() 5713 } 5714 } 5715 5716 func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *testing.T) { 5717 dockerfile := ` 5718 FROM busybox 5719 COPY foo bar` 5720 ctx := fakecontext.New(c, "", 5721 fakecontext.WithDockerfile(dockerfile), 5722 fakecontext.WithFiles(map[string]string{ 5723 "foo": "abc", 5724 })) 5725 defer ctx.Close() 5726 5727 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5728 5729 dockerfile = ` 5730 FROM build1:latest AS foo 5731 FROM busybox 5732 COPY --from=foo bar / 5733 COPY foo /` 5734 ctx = fakecontext.New(c, "", 5735 fakecontext.WithDockerfile(dockerfile), 5736 fakecontext.WithFiles(map[string]string{ 5737 "foo": "def", 5738 })) 5739 defer ctx.Close() 5740 5741 cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5742 5743 out := cli.DockerCmd(c, "run", "build2", "cat", "bar").Combined() 5744 assert.Equal(c, strings.TrimSpace(out), "abc") 5745 out = cli.DockerCmd(c, "run", "build2", "cat", "foo").Combined() 5746 assert.Equal(c, strings.TrimSpace(out), "def") 5747 } 5748 5749 func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *testing.T) { 5750 dockerfile := ` 5751 FROM busybox 5752 COPY --from=busybox /etc/passwd /mypasswd 5753 RUN cmp /etc/passwd /mypasswd` 5754 5755 if DaemonIsWindows() { 5756 dockerfile = ` 5757 FROM busybox 5758 COPY --from=busybox License.txt foo` 5759 } 5760 5761 ctx := fakecontext.New(c, "", 5762 fakecontext.WithDockerfile(dockerfile), 5763 ) 5764 defer ctx.Close() 5765 5766 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5767 5768 if DaemonIsWindows() { 5769 out := cli.DockerCmd(c, "run", "build1", "cat", "License.txt").Combined() 5770 assert.Assert(c, len(out) > 10) 5771 out2 := cli.DockerCmd(c, "run", "build1", "cat", "foo").Combined() 5772 assert.Equal(c, out, out2) 5773 } 5774 } 5775 5776 func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *testing.T) { 5777 repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL) 5778 5779 dockerfile := ` 5780 FROM busybox 5781 COPY foo bar` 5782 ctx := fakecontext.New(c, "", 5783 fakecontext.WithDockerfile(dockerfile), 5784 fakecontext.WithFiles(map[string]string{ 5785 "foo": "abc", 5786 })) 5787 defer ctx.Close() 5788 5789 cli.BuildCmd(c, repoName, build.WithExternalBuildContext(ctx)) 5790 5791 cli.DockerCmd(c, "push", repoName) 5792 cli.DockerCmd(c, "rmi", repoName) 5793 5794 dockerfile = ` 5795 FROM busybox 5796 COPY --from=%s bar baz` 5797 5798 ctx = fakecontext.New(c, "", fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, repoName))) 5799 defer ctx.Close() 5800 5801 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5802 5803 cli.Docker(cli.Args("run", "build1", "cat", "baz")).Assert(c, icmd.Expected{Out: "abc"}) 5804 } 5805 5806 func (s *DockerSuite) TestBuildMultiStageNameVariants(c *testing.T) { 5807 dockerfile := ` 5808 FROM busybox as foo 5809 COPY foo / 5810 FROM foo as foo1 5811 RUN echo 1 >> foo 5812 FROM foo as foO2 5813 RUN echo 2 >> foo 5814 FROM foo 5815 COPY --from=foo1 foo f1 5816 COPY --from=FOo2 foo f2 5817 ` // foo2 case also tests that names are case insensitive 5818 ctx := fakecontext.New(c, "", 5819 fakecontext.WithDockerfile(dockerfile), 5820 fakecontext.WithFiles(map[string]string{ 5821 "foo": "bar", 5822 })) 5823 defer ctx.Close() 5824 5825 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5826 cli.Docker(cli.Args("run", "build1", "cat", "foo")).Assert(c, icmd.Expected{Out: "bar"}) 5827 cli.Docker(cli.Args("run", "build1", "cat", "f1")).Assert(c, icmd.Expected{Out: "bar1"}) 5828 cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"}) 5829 } 5830 5831 func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *testing.T) { 5832 testRequires(c, DaemonIsWindows) 5833 dockerfile := ` 5834 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5835 COPY foo c:\\bar` 5836 ctx := fakecontext.New(c, "", 5837 fakecontext.WithDockerfile(dockerfile), 5838 fakecontext.WithFiles(map[string]string{ 5839 "foo": "abc", 5840 })) 5841 defer ctx.Close() 5842 5843 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5844 5845 dockerfile = ` 5846 FROM build1:latest 5847 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5848 COPY --from=0 c:\\bar / 5849 COPY foo /` 5850 ctx = fakecontext.New(c, "", 5851 fakecontext.WithDockerfile(dockerfile), 5852 fakecontext.WithFiles(map[string]string{ 5853 "foo": "def", 5854 })) 5855 defer ctx.Close() 5856 5857 cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5858 5859 out := cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\bar").Combined() 5860 assert.Equal(c, strings.TrimSpace(out), "abc") 5861 out = cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\foo").Combined() 5862 assert.Equal(c, strings.TrimSpace(out), "def") 5863 } 5864 5865 func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *testing.T) { 5866 testRequires(c, DaemonIsWindows) 5867 dockerfile := ` 5868 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5869 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5870 COPY --from=0 %s c:\\oscopy 5871 ` 5872 exp := icmd.Expected{ 5873 ExitCode: 1, 5874 Err: "copy from c:\\ or c:\\windows is not allowed on windows", 5875 } 5876 buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\"))).Assert(c, exp) 5877 buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "C:\\\\"))).Assert(c, exp) 5878 buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\windows"))).Assert(c, exp) 5879 buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\wInDows"))).Assert(c, exp) 5880 } 5881 5882 func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *testing.T) { 5883 testRequires(c, DaemonIsWindows) 5884 dockerfile := ` 5885 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5886 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5887 COPY --from=0 %s c:\\oscopy 5888 ` 5889 exp := icmd.Expected{ 5890 ExitCode: 1, 5891 Err: "copy from c:\\ or c:\\windows is not allowed on windows", 5892 } 5893 buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:"))).Assert(c, exp) 5894 buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "."))).Assert(c, exp) 5895 buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "..\\\\"))).Assert(c, exp) 5896 buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, ".\\\\windows"))).Assert(c, exp) 5897 buildImage("testforbidsystempaths5", build.WithDockerfile(fmt.Sprintf(dockerfile, "\\\\windows"))).Assert(c, exp) 5898 } 5899 5900 func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *testing.T) { 5901 testRequires(c, DaemonIsWindows) 5902 dockerfile := ` 5903 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5904 COPY foo / 5905 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5906 COPY --from=0 c:\\fOo c:\\copied 5907 RUN type c:\\copied 5908 ` 5909 cli.Docker(cli.Build("copyfrom-windows-insensitive"), build.WithBuildContext(c, 5910 build.WithFile("Dockerfile", dockerfile), 5911 build.WithFile("foo", "hello world"), 5912 )).Assert(c, icmd.Expected{ 5913 ExitCode: 0, 5914 Out: "hello world", 5915 }) 5916 } 5917 5918 // #33176 5919 func (s *DockerSuite) TestBuildMultiStageResetScratch(c *testing.T) { 5920 testRequires(c, DaemonIsLinux) 5921 5922 dockerfile := ` 5923 FROM busybox 5924 WORKDIR /foo/bar 5925 FROM scratch 5926 ENV FOO=bar 5927 ` 5928 ctx := fakecontext.New(c, "", 5929 fakecontext.WithDockerfile(dockerfile), 5930 ) 5931 defer ctx.Close() 5932 5933 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5934 5935 res := cli.InspectCmd(c, "build1", cli.Format(".Config.WorkingDir")).Combined() 5936 assert.Equal(c, strings.TrimSpace(res), "") 5937 } 5938 5939 func (s *DockerSuite) TestBuildIntermediateTarget(c *testing.T) { 5940 //todo: need to be removed after 18.06 release 5941 if strings.Contains(testEnv.DaemonInfo.ServerVersion, "18.05.0") { 5942 c.Skip(fmt.Sprintf("Bug fixed in 18.06 or higher.Skipping it for %s", testEnv.DaemonInfo.ServerVersion)) 5943 } 5944 dockerfile := ` 5945 FROM busybox AS build-env 5946 CMD ["/dev"] 5947 FROM busybox 5948 CMD ["/dist"] 5949 ` 5950 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile)) 5951 defer ctx.Close() 5952 5953 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx), 5954 cli.WithFlags("--target", "build-env")) 5955 5956 res := cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined() 5957 assert.Equal(c, strings.TrimSpace(res), `["/dev"]`) 5958 5959 // Stage name is case-insensitive by design 5960 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx), 5961 cli.WithFlags("--target", "BUIld-EnV")) 5962 5963 res = cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined() 5964 assert.Equal(c, strings.TrimSpace(res), `["/dev"]`) 5965 5966 result := cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx), 5967 cli.WithFlags("--target", "nosuchtarget")) 5968 result.Assert(c, icmd.Expected{ 5969 ExitCode: 1, 5970 Err: "failed to reach build target", 5971 }) 5972 } 5973 5974 // TestBuildOpaqueDirectory tests that a build succeeds which 5975 // creates opaque directories. 5976 // See https://github.com/docker/docker/issues/25244 5977 func (s *DockerSuite) TestBuildOpaqueDirectory(c *testing.T) { 5978 testRequires(c, DaemonIsLinux) 5979 dockerFile := ` 5980 FROM busybox 5981 RUN mkdir /dir1 && touch /dir1/f1 5982 RUN rm -rf /dir1 && mkdir /dir1 && touch /dir1/f2 5983 RUN touch /dir1/f3 5984 RUN [ -f /dir1/f2 ] 5985 ` 5986 // Test that build succeeds, last command fails if opaque directory 5987 // was not handled correctly 5988 buildImageSuccessfully(c, "testopaquedirectory", build.WithDockerfile(dockerFile)) 5989 } 5990 5991 // Windows test for USER in dockerfile 5992 func (s *DockerSuite) TestBuildWindowsUser(c *testing.T) { 5993 testRequires(c, DaemonIsWindows) 5994 name := "testbuildwindowsuser" 5995 buildImage(name, build.WithDockerfile(`FROM `+testEnv.PlatformDefaults.BaseImage+` 5996 RUN net user user /add 5997 USER user 5998 RUN set username 5999 `)).Assert(c, icmd.Expected{ 6000 Out: "USERNAME=user", 6001 }) 6002 } 6003 6004 // Verifies if COPY file . when WORKDIR is set to a non-existing directory, 6005 // the directory is created and the file is copied into the directory, 6006 // as opposed to the file being copied as a file with the name of the 6007 // directory. Fix for 27545 (found on Windows, but regression good for Linux too). 6008 // Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514. 6009 func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *testing.T) { 6010 name := "testbuildcopyfiledotwithworkdir" 6011 buildImageSuccessfully(c, name, build.WithBuildContext(c, 6012 build.WithFile("Dockerfile", `FROM busybox 6013 WORKDIR /foo 6014 COPY file . 6015 RUN ["cat", "/foo/file"] 6016 `), 6017 build.WithFile("file", "content"), 6018 )) 6019 } 6020 6021 // Case-insensitive environment variables on Windows 6022 func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *testing.T) { 6023 testRequires(c, DaemonIsWindows) 6024 name := "testbuildwindowsenvcaseinsensitive" 6025 buildImageSuccessfully(c, name, build.WithDockerfile(` 6026 FROM `+testEnv.PlatformDefaults.BaseImage+` 6027 ENV FOO=bar foo=baz 6028 `)) 6029 res := inspectFieldJSON(c, name, "Config.Env") 6030 if res != `["foo=baz"]` { // Should not have FOO=bar in it - takes the last one processed. And only one entry as deduped. 6031 c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res) 6032 } 6033 } 6034 6035 // Test case for 29667 6036 func (s *DockerSuite) TestBuildWorkdirImageCmd(c *testing.T) { 6037 image := "testworkdirimagecmd" 6038 buildImageSuccessfully(c, image, build.WithDockerfile(` 6039 FROM busybox 6040 WORKDIR /foo/bar 6041 `)) 6042 out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 6043 assert.Equal(c, strings.TrimSpace(out), `["sh"]`) 6044 6045 image = "testworkdirlabelimagecmd" 6046 buildImageSuccessfully(c, image, build.WithDockerfile(` 6047 FROM busybox 6048 WORKDIR /foo/bar 6049 LABEL a=b 6050 `)) 6051 6052 out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 6053 assert.Equal(c, strings.TrimSpace(out), `["sh"]`) 6054 } 6055 6056 // Test case for 28902/28909 6057 func (s *DockerSuite) TestBuildWorkdirCmd(c *testing.T) { 6058 testRequires(c, DaemonIsLinux) 6059 name := "testbuildworkdircmd" 6060 dockerFile := ` 6061 FROM busybox 6062 WORKDIR / 6063 ` 6064 buildImageSuccessfully(c, name, build.WithDockerfile(dockerFile)) 6065 result := buildImage(name, build.WithDockerfile(dockerFile)) 6066 result.Assert(c, icmd.Success) 6067 assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 1) 6068 } 6069 6070 // FIXME(vdemeester) should be a unit test 6071 func (s *DockerSuite) TestBuildLineErrorOnBuild(c *testing.T) { 6072 name := "test_build_line_error_onbuild" 6073 buildImage(name, build.WithDockerfile(`FROM busybox 6074 ONBUILD 6075 `)).Assert(c, icmd.Expected{ 6076 ExitCode: 1, 6077 Err: "parse error line 2: ONBUILD requires at least one argument", 6078 }) 6079 } 6080 6081 // FIXME(vdemeester) should be a unit test 6082 func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *testing.T) { 6083 name := "test_build_line_error_unknown_instruction" 6084 cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox 6085 RUN echo hello world 6086 NOINSTRUCTION echo ba 6087 RUN echo hello 6088 ERROR 6089 `)).Assert(c, icmd.Expected{ 6090 ExitCode: 1, 6091 Err: "parse error line 3: unknown instruction: NOINSTRUCTION", 6092 }) 6093 } 6094 6095 // FIXME(vdemeester) should be a unit test 6096 func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *testing.T) { 6097 name := "test_build_line_error_with_empty_lines" 6098 cli.Docker(cli.Build(name), build.WithDockerfile(` 6099 FROM busybox 6100 6101 RUN echo hello world 6102 6103 NOINSTRUCTION echo ba 6104 6105 CMD ["/bin/init"] 6106 `)).Assert(c, icmd.Expected{ 6107 ExitCode: 1, 6108 Err: "parse error line 6: unknown instruction: NOINSTRUCTION", 6109 }) 6110 } 6111 6112 // FIXME(vdemeester) should be a unit test 6113 func (s *DockerSuite) TestBuildLineErrorWithComments(c *testing.T) { 6114 name := "test_build_line_error_with_comments" 6115 cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox 6116 # This will print hello world 6117 # and then ba 6118 RUN echo hello world 6119 NOINSTRUCTION echo ba 6120 `)).Assert(c, icmd.Expected{ 6121 ExitCode: 1, 6122 Err: "parse error line 5: unknown instruction: NOINSTRUCTION", 6123 }) 6124 } 6125 6126 // #31957 6127 func (s *DockerSuite) TestBuildSetCommandWithDefinedShell(c *testing.T) { 6128 buildImageSuccessfully(c, "build1", build.WithDockerfile(` 6129 FROM busybox 6130 SHELL ["/bin/sh", "-c"] 6131 `)) 6132 buildImageSuccessfully(c, "build2", build.WithDockerfile(` 6133 FROM build1 6134 CMD echo foo 6135 `)) 6136 6137 out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", "build2") 6138 expected := `["/bin/sh","-c","echo foo"]` 6139 if testEnv.OSType == "windows" { 6140 expected = `["/bin/sh -c echo foo"]` 6141 } 6142 assert.Equal(c, strings.TrimSpace(out), expected) 6143 } 6144 6145 // FIXME(vdemeester) should migrate to docker/cli tests 6146 func (s *DockerSuite) TestBuildIidFile(c *testing.T) { 6147 tmpDir, err := ioutil.TempDir("", "TestBuildIidFile") 6148 if err != nil { 6149 c.Fatal(err) 6150 } 6151 defer os.RemoveAll(tmpDir) 6152 tmpIidFile := filepath.Join(tmpDir, "iid") 6153 6154 name := "testbuildiidfile" 6155 // Use a Dockerfile with multiple stages to ensure we get the last one 6156 cli.BuildCmd(c, name, 6157 build.WithDockerfile(`FROM `+minimalBaseImage()+` AS stage1 6158 ENV FOO FOO 6159 FROM `+minimalBaseImage()+` 6160 ENV BAR BAZ`), 6161 cli.WithFlags("--iidfile", tmpIidFile)) 6162 6163 id, err := ioutil.ReadFile(tmpIidFile) 6164 assert.NilError(c, err) 6165 d, err := digest.Parse(string(id)) 6166 assert.NilError(c, err) 6167 assert.Equal(c, d.String(), getIDByName(c, name)) 6168 } 6169 6170 // FIXME(vdemeester) should migrate to docker/cli tests 6171 func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *testing.T) { 6172 tmpDir, err := ioutil.TempDir("", "TestBuildIidFileCleanupOnFail") 6173 if err != nil { 6174 c.Fatal(err) 6175 } 6176 defer os.RemoveAll(tmpDir) 6177 tmpIidFile := filepath.Join(tmpDir, "iid") 6178 6179 err = ioutil.WriteFile(tmpIidFile, []byte("Dummy"), 0666) 6180 assert.NilError(c, err) 6181 6182 cli.Docker(cli.Build("testbuildiidfilecleanuponfail"), 6183 build.WithDockerfile(`FROM `+minimalBaseImage()+` 6184 RUN /non/existing/command`), 6185 cli.WithFlags("--iidfile", tmpIidFile)).Assert(c, icmd.Expected{ 6186 ExitCode: 1, 6187 }) 6188 _, err = os.Stat(tmpIidFile) 6189 assert.ErrorContains(c, err, "") 6190 assert.Equal(c, os.IsNotExist(err), true) 6191 }