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 3335 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3336 CMD [ '/bin/sh', '-c', 'echo hi' ]`)) 3337 3338 icmd.RunCommand(dockerBinary, "run", "--rm", name).Assert(c, icmd.Expected{ 3339 ExitCode: expectedExitCode, 3340 }) 3341} 3342 3343func (s *DockerSuite) TestBuildVerboseOut(c *check.C) { 3344 name := "testbuildverboseout" 3345 expected := "\n123\n" 3346 3347 if testEnv.OSType == "windows" { 3348 expected = "\n123\r\n" 3349 } 3350 3351 buildImage(name, build.WithDockerfile(`FROM busybox 3352RUN echo 123`)).Assert(c, icmd.Expected{ 3353 Out: expected, 3354 }) 3355} 3356 3357func (s *DockerSuite) TestBuildWithTabs(c *check.C) { 3358 name := "testbuildwithtabs" 3359 buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nRUN echo\tone\t\ttwo")) 3360 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 3361 expected1 := `["/bin/sh","-c","echo\tone\t\ttwo"]` 3362 expected2 := `["/bin/sh","-c","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3363 if testEnv.OSType == "windows" { 3364 expected1 = `["cmd","/S","/C","echo\tone\t\ttwo"]` 3365 expected2 = `["cmd","/S","/C","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates 3366 } 3367 if res != expected1 && res != expected2 { 3368 c.Fatalf("Missing tabs.\nGot: %s\nExp: %s or %s", res, expected1, expected2) 3369 } 3370} 3371 3372func (s *DockerSuite) TestBuildLabels(c *check.C) { 3373 name := "testbuildlabel" 3374 expected := `{"License":"GPL","Vendor":"Acme"}` 3375 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3376 LABEL Vendor=Acme 3377 LABEL License GPL`)) 3378 res := inspectFieldJSON(c, name, "Config.Labels") 3379 if res != expected { 3380 c.Fatalf("Labels %s, expected %s", res, expected) 3381 } 3382} 3383 3384func (s *DockerSuite) TestBuildLabelsCache(c *check.C) { 3385 name := "testbuildlabelcache" 3386 3387 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3388 LABEL Vendor=Acme`)) 3389 id1 := getIDByName(c, name) 3390 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3391 LABEL Vendor=Acme`)) 3392 id2 := getIDByName(c, name) 3393 if id1 != id2 { 3394 c.Fatalf("Build 2 should have worked & used cache(%s,%s)", id1, id2) 3395 } 3396 3397 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3398 LABEL Vendor=Acme1`)) 3399 id2 = getIDByName(c, name) 3400 if id1 == id2 { 3401 c.Fatalf("Build 3 should have worked & NOT used cache(%s,%s)", id1, id2) 3402 } 3403 3404 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3405 LABEL Vendor Acme`)) 3406 id2 = getIDByName(c, name) 3407 if id1 != id2 { 3408 c.Fatalf("Build 4 should have worked & used cache(%s,%s)", id1, id2) 3409 } 3410 3411 // Now make sure the cache isn't used by mistake 3412 buildImageSuccessfully(c, name, build.WithoutCache, build.WithDockerfile(`FROM busybox 3413 LABEL f1=b1 f2=b2`)) 3414 3415 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 3416 LABEL f1=b1 f2=b2`)) 3417 id2 = getIDByName(c, name) 3418 if id1 == id2 { 3419 c.Fatalf("Build 6 should have worked & NOT used the cache(%s,%s)", id1, id2) 3420 } 3421 3422} 3423 3424// FIXME(vdemeester) port to docker/cli e2e tests (api tests should test suppressOutput option though) 3425func (s *DockerSuite) TestBuildNotVerboseSuccess(c *check.C) { 3426 // This test makes sure that -q works correctly when build is successful: 3427 // stdout has only the image ID (long image ID) and stderr is empty. 3428 outRegexp := regexp.MustCompile("^(sha256:|)[a-z0-9]{64}\\n$") 3429 buildFlags := cli.WithFlags("-q") 3430 3431 tt := []struct { 3432 Name string 3433 BuildFunc func(string) *icmd.Result 3434 }{ 3435 { 3436 Name: "quiet_build_stdin_success", 3437 BuildFunc: func(name string) *icmd.Result { 3438 return buildImage(name, buildFlags, build.WithDockerfile("FROM busybox")) 3439 }, 3440 }, 3441 { 3442 Name: "quiet_build_ctx_success", 3443 BuildFunc: func(name string) *icmd.Result { 3444 return buildImage(name, buildFlags, build.WithBuildContext(c, 3445 build.WithFile("Dockerfile", "FROM busybox"), 3446 build.WithFile("quiet_build_success_fctx", "test"), 3447 )) 3448 }, 3449 }, 3450 { 3451 Name: "quiet_build_git_success", 3452 BuildFunc: func(name string) *icmd.Result { 3453 git := fakegit.New(c, "repo", map[string]string{ 3454 "Dockerfile": "FROM busybox", 3455 }, true) 3456 return buildImage(name, buildFlags, build.WithContextPath(git.RepoURL)) 3457 }, 3458 }, 3459 } 3460 3461 for _, te := range tt { 3462 result := te.BuildFunc(te.Name) 3463 result.Assert(c, icmd.Success) 3464 if outRegexp.Find([]byte(result.Stdout())) == nil { 3465 c.Fatalf("Test %s expected stdout to match the [%v] regexp, but it is [%v]", te.Name, outRegexp, result.Stdout()) 3466 } 3467 3468 if result.Stderr() != "" { 3469 c.Fatalf("Test %s expected stderr to be empty, but it is [%#v]", te.Name, result.Stderr()) 3470 } 3471 } 3472 3473} 3474 3475// FIXME(vdemeester) migrate to docker/cli tests 3476func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *check.C) { 3477 // This test makes sure that -q works correctly when build fails by 3478 // comparing between the stderr output in quiet mode and in stdout 3479 // and stderr output in verbose mode 3480 testRequires(c, Network) 3481 testName := "quiet_build_not_exists_image" 3482 dockerfile := "FROM busybox11" 3483 quietResult := buildImage(testName, cli.WithFlags("-q"), build.WithDockerfile(dockerfile)) 3484 quietResult.Assert(c, icmd.Expected{ 3485 ExitCode: 1, 3486 }) 3487 result := buildImage(testName, build.WithDockerfile(dockerfile)) 3488 result.Assert(c, icmd.Expected{ 3489 ExitCode: 1, 3490 }) 3491 if quietResult.Stderr() != result.Combined() { 3492 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", testName, quietResult.Stderr(), result.Combined())) 3493 } 3494} 3495 3496// FIXME(vdemeester) migrate to docker/cli tests 3497func (s *DockerSuite) TestBuildNotVerboseFailure(c *check.C) { 3498 // This test makes sure that -q works correctly when build fails by 3499 // comparing between the stderr output in quiet mode and in stdout 3500 // and stderr output in verbose mode 3501 testCases := []struct { 3502 testName string 3503 dockerfile string 3504 }{ 3505 {"quiet_build_no_from_at_the_beginning", "RUN whoami"}, 3506 {"quiet_build_unknown_instr", "FROMD busybox"}, 3507 } 3508 3509 for _, tc := range testCases { 3510 quietResult := buildImage(tc.testName, cli.WithFlags("-q"), build.WithDockerfile(tc.dockerfile)) 3511 quietResult.Assert(c, icmd.Expected{ 3512 ExitCode: 1, 3513 }) 3514 result := buildImage(tc.testName, build.WithDockerfile(tc.dockerfile)) 3515 result.Assert(c, icmd.Expected{ 3516 ExitCode: 1, 3517 }) 3518 if quietResult.Stderr() != result.Combined() { 3519 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())) 3520 } 3521 } 3522} 3523 3524// FIXME(vdemeester) migrate to docker/cli tests 3525func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *check.C) { 3526 // This test ensures that when given a wrong URL, stderr in quiet mode and 3527 // stderr in verbose mode are identical. 3528 // TODO(vdemeester) with cobra, stdout has a carriage return too much so this test should not check stdout 3529 URL := "http://something.invalid" 3530 name := "quiet_build_wrong_remote" 3531 quietResult := buildImage(name, cli.WithFlags("-q"), build.WithContextPath(URL)) 3532 quietResult.Assert(c, icmd.Expected{ 3533 ExitCode: 1, 3534 }) 3535 result := buildImage(name, build.WithContextPath(URL)) 3536 result.Assert(c, icmd.Expected{ 3537 ExitCode: 1, 3538 }) 3539 3540 // An error message should contain name server IP and port, like this: 3541 // "dial tcp: lookup something.invalid on 172.29.128.11:53: no such host" 3542 // The IP:port need to be removed in order to not trigger a test failur 3543 // when more than one nameserver is configured. 3544 // While at it, also strip excessive newlines. 3545 normalize := func(msg string) string { 3546 return strings.TrimSpace(regexp.MustCompile("[1-9][0-9.]+:[0-9]+").ReplaceAllLiteralString(msg, "<ip:port>")) 3547 } 3548 3549 if normalize(quietResult.Stderr()) != normalize(result.Combined()) { 3550 c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", name, quietResult.Stderr(), result.Combined())) 3551 } 3552} 3553 3554// FIXME(vdemeester) migrate to docker/cli tests 3555func (s *DockerSuite) TestBuildStderr(c *check.C) { 3556 // This test just makes sure that no non-error output goes 3557 // to stderr 3558 name := "testbuildstderr" 3559 result := buildImage(name, build.WithDockerfile("FROM busybox\nRUN echo one")) 3560 result.Assert(c, icmd.Success) 3561 3562 // Windows to non-Windows should have a security warning 3563 if runtime.GOOS == "windows" && testEnv.OSType != "windows" && !strings.Contains(result.Stdout(), "SECURITY WARNING:") { 3564 c.Fatalf("Stdout contains unexpected output: %q", result.Stdout()) 3565 } 3566 3567 // Stderr should always be empty 3568 if result.Stderr() != "" { 3569 c.Fatalf("Stderr should have been empty, instead it's: %q", result.Stderr()) 3570 } 3571} 3572 3573func (s *DockerSuite) TestBuildChownSingleFile(c *check.C) { 3574 testRequires(c, UnixCli, DaemonIsLinux) // test uses chown: not available on windows 3575 3576 name := "testbuildchownsinglefile" 3577 3578 ctx := fakecontext.New(c, "", 3579 fakecontext.WithDockerfile(` 3580FROM busybox 3581COPY test / 3582RUN ls -l /test 3583RUN [ $(ls -l /test | awk '{print $3":"$4}') = 'root:root' ] 3584`), 3585 fakecontext.WithFiles(map[string]string{ 3586 "test": "test", 3587 })) 3588 defer ctx.Close() 3589 3590 if err := os.Chown(filepath.Join(ctx.Dir, "test"), 4242, 4242); err != nil { 3591 c.Fatal(err) 3592 } 3593 3594 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 3595} 3596 3597func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) { 3598 name := "testbuildsymlinkbreakout" 3599 tmpdir, err := ioutil.TempDir("", name) 3600 c.Assert(err, check.IsNil) 3601 defer os.RemoveAll(tmpdir) 3602 ctx := filepath.Join(tmpdir, "context") 3603 if err := os.MkdirAll(ctx, 0755); err != nil { 3604 c.Fatal(err) 3605 } 3606 if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(` 3607 from busybox 3608 add symlink.tar / 3609 add inject /symlink/ 3610 `), 0644); err != nil { 3611 c.Fatal(err) 3612 } 3613 inject := filepath.Join(ctx, "inject") 3614 if err := ioutil.WriteFile(inject, nil, 0644); err != nil { 3615 c.Fatal(err) 3616 } 3617 f, err := os.Create(filepath.Join(ctx, "symlink.tar")) 3618 if err != nil { 3619 c.Fatal(err) 3620 } 3621 w := tar.NewWriter(f) 3622 w.WriteHeader(&tar.Header{ 3623 Name: "symlink2", 3624 Typeflag: tar.TypeSymlink, 3625 Linkname: "/../../../../../../../../../../../../../../", 3626 Uid: os.Getuid(), 3627 Gid: os.Getgid(), 3628 }) 3629 w.WriteHeader(&tar.Header{ 3630 Name: "symlink", 3631 Typeflag: tar.TypeSymlink, 3632 Linkname: filepath.Join("symlink2", tmpdir), 3633 Uid: os.Getuid(), 3634 Gid: os.Getgid(), 3635 }) 3636 w.Close() 3637 f.Close() 3638 3639 buildImageSuccessfully(c, name, build.WithoutCache, build.WithExternalBuildContext(fakecontext.New(c, ctx))) 3640 if _, err := os.Lstat(filepath.Join(tmpdir, "inject")); err == nil { 3641 c.Fatal("symlink breakout - inject") 3642 } else if !os.IsNotExist(err) { 3643 c.Fatalf("unexpected error: %v", err) 3644 } 3645} 3646 3647func (s *DockerSuite) TestBuildXZHost(c *check.C) { 3648 // /usr/local/sbin/xz gets permission denied for the user 3649 testRequires(c, NotUserNamespace) 3650 testRequires(c, DaemonIsLinux) 3651 name := "testbuildxzhost" 3652 3653 buildImageSuccessfully(c, name, build.WithBuildContext(c, 3654 build.WithFile("Dockerfile", ` 3655FROM busybox 3656ADD xz /usr/local/sbin/ 3657RUN chmod 755 /usr/local/sbin/xz 3658ADD test.xz / 3659RUN [ ! -e /injected ]`), 3660 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"), 3661 build.WithFile("xz", "#!/bin/sh\ntouch /injected"), 3662 )) 3663} 3664 3665func (s *DockerSuite) TestBuildVolumesRetainContents(c *check.C) { 3666 // /foo/file gets permission denied for the user 3667 testRequires(c, NotUserNamespace) 3668 testRequires(c, DaemonIsLinux) // TODO Windows: Issue #20127 3669 var ( 3670 name = "testbuildvolumescontent" 3671 expected = "some text" 3672 volName = "/foo" 3673 ) 3674 3675 if testEnv.OSType == "windows" { 3676 volName = "C:/foo" 3677 } 3678 3679 buildImageSuccessfully(c, name, build.WithBuildContext(c, 3680 build.WithFile("Dockerfile", ` 3681FROM busybox 3682COPY content /foo/file 3683VOLUME `+volName+` 3684CMD cat /foo/file`), 3685 build.WithFile("content", expected), 3686 )) 3687 3688 out, _ := dockerCmd(c, "run", "--rm", name) 3689 if out != expected { 3690 c.Fatalf("expected file contents for /foo/file to be %q but received %q", expected, out) 3691 } 3692 3693} 3694 3695func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) { 3696 testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows 3697 testRequires(c, DaemonIsLinux) 3698 3699 // If Dockerfile is not present, use dockerfile 3700 buildImage("test1", build.WithBuildContext(c, 3701 build.WithFile("dockerfile", `FROM busybox 3702 RUN echo from dockerfile`), 3703 )).Assert(c, icmd.Expected{ 3704 Out: "from dockerfile", 3705 }) 3706 3707 // Prefer Dockerfile in place of dockerfile 3708 buildImage("test1", build.WithBuildContext(c, 3709 build.WithFile("dockerfile", `FROM busybox 3710 RUN echo from dockerfile`), 3711 build.WithFile("Dockerfile", `FROM busybox 3712 RUN echo from Dockerfile`), 3713 )).Assert(c, icmd.Expected{ 3714 Out: "from Dockerfile", 3715 }) 3716} 3717 3718// FIXME(vdemeester) should migrate to docker/cli tests 3719func (s *DockerSuite) TestBuildFromURLWithF(c *check.C) { 3720 server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"baz": `FROM busybox 3721RUN echo from baz 3722COPY * /tmp/ 3723RUN find /tmp/`})) 3724 defer server.Close() 3725 3726 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox 3727 RUN echo from Dockerfile`)) 3728 defer ctx.Close() 3729 3730 // Make sure that -f is ignored and that we don't use the Dockerfile 3731 // that's in the current dir 3732 result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", server.URL()+"/baz"), func(cmd *icmd.Cmd) func() { 3733 cmd.Dir = ctx.Dir 3734 return nil 3735 }) 3736 3737 if !strings.Contains(result.Combined(), "from baz") || 3738 strings.Contains(result.Combined(), "/tmp/baz") || 3739 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3740 c.Fatalf("Missing proper output: %s", result.Combined()) 3741 } 3742 3743} 3744 3745// FIXME(vdemeester) should migrate to docker/cli tests 3746func (s *DockerSuite) TestBuildFromStdinWithF(c *check.C) { 3747 testRequires(c, DaemonIsLinux) // TODO Windows: This test is flaky; no idea why 3748 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox 3749RUN echo "from Dockerfile"`)) 3750 defer ctx.Close() 3751 3752 // Make sure that -f is ignored and that we don't use the Dockerfile 3753 // that's in the current dir 3754 result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", "-"), func(cmd *icmd.Cmd) func() { 3755 cmd.Dir = ctx.Dir 3756 cmd.Stdin = strings.NewReader(`FROM busybox 3757RUN echo "from baz" 3758COPY * /tmp/ 3759RUN sh -c "find /tmp/" # sh -c is needed on Windows to use the correct find`) 3760 return nil 3761 }) 3762 3763 if !strings.Contains(result.Combined(), "from baz") || 3764 strings.Contains(result.Combined(), "/tmp/baz") || 3765 !strings.Contains(result.Combined(), "/tmp/Dockerfile") { 3766 c.Fatalf("Missing proper output: %s", result.Combined()) 3767 } 3768 3769} 3770 3771func (s *DockerSuite) TestBuildFromOfficialNames(c *check.C) { 3772 name := "testbuildfromofficial" 3773 fromNames := []string{ 3774 "busybox", 3775 "docker.io/busybox", 3776 "index.docker.io/busybox", 3777 "library/busybox", 3778 "docker.io/library/busybox", 3779 "index.docker.io/library/busybox", 3780 } 3781 for idx, fromName := range fromNames { 3782 imgName := fmt.Sprintf("%s%d", name, idx) 3783 buildImageSuccessfully(c, imgName, build.WithDockerfile("FROM "+fromName)) 3784 dockerCmd(c, "rmi", imgName) 3785 } 3786} 3787 3788// FIXME(vdemeester) should be a unit test 3789func (s *DockerSuite) TestBuildSpaces(c *check.C) { 3790 // Test to make sure that leading/trailing spaces on a command 3791 // doesn't change the error msg we get 3792 name := "testspaces" 3793 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile("FROM busybox\nCOPY\n")) 3794 defer ctx.Close() 3795 3796 result1 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx)) 3797 result1.Assert(c, icmd.Expected{ 3798 ExitCode: 1, 3799 }) 3800 3801 ctx.Add("Dockerfile", "FROM busybox\nCOPY ") 3802 result2 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx)) 3803 result2.Assert(c, icmd.Expected{ 3804 ExitCode: 1, 3805 }) 3806 3807 removeLogTimestamps := func(s string) string { 3808 return regexp.MustCompile(`time="(.*?)"`).ReplaceAllString(s, `time=[TIMESTAMP]`) 3809 } 3810 3811 // Skip over the times 3812 e1 := removeLogTimestamps(result1.Error.Error()) 3813 e2 := removeLogTimestamps(result2.Error.Error()) 3814 3815 // Ignore whitespace since that's what were verifying doesn't change stuff 3816 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3817 c.Fatalf("Build 2's error wasn't the same as build 1's\n1:%s\n2:%s", result1.Error, result2.Error) 3818 } 3819 3820 ctx.Add("Dockerfile", "FROM busybox\n COPY") 3821 result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx)) 3822 result2.Assert(c, icmd.Expected{ 3823 ExitCode: 1, 3824 }) 3825 3826 // Skip over the times 3827 e1 = removeLogTimestamps(result1.Error.Error()) 3828 e2 = removeLogTimestamps(result2.Error.Error()) 3829 3830 // Ignore whitespace since that's what were verifying doesn't change stuff 3831 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3832 c.Fatalf("Build 3's error wasn't the same as build 1's\n1:%s\n3:%s", result1.Error, result2.Error) 3833 } 3834 3835 ctx.Add("Dockerfile", "FROM busybox\n COPY ") 3836 result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx)) 3837 result2.Assert(c, icmd.Expected{ 3838 ExitCode: 1, 3839 }) 3840 3841 // Skip over the times 3842 e1 = removeLogTimestamps(result1.Error.Error()) 3843 e2 = removeLogTimestamps(result2.Error.Error()) 3844 3845 // Ignore whitespace since that's what were verifying doesn't change stuff 3846 if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) { 3847 c.Fatalf("Build 4's error wasn't the same as build 1's\n1:%s\n4:%s", result1.Error, result2.Error) 3848 } 3849 3850} 3851 3852func (s *DockerSuite) TestBuildSpacesWithQuotes(c *check.C) { 3853 // Test to make sure that spaces in quotes aren't lost 3854 name := "testspacesquotes" 3855 3856 dockerfile := `FROM busybox 3857RUN echo " \ 3858 foo "` 3859 3860 expected := "\n foo \n" 3861 // Windows uses the builtin echo, which preserves quotes 3862 if testEnv.OSType == "windows" { 3863 expected = "\" foo \"" 3864 } 3865 3866 buildImage(name, build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3867 Out: expected, 3868 }) 3869} 3870 3871// #4393 3872func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *check.C) { 3873 testRequires(c, DaemonIsLinux) // TODO Windows: This should error out 3874 buildImage("docker-test-errcreatevolumewithfile", build.WithDockerfile(` 3875 FROM busybox 3876 RUN touch /foo 3877 VOLUME /foo 3878 `)).Assert(c, icmd.Expected{ 3879 ExitCode: 1, 3880 Err: "file exists", 3881 }) 3882} 3883 3884// FIXME(vdemeester) should be a unit test 3885func (s *DockerSuite) TestBuildMissingArgs(c *check.C) { 3886 // Test to make sure that all Dockerfile commands (except the ones listed 3887 // in skipCmds) will generate an error if no args are provided. 3888 // Note: INSERT is deprecated so we exclude it because of that. 3889 skipCmds := map[string]struct{}{ 3890 "CMD": {}, 3891 "RUN": {}, 3892 "ENTRYPOINT": {}, 3893 "INSERT": {}, 3894 } 3895 3896 if testEnv.OSType == "windows" { 3897 skipCmds = map[string]struct{}{ 3898 "CMD": {}, 3899 "RUN": {}, 3900 "ENTRYPOINT": {}, 3901 "INSERT": {}, 3902 "STOPSIGNAL": {}, 3903 "ARG": {}, 3904 "USER": {}, 3905 "EXPOSE": {}, 3906 } 3907 } 3908 3909 for cmd := range command.Commands { 3910 cmd = strings.ToUpper(cmd) 3911 if _, ok := skipCmds[cmd]; ok { 3912 continue 3913 } 3914 var dockerfile string 3915 if cmd == "FROM" { 3916 dockerfile = cmd 3917 } else { 3918 // Add FROM to make sure we don't complain about it missing 3919 dockerfile = "FROM busybox\n" + cmd 3920 } 3921 3922 buildImage("args", build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{ 3923 ExitCode: 1, 3924 Err: cmd + " requires", 3925 }) 3926 } 3927 3928} 3929 3930func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) { 3931 testRequires(c, DaemonIsLinux) 3932 buildImage("sc", build.WithDockerfile("FROM scratch")).Assert(c, icmd.Expected{ 3933 ExitCode: 1, 3934 Err: "No image was generated", 3935 }) 3936} 3937 3938func (s *DockerSuite) TestBuildDotDotFile(c *check.C) { 3939 buildImageSuccessfully(c, "sc", build.WithBuildContext(c, 3940 build.WithFile("Dockerfile", "FROM busybox\n"), 3941 build.WithFile("..gitme", ""), 3942 )) 3943} 3944 3945func (s *DockerSuite) TestBuildRUNoneJSON(c *check.C) { 3946 testRequires(c, DaemonIsLinux) // No hello-world Windows image 3947 name := "testbuildrunonejson" 3948 3949 buildImage(name, build.WithDockerfile(`FROM hello-world:frozen 3950RUN [ "/hello" ]`)).Assert(c, icmd.Expected{ 3951 Out: "Hello from Docker", 3952 }) 3953} 3954 3955func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { 3956 name := "testbuildemptystringvolume" 3957 3958 buildImage(name, build.WithDockerfile(` 3959 FROM busybox 3960 ENV foo="" 3961 VOLUME $foo 3962 `)).Assert(c, icmd.Expected{ 3963 ExitCode: 1, 3964 }) 3965} 3966 3967func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *check.C) { 3968 testRequires(c, SameHostDaemon, DaemonIsLinux) 3969 3970 cgroupParent := "test" 3971 data, err := ioutil.ReadFile("/proc/self/cgroup") 3972 if err != nil { 3973 c.Fatalf("failed to read '/proc/self/cgroup - %v", err) 3974 } 3975 selfCgroupPaths := ParseCgroupPaths(string(data)) 3976 _, found := selfCgroupPaths["memory"] 3977 if !found { 3978 c.Fatalf("unable to find self memory cgroup path. CgroupsPath: %v", selfCgroupPaths) 3979 } 3980 result := buildImage("buildcgroupparent", 3981 cli.WithFlags("--cgroup-parent", cgroupParent), 3982 build.WithDockerfile(` 3983FROM busybox 3984RUN cat /proc/self/cgroup 3985`)) 3986 result.Assert(c, icmd.Success) 3987 m, err := regexp.MatchString(fmt.Sprintf("memory:.*/%s/.*", cgroupParent), result.Combined()) 3988 c.Assert(err, check.IsNil) 3989 if !m { 3990 c.Fatalf("There is no expected memory cgroup with parent /%s/: %s", cgroupParent, result.Combined()) 3991 } 3992} 3993 3994// FIXME(vdemeester) could be a unit test 3995func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) { 3996 // Check to make sure our build output prints the Dockerfile cmd 3997 // property - there was a bug that caused it to be duplicated on the 3998 // Step X line 3999 name := "testbuildnodupoutput" 4000 result := buildImage(name, build.WithDockerfile(` 4001 FROM busybox 4002 RUN env`)) 4003 result.Assert(c, icmd.Success) 4004 exp := "\nStep 2/2 : RUN env\n" 4005 if !strings.Contains(result.Combined(), exp) { 4006 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4007 } 4008} 4009 4010// GH15826 4011// FIXME(vdemeester) could be a unit test 4012func (s *DockerSuite) TestBuildStartsFromOne(c *check.C) { 4013 // Explicit check to ensure that build starts from step 1 rather than 0 4014 name := "testbuildstartsfromone" 4015 result := buildImage(name, build.WithDockerfile(`FROM busybox`)) 4016 result.Assert(c, icmd.Success) 4017 exp := "\nStep 1/1 : FROM busybox\n" 4018 if !strings.Contains(result.Combined(), exp) { 4019 c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp) 4020 } 4021} 4022 4023func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) { 4024 // Test to make sure the bad command is quoted with just "s and 4025 // not as a Go []string 4026 name := "testbuildbadrunerrmsg" 4027 shell := "/bin/sh -c" 4028 exitCode := 127 4029 if testEnv.OSType == "windows" { 4030 shell = "cmd /S /C" 4031 // architectural - Windows has to start the container to determine the exe is bad, Linux does not 4032 exitCode = 1 4033 } 4034 exp := fmt.Sprintf(`The command '%s badEXE a1 \& a2 a3' returned a non-zero code: %d`, shell, exitCode) 4035 4036 buildImage(name, build.WithDockerfile(` 4037 FROM busybox 4038 RUN badEXE a1 \& a2 a3`)).Assert(c, icmd.Expected{ 4039 ExitCode: exitCode, 4040 Err: exp, 4041 }) 4042} 4043 4044// Issue #15634: COPY fails when path starts with "null" 4045func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) { 4046 name := "testbuildnullstringinaddcopyvolume" 4047 volName := "nullvolume" 4048 if testEnv.OSType == "windows" { 4049 volName = `C:\\nullvolume` 4050 } 4051 4052 buildImageSuccessfully(c, name, build.WithBuildContext(c, 4053 build.WithFile("Dockerfile", ` 4054 FROM busybox 4055 4056 ADD null / 4057 COPY nullfile / 4058 VOLUME `+volName+` 4059 `), 4060 build.WithFile("null", "test1"), 4061 build.WithFile("nullfile", "test2"), 4062 )) 4063} 4064 4065func (s *DockerSuite) TestBuildStopSignal(c *check.C) { 4066 testRequires(c, DaemonIsLinux) // Windows does not support STOPSIGNAL yet 4067 imgName := "test_build_stop_signal" 4068 buildImageSuccessfully(c, imgName, build.WithDockerfile(`FROM busybox 4069 STOPSIGNAL SIGKILL`)) 4070 res := inspectFieldJSON(c, imgName, "Config.StopSignal") 4071 if res != `"SIGKILL"` { 4072 c.Fatalf("Signal %s, expected SIGKILL", res) 4073 } 4074 4075 containerName := "test-container-stop-signal" 4076 dockerCmd(c, "run", "-d", "--name", containerName, imgName, "top") 4077 res = inspectFieldJSON(c, containerName, "Config.StopSignal") 4078 if res != `"SIGKILL"` { 4079 c.Fatalf("Signal %s, expected SIGKILL", res) 4080 } 4081} 4082 4083func (s *DockerSuite) TestBuildBuildTimeArg(c *check.C) { 4084 imgName := "bldargtest" 4085 envKey := "foo" 4086 envVal := "bar" 4087 var dockerfile string 4088 if testEnv.OSType == "windows" { 4089 // Bugs in Windows busybox port - use the default base image and native cmd stuff 4090 dockerfile = fmt.Sprintf(`FROM `+minimalBaseImage()+` 4091 ARG %s 4092 RUN echo %%%s%% 4093 CMD setlocal enableextensions && if defined %s (echo %%%s%%)`, envKey, envKey, envKey, envKey) 4094 } else { 4095 dockerfile = fmt.Sprintf(`FROM busybox 4096 ARG %s 4097 RUN echo $%s 4098 CMD echo $%s`, envKey, envKey, envKey) 4099 4100 } 4101 buildImage(imgName, 4102 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4103 build.WithDockerfile(dockerfile), 4104 ).Assert(c, icmd.Expected{ 4105 Out: envVal, 4106 }) 4107 4108 containerName := "bldargCont" 4109 out, _ := dockerCmd(c, "run", "--name", containerName, imgName) 4110 out = strings.Trim(out, " \r\n'") 4111 if out != "" { 4112 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4113 } 4114} 4115 4116func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) { 4117 imgName := "bldargtest" 4118 envKey := "foo" 4119 envVal := "bar" 4120 envDef := "bar1" 4121 dockerfile := fmt.Sprintf(`FROM busybox 4122 ARG %s=%s`, envKey, envDef) 4123 buildImage(imgName, 4124 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4125 build.WithDockerfile(dockerfile), 4126 ).Assert(c, icmd.Expected{ 4127 Out: envVal, 4128 }) 4129 4130 out, _ := dockerCmd(c, "history", "--no-trunc", imgName) 4131 outputTabs := strings.Split(out, "\n")[1] 4132 if !strings.Contains(outputTabs, envDef) { 4133 c.Fatalf("failed to find arg default in image history output: %q expected: %q", outputTabs, envDef) 4134 } 4135} 4136 4137func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *check.C) { 4138 imgName := "bldargtest" 4139 envKey := "foo" 4140 envVal := "bar" 4141 proxy := "HTTP_PROXY=http://user:password@proxy.example.com" 4142 explicitProxyKey := "http_proxy" 4143 explicitProxyVal := "http://user:password@someproxy.example.com" 4144 dockerfile := fmt.Sprintf(`FROM busybox 4145 ARG %s 4146 ARG %s 4147 RUN echo "Testing Build Args!"`, envKey, explicitProxyKey) 4148 4149 buildImage := func(imgName string) string { 4150 cli.BuildCmd(c, imgName, 4151 cli.WithFlags("--build-arg", "https_proxy=https://proxy.example.com", 4152 "--build-arg", fmt.Sprintf("%s=%s", envKey, envVal), 4153 "--build-arg", fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal), 4154 "--build-arg", proxy), 4155 build.WithDockerfile(dockerfile), 4156 ) 4157 return getIDByName(c, imgName) 4158 } 4159 4160 origID := buildImage(imgName) 4161 result := cli.DockerCmd(c, "history", "--no-trunc", imgName) 4162 out := result.Stdout() 4163 4164 if strings.Contains(out, proxy) { 4165 c.Fatalf("failed to exclude proxy settings from history!") 4166 } 4167 if strings.Contains(out, "https_proxy") { 4168 c.Fatalf("failed to exclude proxy settings from history!") 4169 } 4170 result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", envKey, envVal)}) 4171 result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal)}) 4172 4173 cacheID := buildImage(imgName + "-two") 4174 c.Assert(origID, checker.Equals, cacheID) 4175} 4176 4177func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) { 4178 imgName := "bldargtest" 4179 envKey := "foo" 4180 envVal := "bar" 4181 dockerfile := fmt.Sprintf(`FROM busybox 4182 ARG %s 4183 RUN echo $%s`, envKey, envKey) 4184 buildImageSuccessfully(c, imgName, 4185 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4186 build.WithDockerfile(dockerfile), 4187 ) 4188 origImgID := getIDByName(c, imgName) 4189 4190 imgNameCache := "bldargtestcachehit" 4191 buildImageSuccessfully(c, imgNameCache, 4192 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4193 build.WithDockerfile(dockerfile), 4194 ) 4195 newImgID := getIDByName(c, imgName) 4196 if newImgID != origImgID { 4197 c.Fatalf("build didn't use cache! expected image id: %q built image id: %q", origImgID, newImgID) 4198 } 4199} 4200 4201func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *check.C) { 4202 imgName := "bldargtest" 4203 envKey := "foo" 4204 envVal := "bar" 4205 extraEnvKey := "foo1" 4206 extraEnvVal := "bar1" 4207 dockerfile := fmt.Sprintf(`FROM busybox 4208 ARG %s 4209 ARG %s 4210 RUN echo $%s`, envKey, extraEnvKey, envKey) 4211 buildImageSuccessfully(c, imgName, 4212 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4213 build.WithDockerfile(dockerfile), 4214 ) 4215 origImgID := getIDByName(c, imgName) 4216 4217 imgNameCache := "bldargtestcachemiss" 4218 buildImageSuccessfully(c, imgNameCache, 4219 cli.WithFlags( 4220 "--build-arg", fmt.Sprintf("%s=%s", envKey, envVal), 4221 "--build-arg", fmt.Sprintf("%s=%s", extraEnvKey, extraEnvVal), 4222 ), 4223 build.WithDockerfile(dockerfile), 4224 ) 4225 newImgID := getIDByName(c, imgNameCache) 4226 4227 if newImgID == origImgID { 4228 c.Fatalf("build used cache, expected a miss!") 4229 } 4230} 4231 4232func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *check.C) { 4233 imgName := "bldargtest" 4234 envKey := "foo" 4235 envVal := "bar" 4236 newEnvVal := "bar1" 4237 dockerfile := fmt.Sprintf(`FROM busybox 4238 ARG %s 4239 RUN echo $%s`, envKey, envKey) 4240 buildImageSuccessfully(c, imgName, 4241 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4242 build.WithDockerfile(dockerfile), 4243 ) 4244 origImgID := getIDByName(c, imgName) 4245 4246 imgNameCache := "bldargtestcachemiss" 4247 buildImageSuccessfully(c, imgNameCache, 4248 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, newEnvVal)), 4249 build.WithDockerfile(dockerfile), 4250 ) 4251 newImgID := getIDByName(c, imgNameCache) 4252 if newImgID == origImgID { 4253 c.Fatalf("build used cache, expected a miss!") 4254 } 4255} 4256 4257func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *check.C) { 4258 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4259 imgName := "bldargtest" 4260 envKey := "foo" 4261 envVal := "bar" 4262 envValOverride := "barOverride" 4263 dockerfile := fmt.Sprintf(`FROM busybox 4264 ARG %s 4265 ENV %s %s 4266 RUN echo $%s 4267 CMD echo $%s 4268 `, envKey, envKey, envValOverride, envKey, envKey) 4269 4270 result := buildImage(imgName, 4271 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4272 build.WithDockerfile(dockerfile), 4273 ) 4274 result.Assert(c, icmd.Success) 4275 if strings.Count(result.Combined(), envValOverride) != 2 { 4276 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4277 } 4278 4279 containerName := "bldargCont" 4280 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4281 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4282 } 4283} 4284 4285// FIXME(vdemeester) might be useful to merge with the one above ? 4286func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *check.C) { 4287 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4288 imgName := "bldargtest" 4289 envKey := "foo" 4290 envVal := "bar" 4291 envValOverride := "barOverride" 4292 dockerfile := fmt.Sprintf(`FROM busybox 4293 ENV %s %s 4294 ARG %s 4295 RUN echo $%s 4296 CMD echo $%s 4297 `, envKey, envValOverride, envKey, envKey, envKey) 4298 result := buildImage(imgName, 4299 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4300 build.WithDockerfile(dockerfile), 4301 ) 4302 result.Assert(c, icmd.Success) 4303 if strings.Count(result.Combined(), envValOverride) != 2 { 4304 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4305 } 4306 4307 containerName := "bldargCont" 4308 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4309 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4310 } 4311} 4312 4313func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) { 4314 imgName := "bldvarstest" 4315 4316 wdVar := "WDIR" 4317 wdVal := "/tmp/" 4318 addVar := "AFILE" 4319 addVal := "addFile" 4320 copyVar := "CFILE" 4321 copyVal := "copyFile" 4322 envVar := "foo" 4323 envVal := "bar" 4324 exposeVar := "EPORT" 4325 exposeVal := "9999" 4326 userVar := "USER" 4327 userVal := "testUser" 4328 volVar := "VOL" 4329 volVal := "/testVol/" 4330 if DaemonIsWindows() { 4331 volVal = "C:\\testVol" 4332 wdVal = "C:\\tmp" 4333 } 4334 4335 buildImageSuccessfully(c, imgName, 4336 cli.WithFlags( 4337 "--build-arg", fmt.Sprintf("%s=%s", wdVar, wdVal), 4338 "--build-arg", fmt.Sprintf("%s=%s", addVar, addVal), 4339 "--build-arg", fmt.Sprintf("%s=%s", copyVar, copyVal), 4340 "--build-arg", fmt.Sprintf("%s=%s", envVar, envVal), 4341 "--build-arg", fmt.Sprintf("%s=%s", exposeVar, exposeVal), 4342 "--build-arg", fmt.Sprintf("%s=%s", userVar, userVal), 4343 "--build-arg", fmt.Sprintf("%s=%s", volVar, volVal), 4344 ), 4345 build.WithBuildContext(c, 4346 build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox 4347 ARG %s 4348 WORKDIR ${%s} 4349 ARG %s 4350 ADD ${%s} testDir/ 4351 ARG %s 4352 COPY $%s testDir/ 4353 ARG %s 4354 ENV %s=${%s} 4355 ARG %s 4356 EXPOSE $%s 4357 ARG %s 4358 USER $%s 4359 ARG %s 4360 VOLUME ${%s}`, 4361 wdVar, wdVar, addVar, addVar, copyVar, copyVar, envVar, envVar, 4362 envVar, exposeVar, exposeVar, userVar, userVar, volVar, volVar)), 4363 build.WithFile(addVal, "some stuff"), 4364 build.WithFile(copyVal, "some stuff"), 4365 ), 4366 ) 4367 4368 res := inspectField(c, imgName, "Config.WorkingDir") 4369 c.Check(filepath.ToSlash(res), check.Equals, filepath.ToSlash(wdVal)) 4370 4371 var resArr []string 4372 inspectFieldAndUnmarshall(c, imgName, "Config.Env", &resArr) 4373 4374 found := false 4375 for _, v := range resArr { 4376 if fmt.Sprintf("%s=%s", envVar, envVal) == v { 4377 found = true 4378 break 4379 } 4380 } 4381 if !found { 4382 c.Fatalf("Config.Env value mismatch. Expected <key=value> to exist: %s=%s, got: %v", 4383 envVar, envVal, resArr) 4384 } 4385 4386 var resMap map[string]interface{} 4387 inspectFieldAndUnmarshall(c, imgName, "Config.ExposedPorts", &resMap) 4388 if _, ok := resMap[fmt.Sprintf("%s/tcp", exposeVal)]; !ok { 4389 c.Fatalf("Config.ExposedPorts value mismatch. Expected exposed port: %s/tcp, got: %v", exposeVal, resMap) 4390 } 4391 4392 res = inspectField(c, imgName, "Config.User") 4393 if res != userVal { 4394 c.Fatalf("Config.User value mismatch. Expected: %s, got: %s", userVal, res) 4395 } 4396 4397 inspectFieldAndUnmarshall(c, imgName, "Config.Volumes", &resMap) 4398 if _, ok := resMap[volVal]; !ok { 4399 c.Fatalf("Config.Volumes value mismatch. Expected volume: %s, got: %v", volVal, resMap) 4400 } 4401} 4402 4403func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *check.C) { 4404 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4405 imgName := "bldvarstest" 4406 envKey := "foo" 4407 envVal := "bar" 4408 envKey1 := "foo1" 4409 envValOverride := "barOverride" 4410 dockerfile := fmt.Sprintf(`FROM busybox 4411 ARG %s 4412 ENV %s %s 4413 ENV %s ${%s} 4414 RUN echo $%s 4415 CMD echo $%s`, envKey, envKey, envValOverride, envKey1, envKey, envKey1, envKey1) 4416 result := buildImage(imgName, 4417 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4418 build.WithDockerfile(dockerfile), 4419 ) 4420 result.Assert(c, icmd.Success) 4421 if strings.Count(result.Combined(), envValOverride) != 2 { 4422 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4423 } 4424 4425 containerName := "bldargCont" 4426 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4427 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4428 } 4429} 4430 4431func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *check.C) { 4432 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4433 imgName := "bldargtest" 4434 envKey := "foo" 4435 envVal := "bar" 4436 dockerfile := fmt.Sprintf(`FROM busybox 4437 RUN echo $%s 4438 ARG %s 4439 CMD echo $%s`, envKey, envKey, envKey) 4440 result := buildImage(imgName, 4441 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4442 build.WithDockerfile(dockerfile), 4443 ) 4444 result.Assert(c, icmd.Success) 4445 if strings.Contains(result.Combined(), envVal) { 4446 c.Fatalf("able to access environment variable in output: %q expected to be missing", result.Combined()) 4447 } 4448 4449 containerName := "bldargCont" 4450 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4451 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4452 } 4453} 4454 4455func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *check.C) { 4456 testRequires(c, DaemonIsLinux) // Windows does not support --build-arg 4457 imgName := "bldargtest" 4458 envKey := "HTTP_PROXY" 4459 envVal := "bar" 4460 dockerfile := fmt.Sprintf(`FROM busybox 4461 RUN echo $%s 4462 CMD echo $%s`, envKey, envKey) 4463 4464 result := buildImage(imgName, 4465 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4466 build.WithDockerfile(dockerfile), 4467 ) 4468 result.Assert(c, icmd.Success) 4469 if !strings.Contains(result.Combined(), envVal) { 4470 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envVal) 4471 } 4472 containerName := "bldargCont" 4473 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" { 4474 c.Fatalf("run produced invalid output: %q, expected empty string", out) 4475 } 4476} 4477 4478func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *check.C) { 4479 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4480 imgName := "bldargtest" 4481 envKey := "foo" 4482 envVal := "bar" 4483 envValOverride := "barOverride" 4484 dockerfile := fmt.Sprintf(`FROM busybox 4485 ARG %s=%s 4486 ENV %s $%s 4487 RUN echo $%s 4488 CMD echo $%s`, envKey, envVal, envKey, envKey, envKey, envKey) 4489 result := buildImage(imgName, 4490 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envValOverride)), 4491 build.WithDockerfile(dockerfile), 4492 ) 4493 result.Assert(c, icmd.Success) 4494 if strings.Count(result.Combined(), envValOverride) != 1 { 4495 c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride) 4496 } 4497 4498 containerName := "bldargCont" 4499 if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) { 4500 c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride) 4501 } 4502} 4503 4504func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *check.C) { 4505 imgName := "bldargtest" 4506 envKey := "foo" 4507 envVal := "bar" 4508 dockerfile := fmt.Sprintf(`FROM busybox 4509 RUN echo $%s 4510 CMD echo $%s`, envKey, envKey) 4511 warnStr := "[Warning] One or more build-args" 4512 buildImage(imgName, 4513 cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)), 4514 build.WithDockerfile(dockerfile), 4515 ).Assert(c, icmd.Expected{ 4516 Out: warnStr, 4517 }) 4518} 4519 4520func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) { 4521 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4522 dockerfile := `FROM busybox 4523 ARG FOO1=fromfile 4524 ARG FOO2=fromfile 4525 ARG FOO3=fromfile 4526 ARG FOO4=fromfile 4527 ARG FOO5 4528 ARG FOO6 4529 ARG FO10 4530 RUN env 4531 RUN [ "$FOO1" == "fromcmd" ] 4532 RUN [ "$FOO2" == "" ] 4533 RUN [ "$FOO3" == "fromenv" ] 4534 RUN [ "$FOO4" == "fromfile" ] 4535 RUN [ "$FOO5" == "fromcmd" ] 4536 # The following should not exist at all in the env 4537 RUN [ "$(env | grep FOO6)" == "" ] 4538 RUN [ "$(env | grep FOO7)" == "" ] 4539 RUN [ "$(env | grep FOO8)" == "" ] 4540 RUN [ "$(env | grep FOO9)" == "" ] 4541 RUN [ "$FO10" == "" ] 4542 ` 4543 result := buildImage("testbuildtimeargenv", 4544 cli.WithFlags( 4545 "--build-arg", fmt.Sprintf("FOO1=fromcmd"), 4546 "--build-arg", fmt.Sprintf("FOO2="), 4547 "--build-arg", fmt.Sprintf("FOO3"), // set in env 4548 "--build-arg", fmt.Sprintf("FOO4"), // not set in env 4549 "--build-arg", fmt.Sprintf("FOO5=fromcmd"), 4550 // FOO6 is not set at all 4551 "--build-arg", fmt.Sprintf("FOO7=fromcmd"), // should produce a warning 4552 "--build-arg", fmt.Sprintf("FOO8="), // should produce a warning 4553 "--build-arg", fmt.Sprintf("FOO9"), // should produce a warning 4554 "--build-arg", fmt.Sprintf("FO10"), // not set in env, empty value 4555 ), 4556 cli.WithEnvironmentVariables(append(os.Environ(), 4557 "FOO1=fromenv", 4558 "FOO2=fromenv", 4559 "FOO3=fromenv")...), 4560 build.WithBuildContext(c, 4561 build.WithFile("Dockerfile", dockerfile), 4562 ), 4563 ) 4564 result.Assert(c, icmd.Success) 4565 4566 // Now check to make sure we got a warning msg about unused build-args 4567 i := strings.Index(result.Combined(), "[Warning]") 4568 if i < 0 { 4569 c.Fatalf("Missing the build-arg warning in %q", result.Combined()) 4570 } 4571 4572 out := result.Combined()[i:] // "out" should contain just the warning message now 4573 4574 // These were specified on a --build-arg but no ARG was in the Dockerfile 4575 c.Assert(out, checker.Contains, "FOO7") 4576 c.Assert(out, checker.Contains, "FOO8") 4577 c.Assert(out, checker.Contains, "FOO9") 4578} 4579 4580func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *check.C) { 4581 imgName := "bldargtest" 4582 envKey := "foo" 4583 envKey1 := "foo1" 4584 envKey2 := "foo2" 4585 envKey3 := "foo3" 4586 dockerfile := fmt.Sprintf(`FROM busybox 4587 ARG %s="" 4588 ARG %s='' 4589 ARG %s="''" 4590 ARG %s='""' 4591 RUN [ "$%s" != "$%s" ] 4592 RUN [ "$%s" != "$%s" ] 4593 RUN [ "$%s" != "$%s" ] 4594 RUN [ "$%s" != "$%s" ] 4595 RUN [ "$%s" != "$%s" ]`, envKey, envKey1, envKey2, envKey3, 4596 envKey, envKey2, envKey, envKey3, envKey1, envKey2, envKey1, envKey3, 4597 envKey2, envKey3) 4598 buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile)) 4599} 4600 4601func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) { 4602 testRequires(c, DaemonIsLinux) // Windows does not support ARG 4603 imgName := "bldargtest" 4604 envKey := "foo" 4605 envKey1 := "foo1" 4606 envKey2 := "foo2" 4607 dockerfile := fmt.Sprintf(`FROM busybox 4608 ARG %s= 4609 ARG %s="" 4610 ARG %s='' 4611 RUN [ "$%s" == "$%s" ] 4612 RUN [ "$%s" == "$%s" ] 4613 RUN [ "$%s" == "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2) 4614 buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile)) 4615} 4616 4617func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *check.C) { 4618 imgName := "bldargtest" 4619 envKey := "foo" 4620 dockerfile := fmt.Sprintf(`FROM busybox 4621 ARG %s 4622 RUN env`, envKey) 4623 4624 result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) 4625 result.Assert(c, icmd.Success) 4626 if strings.Count(result.Combined(), envKey) != 1 { 4627 c.Fatalf("unexpected number of occurrences of the arg in output: %q expected: 1", result.Combined()) 4628 } 4629} 4630 4631func (s *DockerSuite) TestBuildMultiStageArg(c *check.C) { 4632 imgName := "multifrombldargtest" 4633 dockerfile := `FROM busybox 4634 ARG foo=abc 4635 LABEL multifromtest=1 4636 RUN env > /out 4637 FROM busybox 4638 ARG bar=def 4639 RUN env > /out` 4640 4641 result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile)) 4642 result.Assert(c, icmd.Success) 4643 4644 result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") 4645 parentID := strings.TrimSpace(result.Stdout()) 4646 4647 result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") 4648 c.Assert(result.Stdout(), checker.Contains, "foo=abc") 4649 4650 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4651 c.Assert(result.Stdout(), checker.Not(checker.Contains), "foo") 4652 c.Assert(result.Stdout(), checker.Contains, "bar=def") 4653} 4654 4655func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *check.C) { 4656 imgName := "multifrombldargtest" 4657 dockerfile := `ARG tag=nosuchtag 4658 FROM busybox:${tag} 4659 LABEL multifromtest=1 4660 RUN env > /out 4661 FROM busybox:${tag} 4662 ARG tag 4663 RUN env > /out` 4664 4665 result := cli.BuildCmd(c, imgName, 4666 build.WithDockerfile(dockerfile), 4667 cli.WithFlags("--build-arg", fmt.Sprintf("tag=latest"))) 4668 result.Assert(c, icmd.Success) 4669 4670 result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") 4671 parentID := strings.TrimSpace(result.Stdout()) 4672 4673 result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out") 4674 c.Assert(result.Stdout(), checker.Not(checker.Contains), "tag") 4675 4676 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4677 c.Assert(result.Stdout(), checker.Contains, "tag=latest") 4678} 4679 4680func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *check.C) { 4681 imgName := "multifromunusedarg" 4682 dockerfile := `FROM busybox 4683 ARG foo 4684 FROM busybox 4685 ARG bar 4686 RUN env > /out` 4687 4688 result := cli.BuildCmd(c, imgName, 4689 build.WithDockerfile(dockerfile), 4690 cli.WithFlags("--build-arg", fmt.Sprintf("baz=abc"))) 4691 result.Assert(c, icmd.Success) 4692 c.Assert(result.Combined(), checker.Contains, "[Warning]") 4693 c.Assert(result.Combined(), checker.Contains, "[baz] were not consumed") 4694 4695 result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out") 4696 c.Assert(result.Stdout(), checker.Not(checker.Contains), "bar") 4697 c.Assert(result.Stdout(), checker.Not(checker.Contains), "baz") 4698} 4699 4700func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) { 4701 volName := "testname:/foo" 4702 4703 if testEnv.OSType == "windows" { 4704 volName = "testname:C:\\foo" 4705 } 4706 dockerCmd(c, "run", "-v", volName, "busybox", "sh", "-c", "touch /foo/oops") 4707 4708 dockerFile := `FROM busybox 4709 VOLUME ` + volName + ` 4710 RUN ls /foo/oops 4711 ` 4712 buildImage("test", build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ 4713 ExitCode: 1, 4714 }) 4715} 4716 4717func (s *DockerSuite) TestBuildTagEvent(c *check.C) { 4718 since := daemonUnixTime(c) 4719 4720 dockerFile := `FROM busybox 4721 RUN echo events 4722 ` 4723 buildImageSuccessfully(c, "test", build.WithDockerfile(dockerFile)) 4724 4725 until := daemonUnixTime(c) 4726 out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "type=image") 4727 events := strings.Split(strings.TrimSpace(out), "\n") 4728 actions := eventActionsByIDAndType(c, events, "test:latest", "image") 4729 var foundTag bool 4730 for _, a := range actions { 4731 if a == "tag" { 4732 foundTag = true 4733 break 4734 } 4735 } 4736 4737 c.Assert(foundTag, checker.True, check.Commentf("No tag event found:\n%s", out)) 4738} 4739 4740// #15780 4741func (s *DockerSuite) TestBuildMultipleTags(c *check.C) { 4742 dockerfile := ` 4743 FROM busybox 4744 MAINTAINER test-15780 4745 ` 4746 buildImageSuccessfully(c, "tag1", cli.WithFlags("-t", "tag2:v2", "-t", "tag1:latest", "-t", "tag1"), build.WithDockerfile(dockerfile)) 4747 4748 id1 := getIDByName(c, "tag1") 4749 id2 := getIDByName(c, "tag2:v2") 4750 c.Assert(id1, check.Equals, id2) 4751} 4752 4753// #17290 4754func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) { 4755 name := "testbuildbrokensymlink" 4756 ctx := fakecontext.New(c, "", 4757 fakecontext.WithDockerfile(` 4758 FROM busybox 4759 COPY . ./`), 4760 fakecontext.WithFiles(map[string]string{ 4761 "foo": "bar", 4762 })) 4763 defer ctx.Close() 4764 4765 err := os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink")) 4766 c.Assert(err, checker.IsNil) 4767 4768 // warm up cache 4769 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4770 4771 // add new file to context, should invalidate cache 4772 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644) 4773 c.Assert(err, checker.IsNil) 4774 4775 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4776 if strings.Contains(result.Combined(), "Using cache") { 4777 c.Fatal("2nd build used cache on ADD, it shouldn't") 4778 } 4779} 4780 4781func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) { 4782 name := "testbuildbrokensymlink" 4783 ctx := fakecontext.New(c, "", 4784 fakecontext.WithDockerfile(` 4785 FROM busybox 4786 COPY asymlink target`), 4787 fakecontext.WithFiles(map[string]string{ 4788 "foo": "bar", 4789 })) 4790 defer ctx.Close() 4791 4792 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4793 c.Assert(err, checker.IsNil) 4794 4795 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4796 4797 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined() 4798 c.Assert(out, checker.Matches, "bar") 4799 4800 // change target file should invalidate cache 4801 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4802 c.Assert(err, checker.IsNil) 4803 4804 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4805 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4806 4807 out = cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined() 4808 c.Assert(out, checker.Matches, "baz") 4809} 4810 4811func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) { 4812 name := "testbuildbrokensymlink" 4813 ctx := fakecontext.New(c, "", 4814 fakecontext.WithDockerfile(` 4815 FROM busybox 4816 COPY asymlink /`), 4817 fakecontext.WithFiles(map[string]string{ 4818 "foo/abc": "bar", 4819 "foo/def": "baz", 4820 })) 4821 defer ctx.Close() 4822 4823 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4824 c.Assert(err, checker.IsNil) 4825 4826 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4827 4828 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined() 4829 c.Assert(out, checker.Matches, "barbaz") 4830 4831 // change target file should invalidate cache 4832 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644) 4833 c.Assert(err, checker.IsNil) 4834 4835 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4836 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4837 4838 out = cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined() 4839 c.Assert(out, checker.Matches, "barbax") 4840 4841} 4842 4843// TestBuildSymlinkBasename tests that target file gets basename from symlink, 4844// not from the target file. 4845func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) { 4846 name := "testbuildbrokensymlink" 4847 ctx := fakecontext.New(c, "", 4848 fakecontext.WithDockerfile(` 4849 FROM busybox 4850 COPY asymlink /`), 4851 fakecontext.WithFiles(map[string]string{ 4852 "foo": "bar", 4853 })) 4854 defer ctx.Close() 4855 4856 err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink")) 4857 c.Assert(err, checker.IsNil) 4858 4859 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4860 4861 out := cli.DockerCmd(c, "run", "--rm", name, "cat", "asymlink").Combined() 4862 c.Assert(out, checker.Matches, "bar") 4863} 4864 4865// #17827 4866func (s *DockerSuite) TestBuildCacheRootSource(c *check.C) { 4867 name := "testbuildrootsource" 4868 ctx := fakecontext.New(c, "", 4869 fakecontext.WithDockerfile(` 4870 FROM busybox 4871 COPY / /data`), 4872 fakecontext.WithFiles(map[string]string{ 4873 "foo": "bar", 4874 })) 4875 defer ctx.Close() 4876 4877 // warm up cache 4878 cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4879 4880 // change file, should invalidate cache 4881 err := ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644) 4882 c.Assert(err, checker.IsNil) 4883 4884 result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx)) 4885 4886 c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache") 4887} 4888 4889// #19375 4890// FIXME(vdemeester) should migrate to docker/cli tests 4891func (s *DockerSuite) TestBuildFailsGitNotCallable(c *check.C) { 4892 buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="), 4893 build.WithContextPath("github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4894 ExitCode: 1, 4895 Err: "unable to prepare context: unable to find 'git': ", 4896 }) 4897 4898 buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="), 4899 build.WithContextPath("https://github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{ 4900 ExitCode: 1, 4901 Err: "unable to prepare context: unable to find 'git': ", 4902 }) 4903} 4904 4905// TestBuildWorkdirWindowsPath tests that a Windows style path works as a workdir 4906func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) { 4907 testRequires(c, DaemonIsWindows) 4908 name := "testbuildworkdirwindowspath" 4909 buildImageSuccessfully(c, name, build.WithDockerfile(` 4910 FROM `+testEnv.PlatformDefaults.BaseImage+` 4911 RUN mkdir C:\\work 4912 WORKDIR C:\\work 4913 RUN if "%CD%" NEQ "C:\work" exit -1 4914 `)) 4915} 4916 4917func (s *DockerSuite) TestBuildLabel(c *check.C) { 4918 name := "testbuildlabel" 4919 testLabel := "foo" 4920 4921 buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel), 4922 build.WithDockerfile(` 4923 FROM `+minimalBaseImage()+` 4924 LABEL default foo 4925`)) 4926 4927 var labels map[string]string 4928 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4929 if _, ok := labels[testLabel]; !ok { 4930 c.Fatal("label not found in image") 4931 } 4932} 4933 4934func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) { 4935 name := "testbuildlabel" 4936 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=bar"), 4937 build.WithDockerfile("FROM busybox")) 4938 4939 var labels map[string]string 4940 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4941 v, ok := labels["foo"] 4942 if !ok { 4943 c.Fatal("label `foo` not found in image") 4944 } 4945 c.Assert(v, checker.Equals, "bar") 4946} 4947 4948func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) { 4949 name := "testbuildlabelcachecommit" 4950 testLabel := "foo" 4951 4952 buildImageSuccessfully(c, name, build.WithDockerfile(` 4953 FROM `+minimalBaseImage()+` 4954 LABEL default foo 4955 `)) 4956 buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel), 4957 build.WithDockerfile(` 4958 FROM `+minimalBaseImage()+` 4959 LABEL default foo 4960 `)) 4961 4962 var labels map[string]string 4963 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4964 if _, ok := labels[testLabel]; !ok { 4965 c.Fatal("label not found in image") 4966 } 4967} 4968 4969func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) { 4970 name := "testbuildlabelmultiple" 4971 testLabels := map[string]string{ 4972 "foo": "bar", 4973 "123": "456", 4974 } 4975 var labelArgs []string 4976 for k, v := range testLabels { 4977 labelArgs = append(labelArgs, "--label", k+"="+v) 4978 } 4979 4980 buildImageSuccessfully(c, name, cli.WithFlags(labelArgs...), 4981 build.WithDockerfile(` 4982 FROM `+minimalBaseImage()+` 4983 LABEL default foo 4984`)) 4985 4986 var labels map[string]string 4987 inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels) 4988 for k, v := range testLabels { 4989 if x, ok := labels[k]; !ok || x != v { 4990 c.Fatalf("label %s=%s not found in image", k, v) 4991 } 4992 } 4993} 4994 4995func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) { 4996 dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 4997 baseImage := privateRegistryURL + "/baseimage" 4998 4999 buildImageSuccessfully(c, baseImage, build.WithDockerfile(` 5000 FROM busybox 5001 ENV env1 val1 5002 `)) 5003 5004 dockerCmd(c, "push", baseImage) 5005 dockerCmd(c, "rmi", baseImage) 5006 5007 buildImageSuccessfully(c, baseImage, build.WithDockerfile(fmt.Sprintf(` 5008 FROM %s 5009 ENV env2 val2 5010 `, baseImage))) 5011} 5012 5013func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) { 5014 osPath := os.Getenv("PATH") 5015 defer os.Setenv("PATH", osPath) 5016 5017 workingDir, err := os.Getwd() 5018 c.Assert(err, checker.IsNil) 5019 absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth")) 5020 c.Assert(err, checker.IsNil) 5021 testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute) 5022 5023 os.Setenv("PATH", testPath) 5024 5025 repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL) 5026 5027 tmp, err := ioutil.TempDir("", "integration-cli-") 5028 c.Assert(err, checker.IsNil) 5029 5030 externalAuthConfig := `{ "credsStore": "shell-test" }` 5031 5032 configPath := filepath.Join(tmp, "config.json") 5033 err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) 5034 c.Assert(err, checker.IsNil) 5035 5036 dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) 5037 5038 b, err := ioutil.ReadFile(configPath) 5039 c.Assert(err, checker.IsNil) 5040 c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":") 5041 5042 dockerCmd(c, "--config", tmp, "tag", "busybox", repoName) 5043 dockerCmd(c, "--config", tmp, "push", repoName) 5044 5045 // make sure the image is pulled when building 5046 dockerCmd(c, "rmi", repoName) 5047 5048 icmd.RunCmd(icmd.Cmd{ 5049 Command: []string{dockerBinary, "--config", tmp, "build", "-"}, 5050 Stdin: strings.NewReader(fmt.Sprintf("FROM %s", repoName)), 5051 }).Assert(c, icmd.Success) 5052} 5053 5054// Test cases in #22036 5055func (s *DockerSuite) TestBuildLabelsOverride(c *check.C) { 5056 // Command line option labels will always override 5057 name := "scratchy" 5058 expected := `{"bar":"from-flag","foo":"from-flag"}` 5059 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5060 build.WithDockerfile(`FROM `+minimalBaseImage()+` 5061 LABEL foo=from-dockerfile`)) 5062 res := inspectFieldJSON(c, name, "Config.Labels") 5063 if res != expected { 5064 c.Fatalf("Labels %s, expected %s", res, expected) 5065 } 5066 5067 name = "from" 5068 expected = `{"foo":"from-dockerfile"}` 5069 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5070 LABEL foo from-dockerfile`)) 5071 res = inspectFieldJSON(c, name, "Config.Labels") 5072 if res != expected { 5073 c.Fatalf("Labels %s, expected %s", res, expected) 5074 } 5075 5076 // Command line option label will override even via `FROM` 5077 name = "new" 5078 expected = `{"bar":"from-dockerfile2","foo":"new"}` 5079 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=new"), 5080 build.WithDockerfile(`FROM from 5081 LABEL bar from-dockerfile2`)) 5082 res = inspectFieldJSON(c, name, "Config.Labels") 5083 if res != expected { 5084 c.Fatalf("Labels %s, expected %s", res, expected) 5085 } 5086 5087 // Command line option without a value set (--label foo, --label bar=) 5088 // will be treated as --label foo="", --label bar="" 5089 name = "scratchy2" 5090 expected = `{"bar":"","foo":""}` 5091 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo", "--label", "bar="), 5092 build.WithDockerfile(`FROM `+minimalBaseImage()+` 5093 LABEL foo=from-dockerfile`)) 5094 res = inspectFieldJSON(c, name, "Config.Labels") 5095 if res != expected { 5096 c.Fatalf("Labels %s, expected %s", res, expected) 5097 } 5098 5099 // Command line option without a value set (--label foo, --label bar=) 5100 // will be treated as --label foo="", --label bar="" 5101 // This time is for inherited images 5102 name = "new2" 5103 expected = `{"bar":"","foo":""}` 5104 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=", "--label", "bar"), 5105 build.WithDockerfile(`FROM from 5106 LABEL bar from-dockerfile2`)) 5107 res = inspectFieldJSON(c, name, "Config.Labels") 5108 if res != expected { 5109 c.Fatalf("Labels %s, expected %s", res, expected) 5110 } 5111 5112 // Command line option labels with only `FROM` 5113 name = "scratchy" 5114 expected = `{"bar":"from-flag","foo":"from-flag"}` 5115 buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"), 5116 build.WithDockerfile(`FROM `+minimalBaseImage())) 5117 res = inspectFieldJSON(c, name, "Config.Labels") 5118 if res != expected { 5119 c.Fatalf("Labels %s, expected %s", res, expected) 5120 } 5121 5122 // Command line option labels with env var 5123 name = "scratchz" 5124 expected = `{"bar":"$PATH"}` 5125 buildImageSuccessfully(c, name, cli.WithFlags("--label", "bar=$PATH"), 5126 build.WithDockerfile(`FROM `+minimalBaseImage())) 5127 res = inspectFieldJSON(c, name, "Config.Labels") 5128 if res != expected { 5129 c.Fatalf("Labels %s, expected %s", res, expected) 5130 } 5131} 5132 5133// Test case for #22855 5134func (s *DockerSuite) TestBuildDeleteCommittedFile(c *check.C) { 5135 name := "test-delete-committed-file" 5136 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 5137 RUN echo test > file 5138 RUN test -e file 5139 RUN rm file 5140 RUN sh -c "! test -e file"`)) 5141} 5142 5143// #20083 5144func (s *DockerSuite) TestBuildDockerignoreComment(c *check.C) { 5145 // TODO Windows: Figure out why this test is flakey on TP5. If you add 5146 // something like RUN sleep 5, or even RUN ls /tmp after the ADD line, 5147 // it is more reliable, but that's not a good fix. 5148 testRequires(c, DaemonIsLinux) 5149 5150 name := "testbuilddockerignorecleanpaths" 5151 dockerfile := ` 5152 FROM busybox 5153 ADD . /tmp/ 5154 RUN sh -c "(ls -la /tmp/#1)" 5155 RUN sh -c "(! ls -la /tmp/#2)" 5156 RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (ls /tmp/dir1/foo)"` 5157 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5158 build.WithFile("Dockerfile", dockerfile), 5159 build.WithFile("foo", "foo"), 5160 build.WithFile("foo2", "foo2"), 5161 build.WithFile("dir1/foo", "foo in dir1"), 5162 build.WithFile("#1", "# file 1"), 5163 build.WithFile("#2", "# file 2"), 5164 build.WithFile(".dockerignore", `# Visual C++ cache files 5165# because we have git ;-) 5166# The above comment is from #20083 5167foo 5168#dir1/foo 5169foo2 5170# The following is considered as comment as # is at the beginning 5171#1 5172# The following is not considered as comment as # is not at the beginning 5173 #2 5174`))) 5175} 5176 5177// Test case for #23221 5178func (s *DockerSuite) TestBuildWithUTF8BOM(c *check.C) { 5179 name := "test-with-utf8-bom" 5180 dockerfile := []byte(`FROM busybox`) 5181 bomDockerfile := append([]byte{0xEF, 0xBB, 0xBF}, dockerfile...) 5182 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5183 build.WithFile("Dockerfile", string(bomDockerfile)), 5184 )) 5185} 5186 5187// Test case for UTF-8 BOM in .dockerignore, related to #23221 5188func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *check.C) { 5189 name := "test-with-utf8-bom-dockerignore" 5190 dockerfile := ` 5191 FROM busybox 5192 ADD . /tmp/ 5193 RUN ls -la /tmp 5194 RUN sh -c "! ls /tmp/Dockerfile" 5195 RUN ls /tmp/.dockerignore` 5196 dockerignore := []byte("./Dockerfile\n") 5197 bomDockerignore := append([]byte{0xEF, 0xBB, 0xBF}, dockerignore...) 5198 buildImageSuccessfully(c, name, build.WithBuildContext(c, 5199 build.WithFile("Dockerfile", dockerfile), 5200 build.WithFile(".dockerignore", string(bomDockerignore)), 5201 )) 5202} 5203 5204// #22489 Shell test to confirm config gets updated correctly 5205func (s *DockerSuite) TestBuildShellUpdatesConfig(c *check.C) { 5206 name := "testbuildshellupdatesconfig" 5207 5208 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5209 SHELL ["foo", "-bar"]`)) 5210 expected := `["foo","-bar","#(nop) ","SHELL [foo -bar]"]` 5211 res := inspectFieldJSON(c, name, "ContainerConfig.Cmd") 5212 if res != expected { 5213 c.Fatalf("%s, expected %s", res, expected) 5214 } 5215 res = inspectFieldJSON(c, name, "ContainerConfig.Shell") 5216 if res != `["foo","-bar"]` { 5217 c.Fatalf(`%s, expected ["foo","-bar"]`, res) 5218 } 5219} 5220 5221// #22489 Changing the shell multiple times and CMD after. 5222func (s *DockerSuite) TestBuildShellMultiple(c *check.C) { 5223 name := "testbuildshellmultiple" 5224 5225 result := buildImage(name, build.WithDockerfile(`FROM busybox 5226 RUN echo defaultshell 5227 SHELL ["echo"] 5228 RUN echoshell 5229 SHELL ["ls"] 5230 RUN -l 5231 CMD -l`)) 5232 result.Assert(c, icmd.Success) 5233 5234 // Must contain 'defaultshell' twice 5235 if len(strings.Split(result.Combined(), "defaultshell")) != 3 { 5236 c.Fatalf("defaultshell should have appeared twice in %s", result.Combined()) 5237 } 5238 5239 // Must contain 'echoshell' twice 5240 if len(strings.Split(result.Combined(), "echoshell")) != 3 { 5241 c.Fatalf("echoshell should have appeared twice in %s", result.Combined()) 5242 } 5243 5244 // Must contain "total " (part of ls -l) 5245 if !strings.Contains(result.Combined(), "total ") { 5246 c.Fatalf("%s should have contained 'total '", result.Combined()) 5247 } 5248 5249 // A container started from the image uses the shell-form CMD. 5250 // Last shell is ls. CMD is -l. So should contain 'total '. 5251 outrun, _ := dockerCmd(c, "run", "--rm", name) 5252 if !strings.Contains(outrun, "total ") { 5253 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5254 } 5255} 5256 5257// #22489. Changed SHELL with ENTRYPOINT 5258func (s *DockerSuite) TestBuildShellEntrypoint(c *check.C) { 5259 name := "testbuildshellentrypoint" 5260 5261 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 5262 SHELL ["ls"] 5263 ENTRYPOINT -l`)) 5264 // A container started from the image uses the shell-form ENTRYPOINT. 5265 // Shell is ls. ENTRYPOINT is -l. So should contain 'total '. 5266 outrun, _ := dockerCmd(c, "run", "--rm", name) 5267 if !strings.Contains(outrun, "total ") { 5268 c.Fatalf("Expected started container to run ls -l. %s", outrun) 5269 } 5270} 5271 5272// #22489 Shell test to confirm shell is inherited in a subsequent build 5273func (s *DockerSuite) TestBuildShellInherited(c *check.C) { 5274 name1 := "testbuildshellinherited1" 5275 buildImageSuccessfully(c, name1, build.WithDockerfile(`FROM busybox 5276 SHELL ["ls"]`)) 5277 name2 := "testbuildshellinherited2" 5278 buildImage(name2, build.WithDockerfile(`FROM `+name1+` 5279 RUN -l`)).Assert(c, icmd.Expected{ 5280 // ls -l has "total " followed by some number in it, ls without -l does not. 5281 Out: "total ", 5282 }) 5283} 5284 5285// #22489 Shell test to confirm non-JSON doesn't work 5286func (s *DockerSuite) TestBuildShellNotJSON(c *check.C) { 5287 name := "testbuildshellnotjson" 5288 5289 buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5290 sHeLl exec -form`, // Casing explicit to ensure error is upper-cased. 5291 )).Assert(c, icmd.Expected{ 5292 ExitCode: 1, 5293 Err: "SHELL requires the arguments to be in JSON form", 5294 }) 5295} 5296 5297// #22489 Windows shell test to confirm native is powershell if executing a PS command 5298// This would error if the default shell were still cmd. 5299func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) { 5300 testRequires(c, DaemonIsWindows) 5301 name := "testbuildshellpowershell" 5302 buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+` 5303 SHELL ["powershell", "-command"] 5304 RUN Write-Host John`)).Assert(c, icmd.Expected{ 5305 Out: "\nJohn\n", 5306 }) 5307} 5308 5309// Verify that escape is being correctly applied to words when escape directive is not \. 5310// Tests WORKDIR, ADD 5311func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) { 5312 testRequires(c, DaemonIsWindows) 5313 name := "testbuildescapenotbackslashwordtesta" 5314 buildImage(name, build.WithDockerfile(`# escape= `+"`"+` 5315 FROM `+minimalBaseImage()+` 5316 WORKDIR c:\windows 5317 RUN dir /w`)).Assert(c, icmd.Expected{ 5318 Out: "[System32]", 5319 }) 5320 5321 name = "testbuildescapenotbackslashwordtestb" 5322 buildImage(name, build.WithDockerfile(`# escape= `+"`"+` 5323 FROM `+minimalBaseImage()+` 5324 SHELL ["powershell.exe"] 5325 WORKDIR c:\foo 5326 ADD Dockerfile c:\foo\ 5327 RUN dir Dockerfile`)).Assert(c, icmd.Expected{ 5328 Out: "-a----", 5329 }) 5330} 5331 5332// #22868. Make sure shell-form CMD is marked as escaped in the config of the image 5333func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) { 5334 testRequires(c, DaemonIsWindows) 5335 name := "testbuildcmdshellescaped" 5336 buildImageSuccessfully(c, name, build.WithDockerfile(` 5337 FROM `+minimalBaseImage()+` 5338 CMD "ipconfig" 5339 `)) 5340 res := inspectFieldJSON(c, name, "Config.ArgsEscaped") 5341 if res != "true" { 5342 c.Fatalf("CMD did not update Config.ArgsEscaped on image: %v", res) 5343 } 5344 dockerCmd(c, "run", "--name", "inspectme", name) 5345 dockerCmd(c, "wait", "inspectme") 5346 res = inspectFieldJSON(c, name, "Config.Cmd") 5347 5348 if res != `["cmd","/S","/C","\"ipconfig\""]` { 5349 c.Fatalf("CMD was not escaped Config.Cmd: got %v", res) 5350 } 5351} 5352 5353// Test case for #24912. 5354func (s *DockerSuite) TestBuildStepsWithProgress(c *check.C) { 5355 name := "testbuildstepswithprogress" 5356 totalRun := 5 5357 result := buildImage(name, build.WithDockerfile("FROM busybox\n"+strings.Repeat("RUN echo foo\n", totalRun))) 5358 result.Assert(c, icmd.Success) 5359 c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun)) 5360 for i := 2; i <= 1+totalRun; i++ { 5361 c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun)) 5362 } 5363} 5364 5365func (s *DockerSuite) TestBuildWithFailure(c *check.C) { 5366 name := "testbuildwithfailure" 5367 5368 // First test case can only detect `nobody` in runtime so all steps will show up 5369 dockerfile := "FROM busybox\nRUN nobody" 5370 result := buildImage(name, build.WithDockerfile(dockerfile)) 5371 c.Assert(result.Error, checker.NotNil) 5372 c.Assert(result.Stdout(), checker.Contains, "Step 1/2 : FROM busybox") 5373 c.Assert(result.Stdout(), checker.Contains, "Step 2/2 : RUN nobody") 5374 5375 // Second test case `FFOM` should have been detected before build runs so no steps 5376 dockerfile = "FFOM nobody\nRUN nobody" 5377 result = buildImage(name, build.WithDockerfile(dockerfile)) 5378 c.Assert(result.Error, checker.NotNil) 5379 c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 1/2 : FROM busybox") 5380 c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 2/2 : RUN nobody") 5381} 5382 5383func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) { 5384 dockerfile := ` 5385 FROM busybox 5386 RUN echo "test" 5387 ENTRYPOINT ["sh"]` 5388 ctx := fakecontext.New(c, "", 5389 fakecontext.WithDockerfile(dockerfile), 5390 fakecontext.WithFiles(map[string]string{ 5391 "Dockerfile": dockerfile, 5392 })) 5393 defer ctx.Close() 5394 5395 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5396 id1 := getIDByName(c, "build1") 5397 5398 // rebuild with cache-from 5399 result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5400 id2 := getIDByName(c, "build2") 5401 c.Assert(id1, checker.Equals, id2) 5402 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2) 5403} 5404 5405func (s *DockerSuite) TestBuildCacheFrom(c *check.C) { 5406 testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows 5407 dockerfile := ` 5408 FROM busybox 5409 ENV FOO=bar 5410 ADD baz / 5411 RUN touch bax` 5412 ctx := fakecontext.New(c, "", 5413 fakecontext.WithDockerfile(dockerfile), 5414 fakecontext.WithFiles(map[string]string{ 5415 "Dockerfile": dockerfile, 5416 "baz": "baz", 5417 })) 5418 defer ctx.Close() 5419 5420 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5421 id1 := getIDByName(c, "build1") 5422 5423 // rebuild with cache-from 5424 result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5425 id2 := getIDByName(c, "build2") 5426 c.Assert(id1, checker.Equals, id2) 5427 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5428 cli.DockerCmd(c, "rmi", "build2") 5429 5430 // no cache match with unknown source 5431 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=nosuchtag"), build.WithExternalBuildContext(ctx)) 5432 id2 = getIDByName(c, "build2") 5433 c.Assert(id1, checker.Not(checker.Equals), id2) 5434 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 0) 5435 cli.DockerCmd(c, "rmi", "build2") 5436 5437 // clear parent images 5438 tempDir, err := ioutil.TempDir("", "test-build-cache-from-") 5439 if err != nil { 5440 c.Fatalf("failed to create temporary directory: %s", tempDir) 5441 } 5442 defer os.RemoveAll(tempDir) 5443 tempFile := filepath.Join(tempDir, "img.tar") 5444 cli.DockerCmd(c, "save", "-o", tempFile, "build1") 5445 cli.DockerCmd(c, "rmi", "build1") 5446 cli.DockerCmd(c, "load", "-i", tempFile) 5447 parentID := cli.DockerCmd(c, "inspect", "-f", "{{.Parent}}", "build1").Combined() 5448 c.Assert(strings.TrimSpace(parentID), checker.Equals, "") 5449 5450 // cache still applies without parents 5451 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5452 id2 = getIDByName(c, "build2") 5453 c.Assert(id1, checker.Equals, id2) 5454 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5455 history1 := cli.DockerCmd(c, "history", "-q", "build2").Combined() 5456 5457 // Retry, no new intermediate images 5458 result = cli.BuildCmd(c, "build3", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5459 id3 := getIDByName(c, "build3") 5460 c.Assert(id1, checker.Equals, id3) 5461 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3) 5462 history2 := cli.DockerCmd(c, "history", "-q", "build3").Combined() 5463 5464 c.Assert(history1, checker.Equals, history2) 5465 cli.DockerCmd(c, "rmi", "build2") 5466 cli.DockerCmd(c, "rmi", "build3") 5467 cli.DockerCmd(c, "rmi", "build1") 5468 cli.DockerCmd(c, "load", "-i", tempFile) 5469 5470 // Modify file, everything up to last command and layers are reused 5471 dockerfile = ` 5472 FROM busybox 5473 ENV FOO=bar 5474 ADD baz / 5475 RUN touch newfile` 5476 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644) 5477 c.Assert(err, checker.IsNil) 5478 5479 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5480 id2 = getIDByName(c, "build2") 5481 c.Assert(id1, checker.Not(checker.Equals), id2) 5482 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2) 5483 5484 layers1Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build1").Combined() 5485 layers2Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build2").Combined() 5486 5487 var layers1 []string 5488 var layers2 []string 5489 c.Assert(json.Unmarshal([]byte(layers1Str), &layers1), checker.IsNil) 5490 c.Assert(json.Unmarshal([]byte(layers2Str), &layers2), checker.IsNil) 5491 5492 c.Assert(len(layers1), checker.Equals, len(layers2)) 5493 for i := 0; i < len(layers1)-1; i++ { 5494 c.Assert(layers1[i], checker.Equals, layers2[i]) 5495 } 5496 c.Assert(layers1[len(layers1)-1], checker.Not(checker.Equals), layers2[len(layers1)-1]) 5497} 5498 5499func (s *DockerSuite) TestBuildMultiStageCache(c *check.C) { 5500 testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows 5501 dockerfile := ` 5502 FROM busybox 5503 ADD baz / 5504 FROM busybox 5505 ADD baz /` 5506 ctx := fakecontext.New(c, "", 5507 fakecontext.WithDockerfile(dockerfile), 5508 fakecontext.WithFiles(map[string]string{ 5509 "Dockerfile": dockerfile, 5510 "baz": "baz", 5511 })) 5512 defer ctx.Close() 5513 5514 result := cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5515 // second part of dockerfile was a repeat of first so should be cached 5516 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1) 5517 5518 result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx)) 5519 // now both parts of dockerfile should be cached 5520 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2) 5521} 5522 5523func (s *DockerSuite) TestBuildNetNone(c *check.C) { 5524 testRequires(c, DaemonIsLinux) 5525 name := "testbuildnetnone" 5526 buildImage(name, cli.WithFlags("--network=none"), build.WithDockerfile(` 5527 FROM busybox 5528 RUN ping -c 1 8.8.8.8 5529 `)).Assert(c, icmd.Expected{ 5530 ExitCode: 1, 5531 Out: "unreachable", 5532 }) 5533} 5534 5535func (s *DockerSuite) TestBuildNetContainer(c *check.C) { 5536 testRequires(c, DaemonIsLinux) 5537 5538 id, _ := dockerCmd(c, "run", "--hostname", "foobar", "-d", "busybox", "nc", "-ll", "-p", "1234", "-e", "hostname") 5539 5540 name := "testbuildnetcontainer" 5541 buildImageSuccessfully(c, name, cli.WithFlags("--network=container:"+strings.TrimSpace(id)), 5542 build.WithDockerfile(` 5543 FROM busybox 5544 RUN nc localhost 1234 > /otherhost 5545 `)) 5546 5547 host, _ := dockerCmd(c, "run", "testbuildnetcontainer", "cat", "/otherhost") 5548 c.Assert(strings.TrimSpace(host), check.Equals, "foobar") 5549} 5550 5551func (s *DockerSuite) TestBuildWithExtraHost(c *check.C) { 5552 testRequires(c, DaemonIsLinux) 5553 5554 name := "testbuildwithextrahost" 5555 buildImageSuccessfully(c, name, 5556 cli.WithFlags( 5557 "--add-host", "foo:127.0.0.1", 5558 "--add-host", "bar:127.0.0.1", 5559 ), 5560 build.WithDockerfile(` 5561 FROM busybox 5562 RUN ping -c 1 foo 5563 RUN ping -c 1 bar 5564 `)) 5565} 5566 5567func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *check.C) { 5568 testRequires(c, DaemonIsLinux) 5569 dockerfile := ` 5570 FROM busybox 5571 RUN ping -c 1 foo` 5572 5573 testCases := []struct { 5574 testName string 5575 dockerfile string 5576 buildFlag string 5577 }{ 5578 {"extra_host_missing_ip", dockerfile, "--add-host=foo"}, 5579 {"extra_host_missing_ip_with_delimiter", dockerfile, "--add-host=foo:"}, 5580 {"extra_host_missing_hostname", dockerfile, "--add-host=:127.0.0.1"}, 5581 {"extra_host_invalid_ipv4", dockerfile, "--add-host=foo:101.10.2"}, 5582 {"extra_host_invalid_ipv6", dockerfile, "--add-host=foo:2001::1::3F"}, 5583 } 5584 5585 for _, tc := range testCases { 5586 result := buildImage(tc.testName, cli.WithFlags(tc.buildFlag), build.WithDockerfile(tc.dockerfile)) 5587 result.Assert(c, icmd.Expected{ 5588 ExitCode: 125, 5589 }) 5590 } 5591 5592} 5593 5594func (s *DockerSuite) TestBuildContChar(c *check.C) { 5595 name := "testbuildcontchar" 5596 5597 buildImage(name, build.WithDockerfile(`FROM busybox\`)).Assert(c, icmd.Expected{ 5598 Out: "Step 1/1 : FROM busybox", 5599 }) 5600 5601 result := buildImage(name, build.WithDockerfile(`FROM busybox 5602 RUN echo hi \`)) 5603 result.Assert(c, icmd.Success) 5604 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5605 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi\n") 5606 5607 result = buildImage(name, build.WithDockerfile(`FROM busybox 5608 RUN echo hi \\`)) 5609 result.Assert(c, icmd.Success) 5610 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5611 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\n") 5612 5613 result = buildImage(name, build.WithDockerfile(`FROM busybox 5614 RUN echo hi \\\`)) 5615 result.Assert(c, icmd.Success) 5616 c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox") 5617 c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\\\n") 5618} 5619 5620func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *check.C) { 5621 dockerfile := ` 5622 FROM busybox AS first 5623 COPY foo bar 5624 5625 FROM busybox 5626 %s 5627 COPY baz baz 5628 RUN echo mno > baz/cc 5629 5630 FROM busybox 5631 COPY bar / 5632 COPY --from=1 baz sub/ 5633 COPY --from=0 bar baz 5634 COPY --from=first bar bay` 5635 5636 ctx := fakecontext.New(c, "", 5637 fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, "")), 5638 fakecontext.WithFiles(map[string]string{ 5639 "foo": "abc", 5640 "bar": "def", 5641 "baz/aa": "ghi", 5642 "baz/bb": "jkl", 5643 })) 5644 defer ctx.Close() 5645 5646 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5647 5648 cli.DockerCmd(c, "run", "build1", "cat", "bar").Assert(c, icmd.Expected{Out: "def"}) 5649 cli.DockerCmd(c, "run", "build1", "cat", "sub/aa").Assert(c, icmd.Expected{Out: "ghi"}) 5650 cli.DockerCmd(c, "run", "build1", "cat", "sub/cc").Assert(c, icmd.Expected{Out: "mno"}) 5651 cli.DockerCmd(c, "run", "build1", "cat", "baz").Assert(c, icmd.Expected{Out: "abc"}) 5652 cli.DockerCmd(c, "run", "build1", "cat", "bay").Assert(c, icmd.Expected{Out: "abc"}) 5653 5654 result := cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5655 5656 // all commands should be cached 5657 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 7) 5658 c.Assert(getIDByName(c, "build1"), checker.Equals, getIDByName(c, "build2")) 5659 5660 err := ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(fmt.Sprintf(dockerfile, "COPY baz/aa foo")), 0644) 5661 c.Assert(err, checker.IsNil) 5662 5663 // changing file in parent block should not affect last block 5664 result = cli.BuildCmd(c, "build3", build.WithExternalBuildContext(ctx)) 5665 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5) 5666 5667 err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("pqr"), 0644) 5668 c.Assert(err, checker.IsNil) 5669 5670 // changing file in parent block should affect both first and last block 5671 result = cli.BuildCmd(c, "build4", build.WithExternalBuildContext(ctx)) 5672 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5) 5673 5674 cli.DockerCmd(c, "run", "build4", "cat", "bay").Assert(c, icmd.Expected{Out: "pqr"}) 5675 cli.DockerCmd(c, "run", "build4", "cat", "baz").Assert(c, icmd.Expected{Out: "pqr"}) 5676} 5677 5678func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *check.C) { 5679 testCases := []struct { 5680 dockerfile string 5681 expectedError string 5682 }{ 5683 { 5684 dockerfile: ` 5685 FROM busybox 5686 COPY --from=foo foo bar`, 5687 expectedError: "invalid from flag value foo", 5688 }, 5689 { 5690 dockerfile: ` 5691 FROM busybox 5692 COPY --from=0 foo bar`, 5693 expectedError: "invalid from flag value 0: refers to current build stage", 5694 }, 5695 { 5696 dockerfile: ` 5697 FROM busybox AS foo 5698 COPY --from=bar foo bar`, 5699 expectedError: "invalid from flag value bar", 5700 }, 5701 { 5702 dockerfile: ` 5703 FROM busybox AS 1 5704 COPY --from=1 foo bar`, 5705 expectedError: "invalid name for build stage", 5706 }, 5707 } 5708 5709 for _, tc := range testCases { 5710 ctx := fakecontext.New(c, "", 5711 fakecontext.WithDockerfile(tc.dockerfile), 5712 fakecontext.WithFiles(map[string]string{ 5713 "foo": "abc", 5714 })) 5715 5716 cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx)).Assert(c, icmd.Expected{ 5717 ExitCode: 1, 5718 Err: tc.expectedError, 5719 }) 5720 5721 ctx.Close() 5722 } 5723} 5724 5725func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *check.C) { 5726 dockerfile := ` 5727 FROM busybox 5728 COPY foo bar` 5729 ctx := fakecontext.New(c, "", 5730 fakecontext.WithDockerfile(dockerfile), 5731 fakecontext.WithFiles(map[string]string{ 5732 "foo": "abc", 5733 })) 5734 defer ctx.Close() 5735 5736 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5737 5738 dockerfile = ` 5739 FROM build1:latest AS foo 5740 FROM busybox 5741 COPY --from=foo bar / 5742 COPY foo /` 5743 ctx = fakecontext.New(c, "", 5744 fakecontext.WithDockerfile(dockerfile), 5745 fakecontext.WithFiles(map[string]string{ 5746 "foo": "def", 5747 })) 5748 defer ctx.Close() 5749 5750 cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5751 5752 out := cli.DockerCmd(c, "run", "build2", "cat", "bar").Combined() 5753 c.Assert(strings.TrimSpace(out), check.Equals, "abc") 5754 out = cli.DockerCmd(c, "run", "build2", "cat", "foo").Combined() 5755 c.Assert(strings.TrimSpace(out), check.Equals, "def") 5756} 5757 5758func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *check.C) { 5759 dockerfile := ` 5760 FROM busybox 5761 COPY --from=busybox /etc/passwd /mypasswd 5762 RUN cmp /etc/passwd /mypasswd` 5763 5764 if DaemonIsWindows() { 5765 dockerfile = ` 5766 FROM busybox 5767 COPY --from=busybox License.txt foo` 5768 } 5769 5770 ctx := fakecontext.New(c, "", 5771 fakecontext.WithDockerfile(dockerfile), 5772 ) 5773 defer ctx.Close() 5774 5775 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5776 5777 if DaemonIsWindows() { 5778 out := cli.DockerCmd(c, "run", "build1", "cat", "License.txt").Combined() 5779 c.Assert(len(out), checker.GreaterThan, 10) 5780 out2 := cli.DockerCmd(c, "run", "build1", "cat", "foo").Combined() 5781 c.Assert(out, check.Equals, out2) 5782 } 5783} 5784 5785func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *check.C) { 5786 repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL) 5787 5788 dockerfile := ` 5789 FROM busybox 5790 COPY foo bar` 5791 ctx := fakecontext.New(c, "", 5792 fakecontext.WithDockerfile(dockerfile), 5793 fakecontext.WithFiles(map[string]string{ 5794 "foo": "abc", 5795 })) 5796 defer ctx.Close() 5797 5798 cli.BuildCmd(c, repoName, build.WithExternalBuildContext(ctx)) 5799 5800 cli.DockerCmd(c, "push", repoName) 5801 cli.DockerCmd(c, "rmi", repoName) 5802 5803 dockerfile = ` 5804 FROM busybox 5805 COPY --from=%s bar baz` 5806 5807 ctx = fakecontext.New(c, "", fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, repoName))) 5808 defer ctx.Close() 5809 5810 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5811 5812 cli.Docker(cli.Args("run", "build1", "cat", "baz")).Assert(c, icmd.Expected{Out: "abc"}) 5813} 5814 5815func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) { 5816 dockerfile := ` 5817 FROM busybox as foo 5818 COPY foo / 5819 FROM foo as foo1 5820 RUN echo 1 >> foo 5821 FROM foo as foO2 5822 RUN echo 2 >> foo 5823 FROM foo 5824 COPY --from=foo1 foo f1 5825 COPY --from=FOo2 foo f2 5826 ` // foo2 case also tests that names are case insensitive 5827 ctx := fakecontext.New(c, "", 5828 fakecontext.WithDockerfile(dockerfile), 5829 fakecontext.WithFiles(map[string]string{ 5830 "foo": "bar", 5831 })) 5832 defer ctx.Close() 5833 5834 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5835 cli.Docker(cli.Args("run", "build1", "cat", "foo")).Assert(c, icmd.Expected{Out: "bar"}) 5836 cli.Docker(cli.Args("run", "build1", "cat", "f1")).Assert(c, icmd.Expected{Out: "bar1"}) 5837 cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"}) 5838} 5839 5840func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) { 5841 testRequires(c, DaemonIsWindows) 5842 dockerfile := ` 5843 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5844 COPY foo c:\\bar` 5845 ctx := fakecontext.New(c, "", 5846 fakecontext.WithDockerfile(dockerfile), 5847 fakecontext.WithFiles(map[string]string{ 5848 "foo": "abc", 5849 })) 5850 defer ctx.Close() 5851 5852 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5853 5854 dockerfile = ` 5855 FROM build1:latest 5856 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5857 COPY --from=0 c:\\bar / 5858 COPY foo /` 5859 ctx = fakecontext.New(c, "", 5860 fakecontext.WithDockerfile(dockerfile), 5861 fakecontext.WithFiles(map[string]string{ 5862 "foo": "def", 5863 })) 5864 defer ctx.Close() 5865 5866 cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx)) 5867 5868 out := cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\bar").Combined() 5869 c.Assert(strings.TrimSpace(out), check.Equals, "abc") 5870 out = cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\foo").Combined() 5871 c.Assert(strings.TrimSpace(out), check.Equals, "def") 5872} 5873 5874func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) { 5875 testRequires(c, DaemonIsWindows) 5876 dockerfile := ` 5877 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5878 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5879 COPY --from=0 %s c:\\oscopy 5880 ` 5881 exp := icmd.Expected{ 5882 ExitCode: 1, 5883 Err: "copy from c:\\ or c:\\windows is not allowed on windows", 5884 } 5885 buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\"))).Assert(c, exp) 5886 buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "C:\\\\"))).Assert(c, exp) 5887 buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\windows"))).Assert(c, exp) 5888 buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\wInDows"))).Assert(c, exp) 5889} 5890 5891func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *check.C) { 5892 testRequires(c, DaemonIsWindows) 5893 dockerfile := ` 5894 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5895 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5896 COPY --from=0 %s c:\\oscopy 5897 ` 5898 exp := icmd.Expected{ 5899 ExitCode: 1, 5900 Err: "copy from c:\\ or c:\\windows is not allowed on windows", 5901 } 5902 buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:"))).Assert(c, exp) 5903 buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "."))).Assert(c, exp) 5904 buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "..\\\\"))).Assert(c, exp) 5905 buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, ".\\\\windows"))).Assert(c, exp) 5906 buildImage("testforbidsystempaths5", build.WithDockerfile(fmt.Sprintf(dockerfile, "\\\\windows"))).Assert(c, exp) 5907} 5908 5909func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) { 5910 testRequires(c, DaemonIsWindows) 5911 dockerfile := ` 5912 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5913 COPY foo / 5914 FROM ` + testEnv.PlatformDefaults.BaseImage + ` 5915 COPY --from=0 c:\\fOo c:\\copied 5916 RUN type c:\\copied 5917 ` 5918 cli.Docker(cli.Build("copyfrom-windows-insensitive"), build.WithBuildContext(c, 5919 build.WithFile("Dockerfile", dockerfile), 5920 build.WithFile("foo", "hello world"), 5921 )).Assert(c, icmd.Expected{ 5922 ExitCode: 0, 5923 Out: "hello world", 5924 }) 5925} 5926 5927// #33176 5928func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) { 5929 testRequires(c, DaemonIsLinux) 5930 5931 dockerfile := ` 5932 FROM busybox 5933 WORKDIR /foo/bar 5934 FROM scratch 5935 ENV FOO=bar 5936 ` 5937 ctx := fakecontext.New(c, "", 5938 fakecontext.WithDockerfile(dockerfile), 5939 ) 5940 defer ctx.Close() 5941 5942 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx)) 5943 5944 res := cli.InspectCmd(c, "build1", cli.Format(".Config.WorkingDir")).Combined() 5945 c.Assert(strings.TrimSpace(res), checker.Equals, "") 5946} 5947 5948func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) { 5949 //todo: need to be removed after 18.06 release 5950 if strings.Contains(testEnv.DaemonInfo.ServerVersion, "18.05.0") { 5951 c.Skip(fmt.Sprintf("Bug fixed in 18.06 or higher.Skipping it for %s", testEnv.DaemonInfo.ServerVersion)) 5952 } 5953 dockerfile := ` 5954 FROM busybox AS build-env 5955 CMD ["/dev"] 5956 FROM busybox 5957 CMD ["/dist"] 5958 ` 5959 ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile)) 5960 defer ctx.Close() 5961 5962 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx), 5963 cli.WithFlags("--target", "build-env")) 5964 5965 res := cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined() 5966 c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`) 5967 5968 // Stage name is case-insensitive by design 5969 cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx), 5970 cli.WithFlags("--target", "BUIld-EnV")) 5971 5972 res = cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined() 5973 c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`) 5974 5975 result := cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx), 5976 cli.WithFlags("--target", "nosuchtarget")) 5977 result.Assert(c, icmd.Expected{ 5978 ExitCode: 1, 5979 Err: "failed to reach build target", 5980 }) 5981} 5982 5983// TestBuildOpaqueDirectory tests that a build succeeds which 5984// creates opaque directories. 5985// See https://github.com/docker/docker/issues/25244 5986func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) { 5987 testRequires(c, DaemonIsLinux) 5988 dockerFile := ` 5989 FROM busybox 5990 RUN mkdir /dir1 && touch /dir1/f1 5991 RUN rm -rf /dir1 && mkdir /dir1 && touch /dir1/f2 5992 RUN touch /dir1/f3 5993 RUN [ -f /dir1/f2 ] 5994 ` 5995 // Test that build succeeds, last command fails if opaque directory 5996 // was not handled correctly 5997 buildImageSuccessfully(c, "testopaquedirectory", build.WithDockerfile(dockerFile)) 5998} 5999 6000// Windows test for USER in dockerfile 6001func (s *DockerSuite) TestBuildWindowsUser(c *check.C) { 6002 testRequires(c, DaemonIsWindows) 6003 name := "testbuildwindowsuser" 6004 buildImage(name, build.WithDockerfile(`FROM `+testEnv.PlatformDefaults.BaseImage+` 6005 RUN net user user /add 6006 USER user 6007 RUN set username 6008 `)).Assert(c, icmd.Expected{ 6009 Out: "USERNAME=user", 6010 }) 6011} 6012 6013// Verifies if COPY file . when WORKDIR is set to a non-existing directory, 6014// the directory is created and the file is copied into the directory, 6015// as opposed to the file being copied as a file with the name of the 6016// directory. Fix for 27545 (found on Windows, but regression good for Linux too). 6017// Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514. 6018func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) { 6019 name := "testbuildcopyfiledotwithworkdir" 6020 buildImageSuccessfully(c, name, build.WithBuildContext(c, 6021 build.WithFile("Dockerfile", `FROM busybox 6022WORKDIR /foo 6023COPY file . 6024RUN ["cat", "/foo/file"] 6025`), 6026 build.WithFile("file", "content"), 6027 )) 6028} 6029 6030// Case-insensitive environment variables on Windows 6031func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) { 6032 testRequires(c, DaemonIsWindows) 6033 name := "testbuildwindowsenvcaseinsensitive" 6034 buildImageSuccessfully(c, name, build.WithDockerfile(` 6035 FROM `+testEnv.PlatformDefaults.BaseImage+` 6036 ENV FOO=bar foo=baz 6037 `)) 6038 res := inspectFieldJSON(c, name, "Config.Env") 6039 if res != `["foo=baz"]` { // Should not have FOO=bar in it - takes the last one processed. And only one entry as deduped. 6040 c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res) 6041 } 6042} 6043 6044// Test case for 29667 6045func (s *DockerSuite) TestBuildWorkdirImageCmd(c *check.C) { 6046 image := "testworkdirimagecmd" 6047 buildImageSuccessfully(c, image, build.WithDockerfile(` 6048FROM busybox 6049WORKDIR /foo/bar 6050`)) 6051 out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 6052 c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`) 6053 6054 image = "testworkdirlabelimagecmd" 6055 buildImageSuccessfully(c, image, build.WithDockerfile(` 6056FROM busybox 6057WORKDIR /foo/bar 6058LABEL a=b 6059`)) 6060 6061 out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image) 6062 c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`) 6063} 6064 6065// Test case for 28902/28909 6066func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) { 6067 testRequires(c, DaemonIsLinux) 6068 name := "testbuildworkdircmd" 6069 dockerFile := ` 6070 FROM busybox 6071 WORKDIR / 6072 ` 6073 buildImageSuccessfully(c, name, build.WithDockerfile(dockerFile)) 6074 result := buildImage(name, build.WithDockerfile(dockerFile)) 6075 result.Assert(c, icmd.Success) 6076 c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1) 6077} 6078 6079// FIXME(vdemeester) should be a unit test 6080func (s *DockerSuite) TestBuildLineErrorOnBuild(c *check.C) { 6081 name := "test_build_line_error_onbuild" 6082 buildImage(name, build.WithDockerfile(`FROM busybox 6083 ONBUILD 6084 `)).Assert(c, icmd.Expected{ 6085 ExitCode: 1, 6086 Err: "Dockerfile parse error line 2: ONBUILD requires at least one argument", 6087 }) 6088} 6089 6090// FIXME(vdemeester) should be a unit test 6091func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *check.C) { 6092 name := "test_build_line_error_unknown_instruction" 6093 cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox 6094 RUN echo hello world 6095 NOINSTRUCTION echo ba 6096 RUN echo hello 6097 ERROR 6098 `)).Assert(c, icmd.Expected{ 6099 ExitCode: 1, 6100 Err: "Dockerfile parse error line 3: unknown instruction: NOINSTRUCTION", 6101 }) 6102} 6103 6104// FIXME(vdemeester) should be a unit test 6105func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) { 6106 name := "test_build_line_error_with_empty_lines" 6107 cli.Docker(cli.Build(name), build.WithDockerfile(` 6108 FROM busybox 6109 6110 RUN echo hello world 6111 6112 NOINSTRUCTION echo ba 6113 6114 CMD ["/bin/init"] 6115 `)).Assert(c, icmd.Expected{ 6116 ExitCode: 1, 6117 Err: "Dockerfile parse error line 6: unknown instruction: NOINSTRUCTION", 6118 }) 6119} 6120 6121// FIXME(vdemeester) should be a unit test 6122func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) { 6123 name := "test_build_line_error_with_comments" 6124 cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox 6125 # This will print hello world 6126 # and then ba 6127 RUN echo hello world 6128 NOINSTRUCTION echo ba 6129 `)).Assert(c, icmd.Expected{ 6130 ExitCode: 1, 6131 Err: "Dockerfile parse error line 5: unknown instruction: NOINSTRUCTION", 6132 }) 6133} 6134 6135// #31957 6136func (s *DockerSuite) TestBuildSetCommandWithDefinedShell(c *check.C) { 6137 buildImageSuccessfully(c, "build1", build.WithDockerfile(` 6138FROM busybox 6139SHELL ["/bin/sh", "-c"] 6140`)) 6141 buildImageSuccessfully(c, "build2", build.WithDockerfile(` 6142FROM build1 6143CMD echo foo 6144`)) 6145 6146 out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", "build2") 6147 c.Assert(strings.TrimSpace(out), checker.Equals, `["/bin/sh","-c","echo foo"]`) 6148} 6149 6150// FIXME(vdemeester) should migrate to docker/cli tests 6151func (s *DockerSuite) TestBuildIidFile(c *check.C) { 6152 tmpDir, err := ioutil.TempDir("", "TestBuildIidFile") 6153 if err != nil { 6154 c.Fatal(err) 6155 } 6156 defer os.RemoveAll(tmpDir) 6157 tmpIidFile := filepath.Join(tmpDir, "iid") 6158 6159 name := "testbuildiidfile" 6160 // Use a Dockerfile with multiple stages to ensure we get the last one 6161 cli.BuildCmd(c, name, 6162 build.WithDockerfile(`FROM `+minimalBaseImage()+` AS stage1 6163ENV FOO FOO 6164FROM `+minimalBaseImage()+` 6165ENV BAR BAZ`), 6166 cli.WithFlags("--iidfile", tmpIidFile)) 6167 6168 id, err := ioutil.ReadFile(tmpIidFile) 6169 c.Assert(err, check.IsNil) 6170 d, err := digest.Parse(string(id)) 6171 c.Assert(err, check.IsNil) 6172 c.Assert(d.String(), checker.Equals, getIDByName(c, name)) 6173} 6174 6175// FIXME(vdemeester) should migrate to docker/cli tests 6176func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *check.C) { 6177 tmpDir, err := ioutil.TempDir("", "TestBuildIidFileCleanupOnFail") 6178 if err != nil { 6179 c.Fatal(err) 6180 } 6181 defer os.RemoveAll(tmpDir) 6182 tmpIidFile := filepath.Join(tmpDir, "iid") 6183 6184 err = ioutil.WriteFile(tmpIidFile, []byte("Dummy"), 0666) 6185 c.Assert(err, check.IsNil) 6186 6187 cli.Docker(cli.Build("testbuildiidfilecleanuponfail"), 6188 build.WithDockerfile(`FROM `+minimalBaseImage()+` 6189 RUN /non/existing/command`), 6190 cli.WithFlags("--iidfile", tmpIidFile)).Assert(c, icmd.Expected{ 6191 ExitCode: 1, 6192 }) 6193 _, err = os.Stat(tmpIidFile) 6194 c.Assert(err, check.NotNil) 6195 c.Assert(os.IsNotExist(err), check.Equals, true) 6196} 6197