github.com/skanehira/moby@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_build_test.go (about)

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