github.com/lacework-dev/go-moby@v20.10.12+incompatible/integration-cli/docker_cli_build_test.go (about)

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