github.com/rish1988/moby@v25.0.2+incompatible/integration-cli/docker_cli_build_test.go (about)

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