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