github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/integration-cli/docker_cli_build_test.go (about)

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