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