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