github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration-cli/docker_cli_build_test.go (about)

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