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
3335	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3336		CMD [ '/bin/sh', '-c', 'echo hi' ]`))
3337
3338	icmd.RunCommand(dockerBinary, "run", "--rm", name).Assert(c, icmd.Expected{
3339		ExitCode: expectedExitCode,
3340	})
3341}
3342
3343func (s *DockerSuite) TestBuildVerboseOut(c *check.C) {
3344	name := "testbuildverboseout"
3345	expected := "\n123\n"
3346
3347	if testEnv.OSType == "windows" {
3348		expected = "\n123\r\n"
3349	}
3350
3351	buildImage(name, build.WithDockerfile(`FROM busybox
3352RUN echo 123`)).Assert(c, icmd.Expected{
3353		Out: expected,
3354	})
3355}
3356
3357func (s *DockerSuite) TestBuildWithTabs(c *check.C) {
3358	name := "testbuildwithtabs"
3359	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nRUN echo\tone\t\ttwo"))
3360	res := inspectFieldJSON(c, name, "ContainerConfig.Cmd")
3361	expected1 := `["/bin/sh","-c","echo\tone\t\ttwo"]`
3362	expected2 := `["/bin/sh","-c","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates
3363	if testEnv.OSType == "windows" {
3364		expected1 = `["cmd","/S","/C","echo\tone\t\ttwo"]`
3365		expected2 = `["cmd","/S","/C","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates
3366	}
3367	if res != expected1 && res != expected2 {
3368		c.Fatalf("Missing tabs.\nGot: %s\nExp: %s or %s", res, expected1, expected2)
3369	}
3370}
3371
3372func (s *DockerSuite) TestBuildLabels(c *check.C) {
3373	name := "testbuildlabel"
3374	expected := `{"License":"GPL","Vendor":"Acme"}`
3375	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3376		LABEL Vendor=Acme
3377                LABEL License GPL`))
3378	res := inspectFieldJSON(c, name, "Config.Labels")
3379	if res != expected {
3380		c.Fatalf("Labels %s, expected %s", res, expected)
3381	}
3382}
3383
3384func (s *DockerSuite) TestBuildLabelsCache(c *check.C) {
3385	name := "testbuildlabelcache"
3386
3387	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3388		LABEL Vendor=Acme`))
3389	id1 := getIDByName(c, name)
3390	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3391		LABEL Vendor=Acme`))
3392	id2 := getIDByName(c, name)
3393	if id1 != id2 {
3394		c.Fatalf("Build 2 should have worked & used cache(%s,%s)", id1, id2)
3395	}
3396
3397	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3398		LABEL Vendor=Acme1`))
3399	id2 = getIDByName(c, name)
3400	if id1 == id2 {
3401		c.Fatalf("Build 3 should have worked & NOT used cache(%s,%s)", id1, id2)
3402	}
3403
3404	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3405		LABEL Vendor Acme`))
3406	id2 = getIDByName(c, name)
3407	if id1 != id2 {
3408		c.Fatalf("Build 4 should have worked & used cache(%s,%s)", id1, id2)
3409	}
3410
3411	// Now make sure the cache isn't used by mistake
3412	buildImageSuccessfully(c, name, build.WithoutCache, build.WithDockerfile(`FROM busybox
3413       LABEL f1=b1 f2=b2`))
3414
3415	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
3416       LABEL f1=b1 f2=b2`))
3417	id2 = getIDByName(c, name)
3418	if id1 == id2 {
3419		c.Fatalf("Build 6 should have worked & NOT used the cache(%s,%s)", id1, id2)
3420	}
3421
3422}
3423
3424// FIXME(vdemeester) port to docker/cli e2e tests (api tests should test suppressOutput option though)
3425func (s *DockerSuite) TestBuildNotVerboseSuccess(c *check.C) {
3426	// This test makes sure that -q works correctly when build is successful:
3427	// stdout has only the image ID (long image ID) and stderr is empty.
3428	outRegexp := regexp.MustCompile("^(sha256:|)[a-z0-9]{64}\\n$")
3429	buildFlags := cli.WithFlags("-q")
3430
3431	tt := []struct {
3432		Name      string
3433		BuildFunc func(string) *icmd.Result
3434	}{
3435		{
3436			Name: "quiet_build_stdin_success",
3437			BuildFunc: func(name string) *icmd.Result {
3438				return buildImage(name, buildFlags, build.WithDockerfile("FROM busybox"))
3439			},
3440		},
3441		{
3442			Name: "quiet_build_ctx_success",
3443			BuildFunc: func(name string) *icmd.Result {
3444				return buildImage(name, buildFlags, build.WithBuildContext(c,
3445					build.WithFile("Dockerfile", "FROM busybox"),
3446					build.WithFile("quiet_build_success_fctx", "test"),
3447				))
3448			},
3449		},
3450		{
3451			Name: "quiet_build_git_success",
3452			BuildFunc: func(name string) *icmd.Result {
3453				git := fakegit.New(c, "repo", map[string]string{
3454					"Dockerfile": "FROM busybox",
3455				}, true)
3456				return buildImage(name, buildFlags, build.WithContextPath(git.RepoURL))
3457			},
3458		},
3459	}
3460
3461	for _, te := range tt {
3462		result := te.BuildFunc(te.Name)
3463		result.Assert(c, icmd.Success)
3464		if outRegexp.Find([]byte(result.Stdout())) == nil {
3465			c.Fatalf("Test %s expected stdout to match the [%v] regexp, but it is [%v]", te.Name, outRegexp, result.Stdout())
3466		}
3467
3468		if result.Stderr() != "" {
3469			c.Fatalf("Test %s expected stderr to be empty, but it is [%#v]", te.Name, result.Stderr())
3470		}
3471	}
3472
3473}
3474
3475// FIXME(vdemeester) migrate to docker/cli tests
3476func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *check.C) {
3477	// This test makes sure that -q works correctly when build fails by
3478	// comparing between the stderr output in quiet mode and in stdout
3479	// and stderr output in verbose mode
3480	testRequires(c, Network)
3481	testName := "quiet_build_not_exists_image"
3482	dockerfile := "FROM busybox11"
3483	quietResult := buildImage(testName, cli.WithFlags("-q"), build.WithDockerfile(dockerfile))
3484	quietResult.Assert(c, icmd.Expected{
3485		ExitCode: 1,
3486	})
3487	result := buildImage(testName, build.WithDockerfile(dockerfile))
3488	result.Assert(c, icmd.Expected{
3489		ExitCode: 1,
3490	})
3491	if quietResult.Stderr() != result.Combined() {
3492		c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", testName, quietResult.Stderr(), result.Combined()))
3493	}
3494}
3495
3496// FIXME(vdemeester) migrate to docker/cli tests
3497func (s *DockerSuite) TestBuildNotVerboseFailure(c *check.C) {
3498	// This test makes sure that -q works correctly when build fails by
3499	// comparing between the stderr output in quiet mode and in stdout
3500	// and stderr output in verbose mode
3501	testCases := []struct {
3502		testName   string
3503		dockerfile string
3504	}{
3505		{"quiet_build_no_from_at_the_beginning", "RUN whoami"},
3506		{"quiet_build_unknown_instr", "FROMD busybox"},
3507	}
3508
3509	for _, tc := range testCases {
3510		quietResult := buildImage(tc.testName, cli.WithFlags("-q"), build.WithDockerfile(tc.dockerfile))
3511		quietResult.Assert(c, icmd.Expected{
3512			ExitCode: 1,
3513		})
3514		result := buildImage(tc.testName, build.WithDockerfile(tc.dockerfile))
3515		result.Assert(c, icmd.Expected{
3516			ExitCode: 1,
3517		})
3518		if quietResult.Stderr() != result.Combined() {
3519			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()))
3520		}
3521	}
3522}
3523
3524// FIXME(vdemeester) migrate to docker/cli tests
3525func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *check.C) {
3526	// This test ensures that when given a wrong URL, stderr in quiet mode and
3527	// stderr in verbose mode are identical.
3528	// TODO(vdemeester) with cobra, stdout has a carriage return too much so this test should not check stdout
3529	URL := "http://something.invalid"
3530	name := "quiet_build_wrong_remote"
3531	quietResult := buildImage(name, cli.WithFlags("-q"), build.WithContextPath(URL))
3532	quietResult.Assert(c, icmd.Expected{
3533		ExitCode: 1,
3534	})
3535	result := buildImage(name, build.WithContextPath(URL))
3536	result.Assert(c, icmd.Expected{
3537		ExitCode: 1,
3538	})
3539
3540	// An error message should contain name server IP and port, like this:
3541	//  "dial tcp: lookup something.invalid on 172.29.128.11:53: no such host"
3542	// The IP:port need to be removed in order to not trigger a test failur
3543	// when more than one nameserver is configured.
3544	// While at it, also strip excessive newlines.
3545	normalize := func(msg string) string {
3546		return strings.TrimSpace(regexp.MustCompile("[1-9][0-9.]+:[0-9]+").ReplaceAllLiteralString(msg, "<ip:port>"))
3547	}
3548
3549	if normalize(quietResult.Stderr()) != normalize(result.Combined()) {
3550		c.Fatal(fmt.Errorf("Test[%s] expected that quiet stderr and verbose stdout are equal; quiet [%v], verbose [%v]", name, quietResult.Stderr(), result.Combined()))
3551	}
3552}
3553
3554// FIXME(vdemeester) migrate to docker/cli tests
3555func (s *DockerSuite) TestBuildStderr(c *check.C) {
3556	// This test just makes sure that no non-error output goes
3557	// to stderr
3558	name := "testbuildstderr"
3559	result := buildImage(name, build.WithDockerfile("FROM busybox\nRUN echo one"))
3560	result.Assert(c, icmd.Success)
3561
3562	// Windows to non-Windows should have a security warning
3563	if runtime.GOOS == "windows" && testEnv.OSType != "windows" && !strings.Contains(result.Stdout(), "SECURITY WARNING:") {
3564		c.Fatalf("Stdout contains unexpected output: %q", result.Stdout())
3565	}
3566
3567	// Stderr should always be empty
3568	if result.Stderr() != "" {
3569		c.Fatalf("Stderr should have been empty, instead it's: %q", result.Stderr())
3570	}
3571}
3572
3573func (s *DockerSuite) TestBuildChownSingleFile(c *check.C) {
3574	testRequires(c, UnixCli, DaemonIsLinux) // test uses chown: not available on windows
3575
3576	name := "testbuildchownsinglefile"
3577
3578	ctx := fakecontext.New(c, "",
3579		fakecontext.WithDockerfile(`
3580FROM busybox
3581COPY test /
3582RUN ls -l /test
3583RUN [ $(ls -l /test | awk '{print $3":"$4}') = 'root:root' ]
3584`),
3585		fakecontext.WithFiles(map[string]string{
3586			"test": "test",
3587		}))
3588	defer ctx.Close()
3589
3590	if err := os.Chown(filepath.Join(ctx.Dir, "test"), 4242, 4242); err != nil {
3591		c.Fatal(err)
3592	}
3593
3594	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
3595}
3596
3597func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) {
3598	name := "testbuildsymlinkbreakout"
3599	tmpdir, err := ioutil.TempDir("", name)
3600	c.Assert(err, check.IsNil)
3601	defer os.RemoveAll(tmpdir)
3602	ctx := filepath.Join(tmpdir, "context")
3603	if err := os.MkdirAll(ctx, 0755); err != nil {
3604		c.Fatal(err)
3605	}
3606	if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(`
3607	from busybox
3608	add symlink.tar /
3609	add inject /symlink/
3610	`), 0644); err != nil {
3611		c.Fatal(err)
3612	}
3613	inject := filepath.Join(ctx, "inject")
3614	if err := ioutil.WriteFile(inject, nil, 0644); err != nil {
3615		c.Fatal(err)
3616	}
3617	f, err := os.Create(filepath.Join(ctx, "symlink.tar"))
3618	if err != nil {
3619		c.Fatal(err)
3620	}
3621	w := tar.NewWriter(f)
3622	w.WriteHeader(&tar.Header{
3623		Name:     "symlink2",
3624		Typeflag: tar.TypeSymlink,
3625		Linkname: "/../../../../../../../../../../../../../../",
3626		Uid:      os.Getuid(),
3627		Gid:      os.Getgid(),
3628	})
3629	w.WriteHeader(&tar.Header{
3630		Name:     "symlink",
3631		Typeflag: tar.TypeSymlink,
3632		Linkname: filepath.Join("symlink2", tmpdir),
3633		Uid:      os.Getuid(),
3634		Gid:      os.Getgid(),
3635	})
3636	w.Close()
3637	f.Close()
3638
3639	buildImageSuccessfully(c, name, build.WithoutCache, build.WithExternalBuildContext(fakecontext.New(c, ctx)))
3640	if _, err := os.Lstat(filepath.Join(tmpdir, "inject")); err == nil {
3641		c.Fatal("symlink breakout - inject")
3642	} else if !os.IsNotExist(err) {
3643		c.Fatalf("unexpected error: %v", err)
3644	}
3645}
3646
3647func (s *DockerSuite) TestBuildXZHost(c *check.C) {
3648	// /usr/local/sbin/xz gets permission denied for the user
3649	testRequires(c, NotUserNamespace)
3650	testRequires(c, DaemonIsLinux)
3651	name := "testbuildxzhost"
3652
3653	buildImageSuccessfully(c, name, build.WithBuildContext(c,
3654		build.WithFile("Dockerfile", `
3655FROM busybox
3656ADD xz /usr/local/sbin/
3657RUN chmod 755 /usr/local/sbin/xz
3658ADD test.xz /
3659RUN [ ! -e /injected ]`),
3660		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"),
3661		build.WithFile("xz", "#!/bin/sh\ntouch /injected"),
3662	))
3663}
3664
3665func (s *DockerSuite) TestBuildVolumesRetainContents(c *check.C) {
3666	// /foo/file gets permission denied for the user
3667	testRequires(c, NotUserNamespace)
3668	testRequires(c, DaemonIsLinux) // TODO Windows: Issue #20127
3669	var (
3670		name     = "testbuildvolumescontent"
3671		expected = "some text"
3672		volName  = "/foo"
3673	)
3674
3675	if testEnv.OSType == "windows" {
3676		volName = "C:/foo"
3677	}
3678
3679	buildImageSuccessfully(c, name, build.WithBuildContext(c,
3680		build.WithFile("Dockerfile", `
3681FROM busybox
3682COPY content /foo/file
3683VOLUME `+volName+`
3684CMD cat /foo/file`),
3685		build.WithFile("content", expected),
3686	))
3687
3688	out, _ := dockerCmd(c, "run", "--rm", name)
3689	if out != expected {
3690		c.Fatalf("expected file contents for /foo/file to be %q but received %q", expected, out)
3691	}
3692
3693}
3694
3695func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) {
3696	testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows
3697	testRequires(c, DaemonIsLinux)
3698
3699	// If Dockerfile is not present, use dockerfile
3700	buildImage("test1", build.WithBuildContext(c,
3701		build.WithFile("dockerfile", `FROM busybox
3702	RUN echo from dockerfile`),
3703	)).Assert(c, icmd.Expected{
3704		Out: "from dockerfile",
3705	})
3706
3707	// Prefer Dockerfile in place of dockerfile
3708	buildImage("test1", build.WithBuildContext(c,
3709		build.WithFile("dockerfile", `FROM busybox
3710	RUN echo from dockerfile`),
3711		build.WithFile("Dockerfile", `FROM busybox
3712	RUN echo from Dockerfile`),
3713	)).Assert(c, icmd.Expected{
3714		Out: "from Dockerfile",
3715	})
3716}
3717
3718// FIXME(vdemeester) should migrate to docker/cli tests
3719func (s *DockerSuite) TestBuildFromURLWithF(c *check.C) {
3720	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"baz": `FROM busybox
3721RUN echo from baz
3722COPY * /tmp/
3723RUN find /tmp/`}))
3724	defer server.Close()
3725
3726	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
3727	RUN echo from Dockerfile`))
3728	defer ctx.Close()
3729
3730	// Make sure that -f is ignored and that we don't use the Dockerfile
3731	// that's in the current dir
3732	result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", server.URL()+"/baz"), func(cmd *icmd.Cmd) func() {
3733		cmd.Dir = ctx.Dir
3734		return nil
3735	})
3736
3737	if !strings.Contains(result.Combined(), "from baz") ||
3738		strings.Contains(result.Combined(), "/tmp/baz") ||
3739		!strings.Contains(result.Combined(), "/tmp/Dockerfile") {
3740		c.Fatalf("Missing proper output: %s", result.Combined())
3741	}
3742
3743}
3744
3745// FIXME(vdemeester) should migrate to docker/cli tests
3746func (s *DockerSuite) TestBuildFromStdinWithF(c *check.C) {
3747	testRequires(c, DaemonIsLinux) // TODO Windows: This test is flaky; no idea why
3748	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
3749RUN echo "from Dockerfile"`))
3750	defer ctx.Close()
3751
3752	// Make sure that -f is ignored and that we don't use the Dockerfile
3753	// that's in the current dir
3754	result := cli.BuildCmd(c, "test1", cli.WithFlags("-f", "baz", "-"), func(cmd *icmd.Cmd) func() {
3755		cmd.Dir = ctx.Dir
3756		cmd.Stdin = strings.NewReader(`FROM busybox
3757RUN echo "from baz"
3758COPY * /tmp/
3759RUN sh -c "find /tmp/" # sh -c is needed on Windows to use the correct find`)
3760		return nil
3761	})
3762
3763	if !strings.Contains(result.Combined(), "from baz") ||
3764		strings.Contains(result.Combined(), "/tmp/baz") ||
3765		!strings.Contains(result.Combined(), "/tmp/Dockerfile") {
3766		c.Fatalf("Missing proper output: %s", result.Combined())
3767	}
3768
3769}
3770
3771func (s *DockerSuite) TestBuildFromOfficialNames(c *check.C) {
3772	name := "testbuildfromofficial"
3773	fromNames := []string{
3774		"busybox",
3775		"docker.io/busybox",
3776		"index.docker.io/busybox",
3777		"library/busybox",
3778		"docker.io/library/busybox",
3779		"index.docker.io/library/busybox",
3780	}
3781	for idx, fromName := range fromNames {
3782		imgName := fmt.Sprintf("%s%d", name, idx)
3783		buildImageSuccessfully(c, imgName, build.WithDockerfile("FROM "+fromName))
3784		dockerCmd(c, "rmi", imgName)
3785	}
3786}
3787
3788// FIXME(vdemeester) should be a unit test
3789func (s *DockerSuite) TestBuildSpaces(c *check.C) {
3790	// Test to make sure that leading/trailing spaces on a command
3791	// doesn't change the error msg we get
3792	name := "testspaces"
3793	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile("FROM busybox\nCOPY\n"))
3794	defer ctx.Close()
3795
3796	result1 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx))
3797	result1.Assert(c, icmd.Expected{
3798		ExitCode: 1,
3799	})
3800
3801	ctx.Add("Dockerfile", "FROM busybox\nCOPY    ")
3802	result2 := cli.Docker(cli.Build(name), build.WithExternalBuildContext(ctx))
3803	result2.Assert(c, icmd.Expected{
3804		ExitCode: 1,
3805	})
3806
3807	removeLogTimestamps := func(s string) string {
3808		return regexp.MustCompile(`time="(.*?)"`).ReplaceAllString(s, `time=[TIMESTAMP]`)
3809	}
3810
3811	// Skip over the times
3812	e1 := removeLogTimestamps(result1.Error.Error())
3813	e2 := removeLogTimestamps(result2.Error.Error())
3814
3815	// Ignore whitespace since that's what were verifying doesn't change stuff
3816	if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) {
3817		c.Fatalf("Build 2's error wasn't the same as build 1's\n1:%s\n2:%s", result1.Error, result2.Error)
3818	}
3819
3820	ctx.Add("Dockerfile", "FROM busybox\n   COPY")
3821	result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx))
3822	result2.Assert(c, icmd.Expected{
3823		ExitCode: 1,
3824	})
3825
3826	// Skip over the times
3827	e1 = removeLogTimestamps(result1.Error.Error())
3828	e2 = removeLogTimestamps(result2.Error.Error())
3829
3830	// Ignore whitespace since that's what were verifying doesn't change stuff
3831	if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) {
3832		c.Fatalf("Build 3's error wasn't the same as build 1's\n1:%s\n3:%s", result1.Error, result2.Error)
3833	}
3834
3835	ctx.Add("Dockerfile", "FROM busybox\n   COPY    ")
3836	result2 = cli.Docker(cli.Build(name), build.WithoutCache, build.WithExternalBuildContext(ctx))
3837	result2.Assert(c, icmd.Expected{
3838		ExitCode: 1,
3839	})
3840
3841	// Skip over the times
3842	e1 = removeLogTimestamps(result1.Error.Error())
3843	e2 = removeLogTimestamps(result2.Error.Error())
3844
3845	// Ignore whitespace since that's what were verifying doesn't change stuff
3846	if strings.Replace(e1, " ", "", -1) != strings.Replace(e2, " ", "", -1) {
3847		c.Fatalf("Build 4's error wasn't the same as build 1's\n1:%s\n4:%s", result1.Error, result2.Error)
3848	}
3849
3850}
3851
3852func (s *DockerSuite) TestBuildSpacesWithQuotes(c *check.C) {
3853	// Test to make sure that spaces in quotes aren't lost
3854	name := "testspacesquotes"
3855
3856	dockerfile := `FROM busybox
3857RUN echo "  \
3858  foo  "`
3859
3860	expected := "\n    foo  \n"
3861	// Windows uses the builtin echo, which preserves quotes
3862	if testEnv.OSType == "windows" {
3863		expected = "\"    foo  \""
3864	}
3865
3866	buildImage(name, build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{
3867		Out: expected,
3868	})
3869}
3870
3871// #4393
3872func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *check.C) {
3873	testRequires(c, DaemonIsLinux) // TODO Windows: This should error out
3874	buildImage("docker-test-errcreatevolumewithfile", build.WithDockerfile(`
3875	FROM busybox
3876	RUN touch /foo
3877	VOLUME /foo
3878	`)).Assert(c, icmd.Expected{
3879		ExitCode: 1,
3880		Err:      "file exists",
3881	})
3882}
3883
3884// FIXME(vdemeester) should be a unit test
3885func (s *DockerSuite) TestBuildMissingArgs(c *check.C) {
3886	// Test to make sure that all Dockerfile commands (except the ones listed
3887	// in skipCmds) will generate an error if no args are provided.
3888	// Note: INSERT is deprecated so we exclude it because of that.
3889	skipCmds := map[string]struct{}{
3890		"CMD":        {},
3891		"RUN":        {},
3892		"ENTRYPOINT": {},
3893		"INSERT":     {},
3894	}
3895
3896	if testEnv.OSType == "windows" {
3897		skipCmds = map[string]struct{}{
3898			"CMD":        {},
3899			"RUN":        {},
3900			"ENTRYPOINT": {},
3901			"INSERT":     {},
3902			"STOPSIGNAL": {},
3903			"ARG":        {},
3904			"USER":       {},
3905			"EXPOSE":     {},
3906		}
3907	}
3908
3909	for cmd := range command.Commands {
3910		cmd = strings.ToUpper(cmd)
3911		if _, ok := skipCmds[cmd]; ok {
3912			continue
3913		}
3914		var dockerfile string
3915		if cmd == "FROM" {
3916			dockerfile = cmd
3917		} else {
3918			// Add FROM to make sure we don't complain about it missing
3919			dockerfile = "FROM busybox\n" + cmd
3920		}
3921
3922		buildImage("args", build.WithDockerfile(dockerfile)).Assert(c, icmd.Expected{
3923			ExitCode: 1,
3924			Err:      cmd + " requires",
3925		})
3926	}
3927
3928}
3929
3930func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) {
3931	testRequires(c, DaemonIsLinux)
3932	buildImage("sc", build.WithDockerfile("FROM scratch")).Assert(c, icmd.Expected{
3933		ExitCode: 1,
3934		Err:      "No image was generated",
3935	})
3936}
3937
3938func (s *DockerSuite) TestBuildDotDotFile(c *check.C) {
3939	buildImageSuccessfully(c, "sc", build.WithBuildContext(c,
3940		build.WithFile("Dockerfile", "FROM busybox\n"),
3941		build.WithFile("..gitme", ""),
3942	))
3943}
3944
3945func (s *DockerSuite) TestBuildRUNoneJSON(c *check.C) {
3946	testRequires(c, DaemonIsLinux) // No hello-world Windows image
3947	name := "testbuildrunonejson"
3948
3949	buildImage(name, build.WithDockerfile(`FROM hello-world:frozen
3950RUN [ "/hello" ]`)).Assert(c, icmd.Expected{
3951		Out: "Hello from Docker",
3952	})
3953}
3954
3955func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) {
3956	name := "testbuildemptystringvolume"
3957
3958	buildImage(name, build.WithDockerfile(`
3959  FROM busybox
3960  ENV foo=""
3961  VOLUME $foo
3962  `)).Assert(c, icmd.Expected{
3963		ExitCode: 1,
3964	})
3965}
3966
3967func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *check.C) {
3968	testRequires(c, SameHostDaemon, DaemonIsLinux)
3969
3970	cgroupParent := "test"
3971	data, err := ioutil.ReadFile("/proc/self/cgroup")
3972	if err != nil {
3973		c.Fatalf("failed to read '/proc/self/cgroup - %v", err)
3974	}
3975	selfCgroupPaths := ParseCgroupPaths(string(data))
3976	_, found := selfCgroupPaths["memory"]
3977	if !found {
3978		c.Fatalf("unable to find self memory cgroup path. CgroupsPath: %v", selfCgroupPaths)
3979	}
3980	result := buildImage("buildcgroupparent",
3981		cli.WithFlags("--cgroup-parent", cgroupParent),
3982		build.WithDockerfile(`
3983FROM busybox
3984RUN cat /proc/self/cgroup
3985`))
3986	result.Assert(c, icmd.Success)
3987	m, err := regexp.MatchString(fmt.Sprintf("memory:.*/%s/.*", cgroupParent), result.Combined())
3988	c.Assert(err, check.IsNil)
3989	if !m {
3990		c.Fatalf("There is no expected memory cgroup with parent /%s/: %s", cgroupParent, result.Combined())
3991	}
3992}
3993
3994// FIXME(vdemeester) could be a unit test
3995func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) {
3996	// Check to make sure our build output prints the Dockerfile cmd
3997	// property - there was a bug that caused it to be duplicated on the
3998	// Step X  line
3999	name := "testbuildnodupoutput"
4000	result := buildImage(name, build.WithDockerfile(`
4001  FROM busybox
4002  RUN env`))
4003	result.Assert(c, icmd.Success)
4004	exp := "\nStep 2/2 : RUN env\n"
4005	if !strings.Contains(result.Combined(), exp) {
4006		c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp)
4007	}
4008}
4009
4010// GH15826
4011// FIXME(vdemeester) could be a unit test
4012func (s *DockerSuite) TestBuildStartsFromOne(c *check.C) {
4013	// Explicit check to ensure that build starts from step 1 rather than 0
4014	name := "testbuildstartsfromone"
4015	result := buildImage(name, build.WithDockerfile(`FROM busybox`))
4016	result.Assert(c, icmd.Success)
4017	exp := "\nStep 1/1 : FROM busybox\n"
4018	if !strings.Contains(result.Combined(), exp) {
4019		c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", result.Combined(), exp)
4020	}
4021}
4022
4023func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) {
4024	// Test to make sure the bad command is quoted with just "s and
4025	// not as a Go []string
4026	name := "testbuildbadrunerrmsg"
4027	shell := "/bin/sh -c"
4028	exitCode := 127
4029	if testEnv.OSType == "windows" {
4030		shell = "cmd /S /C"
4031		// architectural - Windows has to start the container to determine the exe is bad, Linux does not
4032		exitCode = 1
4033	}
4034	exp := fmt.Sprintf(`The command '%s badEXE a1 \& a2	a3' returned a non-zero code: %d`, shell, exitCode)
4035
4036	buildImage(name, build.WithDockerfile(`
4037  FROM busybox
4038  RUN badEXE a1 \& a2	a3`)).Assert(c, icmd.Expected{
4039		ExitCode: exitCode,
4040		Err:      exp,
4041	})
4042}
4043
4044// Issue #15634: COPY fails when path starts with "null"
4045func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
4046	name := "testbuildnullstringinaddcopyvolume"
4047	volName := "nullvolume"
4048	if testEnv.OSType == "windows" {
4049		volName = `C:\\nullvolume`
4050	}
4051
4052	buildImageSuccessfully(c, name, build.WithBuildContext(c,
4053		build.WithFile("Dockerfile", `
4054		FROM busybox
4055
4056		ADD null /
4057		COPY nullfile /
4058		VOLUME `+volName+`
4059		`),
4060		build.WithFile("null", "test1"),
4061		build.WithFile("nullfile", "test2"),
4062	))
4063}
4064
4065func (s *DockerSuite) TestBuildStopSignal(c *check.C) {
4066	testRequires(c, DaemonIsLinux) // Windows does not support STOPSIGNAL yet
4067	imgName := "test_build_stop_signal"
4068	buildImageSuccessfully(c, imgName, build.WithDockerfile(`FROM busybox
4069		 STOPSIGNAL SIGKILL`))
4070	res := inspectFieldJSON(c, imgName, "Config.StopSignal")
4071	if res != `"SIGKILL"` {
4072		c.Fatalf("Signal %s, expected SIGKILL", res)
4073	}
4074
4075	containerName := "test-container-stop-signal"
4076	dockerCmd(c, "run", "-d", "--name", containerName, imgName, "top")
4077	res = inspectFieldJSON(c, containerName, "Config.StopSignal")
4078	if res != `"SIGKILL"` {
4079		c.Fatalf("Signal %s, expected SIGKILL", res)
4080	}
4081}
4082
4083func (s *DockerSuite) TestBuildBuildTimeArg(c *check.C) {
4084	imgName := "bldargtest"
4085	envKey := "foo"
4086	envVal := "bar"
4087	var dockerfile string
4088	if testEnv.OSType == "windows" {
4089		// Bugs in Windows busybox port - use the default base image and native cmd stuff
4090		dockerfile = fmt.Sprintf(`FROM `+minimalBaseImage()+`
4091			ARG %s
4092			RUN echo %%%s%%
4093			CMD setlocal enableextensions && if defined %s (echo %%%s%%)`, envKey, envKey, envKey, envKey)
4094	} else {
4095		dockerfile = fmt.Sprintf(`FROM busybox
4096			ARG %s
4097			RUN echo $%s
4098			CMD echo $%s`, envKey, envKey, envKey)
4099
4100	}
4101	buildImage(imgName,
4102		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4103		build.WithDockerfile(dockerfile),
4104	).Assert(c, icmd.Expected{
4105		Out: envVal,
4106	})
4107
4108	containerName := "bldargCont"
4109	out, _ := dockerCmd(c, "run", "--name", containerName, imgName)
4110	out = strings.Trim(out, " \r\n'")
4111	if out != "" {
4112		c.Fatalf("run produced invalid output: %q, expected empty string", out)
4113	}
4114}
4115
4116func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) {
4117	imgName := "bldargtest"
4118	envKey := "foo"
4119	envVal := "bar"
4120	envDef := "bar1"
4121	dockerfile := fmt.Sprintf(`FROM busybox
4122		ARG %s=%s`, envKey, envDef)
4123	buildImage(imgName,
4124		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4125		build.WithDockerfile(dockerfile),
4126	).Assert(c, icmd.Expected{
4127		Out: envVal,
4128	})
4129
4130	out, _ := dockerCmd(c, "history", "--no-trunc", imgName)
4131	outputTabs := strings.Split(out, "\n")[1]
4132	if !strings.Contains(outputTabs, envDef) {
4133		c.Fatalf("failed to find arg default in image history output: %q expected: %q", outputTabs, envDef)
4134	}
4135}
4136
4137func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *check.C) {
4138	imgName := "bldargtest"
4139	envKey := "foo"
4140	envVal := "bar"
4141	proxy := "HTTP_PROXY=http://user:password@proxy.example.com"
4142	explicitProxyKey := "http_proxy"
4143	explicitProxyVal := "http://user:password@someproxy.example.com"
4144	dockerfile := fmt.Sprintf(`FROM busybox
4145		ARG %s
4146		ARG %s
4147		RUN echo "Testing Build Args!"`, envKey, explicitProxyKey)
4148
4149	buildImage := func(imgName string) string {
4150		cli.BuildCmd(c, imgName,
4151			cli.WithFlags("--build-arg", "https_proxy=https://proxy.example.com",
4152				"--build-arg", fmt.Sprintf("%s=%s", envKey, envVal),
4153				"--build-arg", fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal),
4154				"--build-arg", proxy),
4155			build.WithDockerfile(dockerfile),
4156		)
4157		return getIDByName(c, imgName)
4158	}
4159
4160	origID := buildImage(imgName)
4161	result := cli.DockerCmd(c, "history", "--no-trunc", imgName)
4162	out := result.Stdout()
4163
4164	if strings.Contains(out, proxy) {
4165		c.Fatalf("failed to exclude proxy settings from history!")
4166	}
4167	if strings.Contains(out, "https_proxy") {
4168		c.Fatalf("failed to exclude proxy settings from history!")
4169	}
4170	result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", envKey, envVal)})
4171	result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal)})
4172
4173	cacheID := buildImage(imgName + "-two")
4174	c.Assert(origID, checker.Equals, cacheID)
4175}
4176
4177func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) {
4178	imgName := "bldargtest"
4179	envKey := "foo"
4180	envVal := "bar"
4181	dockerfile := fmt.Sprintf(`FROM busybox
4182		ARG %s
4183		RUN echo $%s`, envKey, envKey)
4184	buildImageSuccessfully(c, imgName,
4185		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4186		build.WithDockerfile(dockerfile),
4187	)
4188	origImgID := getIDByName(c, imgName)
4189
4190	imgNameCache := "bldargtestcachehit"
4191	buildImageSuccessfully(c, imgNameCache,
4192		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4193		build.WithDockerfile(dockerfile),
4194	)
4195	newImgID := getIDByName(c, imgName)
4196	if newImgID != origImgID {
4197		c.Fatalf("build didn't use cache! expected image id: %q built image id: %q", origImgID, newImgID)
4198	}
4199}
4200
4201func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *check.C) {
4202	imgName := "bldargtest"
4203	envKey := "foo"
4204	envVal := "bar"
4205	extraEnvKey := "foo1"
4206	extraEnvVal := "bar1"
4207	dockerfile := fmt.Sprintf(`FROM busybox
4208		ARG %s
4209		ARG %s
4210		RUN echo $%s`, envKey, extraEnvKey, envKey)
4211	buildImageSuccessfully(c, imgName,
4212		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4213		build.WithDockerfile(dockerfile),
4214	)
4215	origImgID := getIDByName(c, imgName)
4216
4217	imgNameCache := "bldargtestcachemiss"
4218	buildImageSuccessfully(c, imgNameCache,
4219		cli.WithFlags(
4220			"--build-arg", fmt.Sprintf("%s=%s", envKey, envVal),
4221			"--build-arg", fmt.Sprintf("%s=%s", extraEnvKey, extraEnvVal),
4222		),
4223		build.WithDockerfile(dockerfile),
4224	)
4225	newImgID := getIDByName(c, imgNameCache)
4226
4227	if newImgID == origImgID {
4228		c.Fatalf("build used cache, expected a miss!")
4229	}
4230}
4231
4232func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *check.C) {
4233	imgName := "bldargtest"
4234	envKey := "foo"
4235	envVal := "bar"
4236	newEnvVal := "bar1"
4237	dockerfile := fmt.Sprintf(`FROM busybox
4238		ARG %s
4239		RUN echo $%s`, envKey, envKey)
4240	buildImageSuccessfully(c, imgName,
4241		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4242		build.WithDockerfile(dockerfile),
4243	)
4244	origImgID := getIDByName(c, imgName)
4245
4246	imgNameCache := "bldargtestcachemiss"
4247	buildImageSuccessfully(c, imgNameCache,
4248		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, newEnvVal)),
4249		build.WithDockerfile(dockerfile),
4250	)
4251	newImgID := getIDByName(c, imgNameCache)
4252	if newImgID == origImgID {
4253		c.Fatalf("build used cache, expected a miss!")
4254	}
4255}
4256
4257func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *check.C) {
4258	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4259	imgName := "bldargtest"
4260	envKey := "foo"
4261	envVal := "bar"
4262	envValOverride := "barOverride"
4263	dockerfile := fmt.Sprintf(`FROM busybox
4264		ARG %s
4265		ENV %s %s
4266		RUN echo $%s
4267		CMD echo $%s
4268        `, envKey, envKey, envValOverride, envKey, envKey)
4269
4270	result := buildImage(imgName,
4271		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4272		build.WithDockerfile(dockerfile),
4273	)
4274	result.Assert(c, icmd.Success)
4275	if strings.Count(result.Combined(), envValOverride) != 2 {
4276		c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride)
4277	}
4278
4279	containerName := "bldargCont"
4280	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) {
4281		c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride)
4282	}
4283}
4284
4285// FIXME(vdemeester) might be useful to merge with the one above ?
4286func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *check.C) {
4287	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4288	imgName := "bldargtest"
4289	envKey := "foo"
4290	envVal := "bar"
4291	envValOverride := "barOverride"
4292	dockerfile := fmt.Sprintf(`FROM busybox
4293		ENV %s %s
4294		ARG %s
4295		RUN echo $%s
4296		CMD echo $%s
4297        `, envKey, envValOverride, envKey, envKey, envKey)
4298	result := buildImage(imgName,
4299		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4300		build.WithDockerfile(dockerfile),
4301	)
4302	result.Assert(c, icmd.Success)
4303	if strings.Count(result.Combined(), envValOverride) != 2 {
4304		c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride)
4305	}
4306
4307	containerName := "bldargCont"
4308	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) {
4309		c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride)
4310	}
4311}
4312
4313func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) {
4314	imgName := "bldvarstest"
4315
4316	wdVar := "WDIR"
4317	wdVal := "/tmp/"
4318	addVar := "AFILE"
4319	addVal := "addFile"
4320	copyVar := "CFILE"
4321	copyVal := "copyFile"
4322	envVar := "foo"
4323	envVal := "bar"
4324	exposeVar := "EPORT"
4325	exposeVal := "9999"
4326	userVar := "USER"
4327	userVal := "testUser"
4328	volVar := "VOL"
4329	volVal := "/testVol/"
4330	if DaemonIsWindows() {
4331		volVal = "C:\\testVol"
4332		wdVal = "C:\\tmp"
4333	}
4334
4335	buildImageSuccessfully(c, imgName,
4336		cli.WithFlags(
4337			"--build-arg", fmt.Sprintf("%s=%s", wdVar, wdVal),
4338			"--build-arg", fmt.Sprintf("%s=%s", addVar, addVal),
4339			"--build-arg", fmt.Sprintf("%s=%s", copyVar, copyVal),
4340			"--build-arg", fmt.Sprintf("%s=%s", envVar, envVal),
4341			"--build-arg", fmt.Sprintf("%s=%s", exposeVar, exposeVal),
4342			"--build-arg", fmt.Sprintf("%s=%s", userVar, userVal),
4343			"--build-arg", fmt.Sprintf("%s=%s", volVar, volVal),
4344		),
4345		build.WithBuildContext(c,
4346			build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox
4347		ARG %s
4348		WORKDIR ${%s}
4349		ARG %s
4350		ADD ${%s} testDir/
4351		ARG %s
4352		COPY $%s testDir/
4353		ARG %s
4354		ENV %s=${%s}
4355		ARG %s
4356		EXPOSE $%s
4357		ARG %s
4358		USER $%s
4359		ARG %s
4360		VOLUME ${%s}`,
4361				wdVar, wdVar, addVar, addVar, copyVar, copyVar, envVar, envVar,
4362				envVar, exposeVar, exposeVar, userVar, userVar, volVar, volVar)),
4363			build.WithFile(addVal, "some stuff"),
4364			build.WithFile(copyVal, "some stuff"),
4365		),
4366	)
4367
4368	res := inspectField(c, imgName, "Config.WorkingDir")
4369	c.Check(filepath.ToSlash(res), check.Equals, filepath.ToSlash(wdVal))
4370
4371	var resArr []string
4372	inspectFieldAndUnmarshall(c, imgName, "Config.Env", &resArr)
4373
4374	found := false
4375	for _, v := range resArr {
4376		if fmt.Sprintf("%s=%s", envVar, envVal) == v {
4377			found = true
4378			break
4379		}
4380	}
4381	if !found {
4382		c.Fatalf("Config.Env value mismatch. Expected <key=value> to exist: %s=%s, got: %v",
4383			envVar, envVal, resArr)
4384	}
4385
4386	var resMap map[string]interface{}
4387	inspectFieldAndUnmarshall(c, imgName, "Config.ExposedPorts", &resMap)
4388	if _, ok := resMap[fmt.Sprintf("%s/tcp", exposeVal)]; !ok {
4389		c.Fatalf("Config.ExposedPorts value mismatch. Expected exposed port: %s/tcp, got: %v", exposeVal, resMap)
4390	}
4391
4392	res = inspectField(c, imgName, "Config.User")
4393	if res != userVal {
4394		c.Fatalf("Config.User value mismatch. Expected: %s, got: %s", userVal, res)
4395	}
4396
4397	inspectFieldAndUnmarshall(c, imgName, "Config.Volumes", &resMap)
4398	if _, ok := resMap[volVal]; !ok {
4399		c.Fatalf("Config.Volumes value mismatch. Expected volume: %s, got: %v", volVal, resMap)
4400	}
4401}
4402
4403func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *check.C) {
4404	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4405	imgName := "bldvarstest"
4406	envKey := "foo"
4407	envVal := "bar"
4408	envKey1 := "foo1"
4409	envValOverride := "barOverride"
4410	dockerfile := fmt.Sprintf(`FROM busybox
4411		ARG %s
4412		ENV %s %s
4413		ENV %s ${%s}
4414		RUN echo $%s
4415		CMD echo $%s`, envKey, envKey, envValOverride, envKey1, envKey, envKey1, envKey1)
4416	result := buildImage(imgName,
4417		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4418		build.WithDockerfile(dockerfile),
4419	)
4420	result.Assert(c, icmd.Success)
4421	if strings.Count(result.Combined(), envValOverride) != 2 {
4422		c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride)
4423	}
4424
4425	containerName := "bldargCont"
4426	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) {
4427		c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride)
4428	}
4429}
4430
4431func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *check.C) {
4432	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4433	imgName := "bldargtest"
4434	envKey := "foo"
4435	envVal := "bar"
4436	dockerfile := fmt.Sprintf(`FROM busybox
4437		RUN echo $%s
4438		ARG %s
4439		CMD echo $%s`, envKey, envKey, envKey)
4440	result := buildImage(imgName,
4441		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4442		build.WithDockerfile(dockerfile),
4443	)
4444	result.Assert(c, icmd.Success)
4445	if strings.Contains(result.Combined(), envVal) {
4446		c.Fatalf("able to access environment variable in output: %q expected to be missing", result.Combined())
4447	}
4448
4449	containerName := "bldargCont"
4450	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" {
4451		c.Fatalf("run produced invalid output: %q, expected empty string", out)
4452	}
4453}
4454
4455func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *check.C) {
4456	testRequires(c, DaemonIsLinux) // Windows does not support --build-arg
4457	imgName := "bldargtest"
4458	envKey := "HTTP_PROXY"
4459	envVal := "bar"
4460	dockerfile := fmt.Sprintf(`FROM busybox
4461		RUN echo $%s
4462		CMD echo $%s`, envKey, envKey)
4463
4464	result := buildImage(imgName,
4465		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4466		build.WithDockerfile(dockerfile),
4467	)
4468	result.Assert(c, icmd.Success)
4469	if !strings.Contains(result.Combined(), envVal) {
4470		c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envVal)
4471	}
4472	containerName := "bldargCont"
4473	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); out != "\n" {
4474		c.Fatalf("run produced invalid output: %q, expected empty string", out)
4475	}
4476}
4477
4478func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *check.C) {
4479	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4480	imgName := "bldargtest"
4481	envKey := "foo"
4482	envVal := "bar"
4483	envValOverride := "barOverride"
4484	dockerfile := fmt.Sprintf(`FROM busybox
4485		ARG %s=%s
4486		ENV %s $%s
4487		RUN echo $%s
4488		CMD echo $%s`, envKey, envVal, envKey, envKey, envKey, envKey)
4489	result := buildImage(imgName,
4490		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envValOverride)),
4491		build.WithDockerfile(dockerfile),
4492	)
4493	result.Assert(c, icmd.Success)
4494	if strings.Count(result.Combined(), envValOverride) != 1 {
4495		c.Fatalf("failed to access environment variable in output: %q expected: %q", result.Combined(), envValOverride)
4496	}
4497
4498	containerName := "bldargCont"
4499	if out, _ := dockerCmd(c, "run", "--name", containerName, imgName); !strings.Contains(out, envValOverride) {
4500		c.Fatalf("run produced invalid output: %q, expected %q", out, envValOverride)
4501	}
4502}
4503
4504func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *check.C) {
4505	imgName := "bldargtest"
4506	envKey := "foo"
4507	envVal := "bar"
4508	dockerfile := fmt.Sprintf(`FROM busybox
4509		RUN echo $%s
4510		CMD echo $%s`, envKey, envKey)
4511	warnStr := "[Warning] One or more build-args"
4512	buildImage(imgName,
4513		cli.WithFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal)),
4514		build.WithDockerfile(dockerfile),
4515	).Assert(c, icmd.Expected{
4516		Out: warnStr,
4517	})
4518}
4519
4520func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) {
4521	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4522	dockerfile := `FROM busybox
4523		ARG FOO1=fromfile
4524		ARG FOO2=fromfile
4525		ARG FOO3=fromfile
4526		ARG FOO4=fromfile
4527		ARG FOO5
4528		ARG FOO6
4529		ARG FO10
4530		RUN env
4531		RUN [ "$FOO1" == "fromcmd" ]
4532		RUN [ "$FOO2" == "" ]
4533		RUN [ "$FOO3" == "fromenv" ]
4534		RUN [ "$FOO4" == "fromfile" ]
4535		RUN [ "$FOO5" == "fromcmd" ]
4536		# The following should not exist at all in the env
4537		RUN [ "$(env | grep FOO6)" == "" ]
4538		RUN [ "$(env | grep FOO7)" == "" ]
4539		RUN [ "$(env | grep FOO8)" == "" ]
4540		RUN [ "$(env | grep FOO9)" == "" ]
4541		RUN [ "$FO10" == "" ]
4542	    `
4543	result := buildImage("testbuildtimeargenv",
4544		cli.WithFlags(
4545			"--build-arg", fmt.Sprintf("FOO1=fromcmd"),
4546			"--build-arg", fmt.Sprintf("FOO2="),
4547			"--build-arg", fmt.Sprintf("FOO3"), // set in env
4548			"--build-arg", fmt.Sprintf("FOO4"), // not set in env
4549			"--build-arg", fmt.Sprintf("FOO5=fromcmd"),
4550			// FOO6 is not set at all
4551			"--build-arg", fmt.Sprintf("FOO7=fromcmd"), // should produce a warning
4552			"--build-arg", fmt.Sprintf("FOO8="), // should produce a warning
4553			"--build-arg", fmt.Sprintf("FOO9"), // should produce a warning
4554			"--build-arg", fmt.Sprintf("FO10"), // not set in env, empty value
4555		),
4556		cli.WithEnvironmentVariables(append(os.Environ(),
4557			"FOO1=fromenv",
4558			"FOO2=fromenv",
4559			"FOO3=fromenv")...),
4560		build.WithBuildContext(c,
4561			build.WithFile("Dockerfile", dockerfile),
4562		),
4563	)
4564	result.Assert(c, icmd.Success)
4565
4566	// Now check to make sure we got a warning msg about unused build-args
4567	i := strings.Index(result.Combined(), "[Warning]")
4568	if i < 0 {
4569		c.Fatalf("Missing the build-arg warning in %q", result.Combined())
4570	}
4571
4572	out := result.Combined()[i:] // "out" should contain just the warning message now
4573
4574	// These were specified on a --build-arg but no ARG was in the Dockerfile
4575	c.Assert(out, checker.Contains, "FOO7")
4576	c.Assert(out, checker.Contains, "FOO8")
4577	c.Assert(out, checker.Contains, "FOO9")
4578}
4579
4580func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *check.C) {
4581	imgName := "bldargtest"
4582	envKey := "foo"
4583	envKey1 := "foo1"
4584	envKey2 := "foo2"
4585	envKey3 := "foo3"
4586	dockerfile := fmt.Sprintf(`FROM busybox
4587		ARG %s=""
4588		ARG %s=''
4589		ARG %s="''"
4590		ARG %s='""'
4591		RUN [ "$%s" != "$%s" ]
4592		RUN [ "$%s" != "$%s" ]
4593		RUN [ "$%s" != "$%s" ]
4594		RUN [ "$%s" != "$%s" ]
4595		RUN [ "$%s" != "$%s" ]`, envKey, envKey1, envKey2, envKey3,
4596		envKey, envKey2, envKey, envKey3, envKey1, envKey2, envKey1, envKey3,
4597		envKey2, envKey3)
4598	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
4599}
4600
4601func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) {
4602	testRequires(c, DaemonIsLinux) // Windows does not support ARG
4603	imgName := "bldargtest"
4604	envKey := "foo"
4605	envKey1 := "foo1"
4606	envKey2 := "foo2"
4607	dockerfile := fmt.Sprintf(`FROM busybox
4608		ARG %s=
4609		ARG %s=""
4610		ARG %s=''
4611		RUN [ "$%s" == "$%s" ]
4612		RUN [ "$%s" == "$%s" ]
4613		RUN [ "$%s" == "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2)
4614	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
4615}
4616
4617func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *check.C) {
4618	imgName := "bldargtest"
4619	envKey := "foo"
4620	dockerfile := fmt.Sprintf(`FROM busybox
4621		ARG %s
4622		RUN env`, envKey)
4623
4624	result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile))
4625	result.Assert(c, icmd.Success)
4626	if strings.Count(result.Combined(), envKey) != 1 {
4627		c.Fatalf("unexpected number of occurrences of the arg in output: %q expected: 1", result.Combined())
4628	}
4629}
4630
4631func (s *DockerSuite) TestBuildMultiStageArg(c *check.C) {
4632	imgName := "multifrombldargtest"
4633	dockerfile := `FROM busybox
4634    ARG foo=abc
4635    LABEL multifromtest=1
4636    RUN env > /out
4637    FROM busybox
4638    ARG bar=def
4639    RUN env > /out`
4640
4641	result := cli.BuildCmd(c, imgName, build.WithDockerfile(dockerfile))
4642	result.Assert(c, icmd.Success)
4643
4644	result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1")
4645	parentID := strings.TrimSpace(result.Stdout())
4646
4647	result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out")
4648	c.Assert(result.Stdout(), checker.Contains, "foo=abc")
4649
4650	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
4651	c.Assert(result.Stdout(), checker.Not(checker.Contains), "foo")
4652	c.Assert(result.Stdout(), checker.Contains, "bar=def")
4653}
4654
4655func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *check.C) {
4656	imgName := "multifrombldargtest"
4657	dockerfile := `ARG tag=nosuchtag
4658     FROM busybox:${tag}
4659     LABEL multifromtest=1
4660     RUN env > /out
4661     FROM busybox:${tag}
4662     ARG tag
4663     RUN env > /out`
4664
4665	result := cli.BuildCmd(c, imgName,
4666		build.WithDockerfile(dockerfile),
4667		cli.WithFlags("--build-arg", fmt.Sprintf("tag=latest")))
4668	result.Assert(c, icmd.Success)
4669
4670	result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1")
4671	parentID := strings.TrimSpace(result.Stdout())
4672
4673	result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out")
4674	c.Assert(result.Stdout(), checker.Not(checker.Contains), "tag")
4675
4676	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
4677	c.Assert(result.Stdout(), checker.Contains, "tag=latest")
4678}
4679
4680func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *check.C) {
4681	imgName := "multifromunusedarg"
4682	dockerfile := `FROM busybox
4683    ARG foo
4684    FROM busybox
4685    ARG bar
4686    RUN env > /out`
4687
4688	result := cli.BuildCmd(c, imgName,
4689		build.WithDockerfile(dockerfile),
4690		cli.WithFlags("--build-arg", fmt.Sprintf("baz=abc")))
4691	result.Assert(c, icmd.Success)
4692	c.Assert(result.Combined(), checker.Contains, "[Warning]")
4693	c.Assert(result.Combined(), checker.Contains, "[baz] were not consumed")
4694
4695	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
4696	c.Assert(result.Stdout(), checker.Not(checker.Contains), "bar")
4697	c.Assert(result.Stdout(), checker.Not(checker.Contains), "baz")
4698}
4699
4700func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
4701	volName := "testname:/foo"
4702
4703	if testEnv.OSType == "windows" {
4704		volName = "testname:C:\\foo"
4705	}
4706	dockerCmd(c, "run", "-v", volName, "busybox", "sh", "-c", "touch /foo/oops")
4707
4708	dockerFile := `FROM busybox
4709	VOLUME ` + volName + `
4710	RUN ls /foo/oops
4711	`
4712	buildImage("test", build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{
4713		ExitCode: 1,
4714	})
4715}
4716
4717func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
4718	since := daemonUnixTime(c)
4719
4720	dockerFile := `FROM busybox
4721	RUN echo events
4722	`
4723	buildImageSuccessfully(c, "test", build.WithDockerfile(dockerFile))
4724
4725	until := daemonUnixTime(c)
4726	out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "type=image")
4727	events := strings.Split(strings.TrimSpace(out), "\n")
4728	actions := eventActionsByIDAndType(c, events, "test:latest", "image")
4729	var foundTag bool
4730	for _, a := range actions {
4731		if a == "tag" {
4732			foundTag = true
4733			break
4734		}
4735	}
4736
4737	c.Assert(foundTag, checker.True, check.Commentf("No tag event found:\n%s", out))
4738}
4739
4740// #15780
4741func (s *DockerSuite) TestBuildMultipleTags(c *check.C) {
4742	dockerfile := `
4743	FROM busybox
4744	MAINTAINER test-15780
4745	`
4746	buildImageSuccessfully(c, "tag1", cli.WithFlags("-t", "tag2:v2", "-t", "tag1:latest", "-t", "tag1"), build.WithDockerfile(dockerfile))
4747
4748	id1 := getIDByName(c, "tag1")
4749	id2 := getIDByName(c, "tag2:v2")
4750	c.Assert(id1, check.Equals, id2)
4751}
4752
4753// #17290
4754func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
4755	name := "testbuildbrokensymlink"
4756	ctx := fakecontext.New(c, "",
4757		fakecontext.WithDockerfile(`
4758	FROM busybox
4759	COPY . ./`),
4760		fakecontext.WithFiles(map[string]string{
4761			"foo": "bar",
4762		}))
4763	defer ctx.Close()
4764
4765	err := os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink"))
4766	c.Assert(err, checker.IsNil)
4767
4768	// warm up cache
4769	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4770
4771	// add new file to context, should invalidate cache
4772	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644)
4773	c.Assert(err, checker.IsNil)
4774
4775	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4776	if strings.Contains(result.Combined(), "Using cache") {
4777		c.Fatal("2nd build used cache on ADD, it shouldn't")
4778	}
4779}
4780
4781func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) {
4782	name := "testbuildbrokensymlink"
4783	ctx := fakecontext.New(c, "",
4784		fakecontext.WithDockerfile(`
4785	FROM busybox
4786	COPY asymlink target`),
4787		fakecontext.WithFiles(map[string]string{
4788			"foo": "bar",
4789		}))
4790	defer ctx.Close()
4791
4792	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
4793	c.Assert(err, checker.IsNil)
4794
4795	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4796
4797	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined()
4798	c.Assert(out, checker.Matches, "bar")
4799
4800	// change target file should invalidate cache
4801	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
4802	c.Assert(err, checker.IsNil)
4803
4804	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4805	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
4806
4807	out = cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined()
4808	c.Assert(out, checker.Matches, "baz")
4809}
4810
4811func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) {
4812	name := "testbuildbrokensymlink"
4813	ctx := fakecontext.New(c, "",
4814		fakecontext.WithDockerfile(`
4815	FROM busybox
4816	COPY asymlink /`),
4817		fakecontext.WithFiles(map[string]string{
4818			"foo/abc": "bar",
4819			"foo/def": "baz",
4820		}))
4821	defer ctx.Close()
4822
4823	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
4824	c.Assert(err, checker.IsNil)
4825
4826	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4827
4828	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined()
4829	c.Assert(out, checker.Matches, "barbaz")
4830
4831	// change target file should invalidate cache
4832	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644)
4833	c.Assert(err, checker.IsNil)
4834
4835	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4836	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
4837
4838	out = cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined()
4839	c.Assert(out, checker.Matches, "barbax")
4840
4841}
4842
4843// TestBuildSymlinkBasename tests that target file gets basename from symlink,
4844// not from the target file.
4845func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) {
4846	name := "testbuildbrokensymlink"
4847	ctx := fakecontext.New(c, "",
4848		fakecontext.WithDockerfile(`
4849	FROM busybox
4850	COPY asymlink /`),
4851		fakecontext.WithFiles(map[string]string{
4852			"foo": "bar",
4853		}))
4854	defer ctx.Close()
4855
4856	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
4857	c.Assert(err, checker.IsNil)
4858
4859	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4860
4861	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "asymlink").Combined()
4862	c.Assert(out, checker.Matches, "bar")
4863}
4864
4865// #17827
4866func (s *DockerSuite) TestBuildCacheRootSource(c *check.C) {
4867	name := "testbuildrootsource"
4868	ctx := fakecontext.New(c, "",
4869		fakecontext.WithDockerfile(`
4870	FROM busybox
4871	COPY / /data`),
4872		fakecontext.WithFiles(map[string]string{
4873			"foo": "bar",
4874		}))
4875	defer ctx.Close()
4876
4877	// warm up cache
4878	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4879
4880	// change file, should invalidate cache
4881	err := ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
4882	c.Assert(err, checker.IsNil)
4883
4884	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
4885
4886	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
4887}
4888
4889// #19375
4890// FIXME(vdemeester) should migrate to docker/cli tests
4891func (s *DockerSuite) TestBuildFailsGitNotCallable(c *check.C) {
4892	buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="),
4893		build.WithContextPath("github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{
4894		ExitCode: 1,
4895		Err:      "unable to prepare context: unable to find 'git': ",
4896	})
4897
4898	buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="),
4899		build.WithContextPath("https://github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{
4900		ExitCode: 1,
4901		Err:      "unable to prepare context: unable to find 'git': ",
4902	})
4903}
4904
4905// TestBuildWorkdirWindowsPath tests that a Windows style path works as a workdir
4906func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
4907	testRequires(c, DaemonIsWindows)
4908	name := "testbuildworkdirwindowspath"
4909	buildImageSuccessfully(c, name, build.WithDockerfile(`
4910	FROM `+testEnv.PlatformDefaults.BaseImage+`
4911	RUN mkdir C:\\work
4912	WORKDIR C:\\work
4913	RUN if "%CD%" NEQ "C:\work" exit -1
4914	`))
4915}
4916
4917func (s *DockerSuite) TestBuildLabel(c *check.C) {
4918	name := "testbuildlabel"
4919	testLabel := "foo"
4920
4921	buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel),
4922		build.WithDockerfile(`
4923  FROM `+minimalBaseImage()+`
4924  LABEL default foo
4925`))
4926
4927	var labels map[string]string
4928	inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels)
4929	if _, ok := labels[testLabel]; !ok {
4930		c.Fatal("label not found in image")
4931	}
4932}
4933
4934func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) {
4935	name := "testbuildlabel"
4936	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=bar"),
4937		build.WithDockerfile("FROM busybox"))
4938
4939	var labels map[string]string
4940	inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels)
4941	v, ok := labels["foo"]
4942	if !ok {
4943		c.Fatal("label `foo` not found in image")
4944	}
4945	c.Assert(v, checker.Equals, "bar")
4946}
4947
4948func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) {
4949	name := "testbuildlabelcachecommit"
4950	testLabel := "foo"
4951
4952	buildImageSuccessfully(c, name, build.WithDockerfile(`
4953  FROM `+minimalBaseImage()+`
4954  LABEL default foo
4955  `))
4956	buildImageSuccessfully(c, name, cli.WithFlags("--label", testLabel),
4957		build.WithDockerfile(`
4958  FROM `+minimalBaseImage()+`
4959  LABEL default foo
4960  `))
4961
4962	var labels map[string]string
4963	inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels)
4964	if _, ok := labels[testLabel]; !ok {
4965		c.Fatal("label not found in image")
4966	}
4967}
4968
4969func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) {
4970	name := "testbuildlabelmultiple"
4971	testLabels := map[string]string{
4972		"foo": "bar",
4973		"123": "456",
4974	}
4975	var labelArgs []string
4976	for k, v := range testLabels {
4977		labelArgs = append(labelArgs, "--label", k+"="+v)
4978	}
4979
4980	buildImageSuccessfully(c, name, cli.WithFlags(labelArgs...),
4981		build.WithDockerfile(`
4982  FROM `+minimalBaseImage()+`
4983  LABEL default foo
4984`))
4985
4986	var labels map[string]string
4987	inspectFieldAndUnmarshall(c, name, "Config.Labels", &labels)
4988	for k, v := range testLabels {
4989		if x, ok := labels[k]; !ok || x != v {
4990			c.Fatalf("label %s=%s not found in image", k, v)
4991		}
4992	}
4993}
4994
4995func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
4996	dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
4997	baseImage := privateRegistryURL + "/baseimage"
4998
4999	buildImageSuccessfully(c, baseImage, build.WithDockerfile(`
5000	FROM busybox
5001	ENV env1 val1
5002	`))
5003
5004	dockerCmd(c, "push", baseImage)
5005	dockerCmd(c, "rmi", baseImage)
5006
5007	buildImageSuccessfully(c, baseImage, build.WithDockerfile(fmt.Sprintf(`
5008	FROM %s
5009	ENV env2 val2
5010	`, baseImage)))
5011}
5012
5013func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) {
5014	osPath := os.Getenv("PATH")
5015	defer os.Setenv("PATH", osPath)
5016
5017	workingDir, err := os.Getwd()
5018	c.Assert(err, checker.IsNil)
5019	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
5020	c.Assert(err, checker.IsNil)
5021	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
5022
5023	os.Setenv("PATH", testPath)
5024
5025	repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
5026
5027	tmp, err := ioutil.TempDir("", "integration-cli-")
5028	c.Assert(err, checker.IsNil)
5029
5030	externalAuthConfig := `{ "credsStore": "shell-test" }`
5031
5032	configPath := filepath.Join(tmp, "config.json")
5033	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
5034	c.Assert(err, checker.IsNil)
5035
5036	dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
5037
5038	b, err := ioutil.ReadFile(configPath)
5039	c.Assert(err, checker.IsNil)
5040	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
5041
5042	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
5043	dockerCmd(c, "--config", tmp, "push", repoName)
5044
5045	// make sure the image is pulled when building
5046	dockerCmd(c, "rmi", repoName)
5047
5048	icmd.RunCmd(icmd.Cmd{
5049		Command: []string{dockerBinary, "--config", tmp, "build", "-"},
5050		Stdin:   strings.NewReader(fmt.Sprintf("FROM %s", repoName)),
5051	}).Assert(c, icmd.Success)
5052}
5053
5054// Test cases in #22036
5055func (s *DockerSuite) TestBuildLabelsOverride(c *check.C) {
5056	// Command line option labels will always override
5057	name := "scratchy"
5058	expected := `{"bar":"from-flag","foo":"from-flag"}`
5059	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"),
5060		build.WithDockerfile(`FROM `+minimalBaseImage()+`
5061                LABEL foo=from-dockerfile`))
5062	res := inspectFieldJSON(c, name, "Config.Labels")
5063	if res != expected {
5064		c.Fatalf("Labels %s, expected %s", res, expected)
5065	}
5066
5067	name = "from"
5068	expected = `{"foo":"from-dockerfile"}`
5069	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
5070                LABEL foo from-dockerfile`))
5071	res = inspectFieldJSON(c, name, "Config.Labels")
5072	if res != expected {
5073		c.Fatalf("Labels %s, expected %s", res, expected)
5074	}
5075
5076	// Command line option label will override even via `FROM`
5077	name = "new"
5078	expected = `{"bar":"from-dockerfile2","foo":"new"}`
5079	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=new"),
5080		build.WithDockerfile(`FROM from
5081                LABEL bar from-dockerfile2`))
5082	res = inspectFieldJSON(c, name, "Config.Labels")
5083	if res != expected {
5084		c.Fatalf("Labels %s, expected %s", res, expected)
5085	}
5086
5087	// Command line option without a value set (--label foo, --label bar=)
5088	// will be treated as --label foo="", --label bar=""
5089	name = "scratchy2"
5090	expected = `{"bar":"","foo":""}`
5091	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo", "--label", "bar="),
5092		build.WithDockerfile(`FROM `+minimalBaseImage()+`
5093                LABEL foo=from-dockerfile`))
5094	res = inspectFieldJSON(c, name, "Config.Labels")
5095	if res != expected {
5096		c.Fatalf("Labels %s, expected %s", res, expected)
5097	}
5098
5099	// Command line option without a value set (--label foo, --label bar=)
5100	// will be treated as --label foo="", --label bar=""
5101	// This time is for inherited images
5102	name = "new2"
5103	expected = `{"bar":"","foo":""}`
5104	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=", "--label", "bar"),
5105		build.WithDockerfile(`FROM from
5106                LABEL bar from-dockerfile2`))
5107	res = inspectFieldJSON(c, name, "Config.Labels")
5108	if res != expected {
5109		c.Fatalf("Labels %s, expected %s", res, expected)
5110	}
5111
5112	// Command line option labels with only `FROM`
5113	name = "scratchy"
5114	expected = `{"bar":"from-flag","foo":"from-flag"}`
5115	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=from-flag", "--label", "bar=from-flag"),
5116		build.WithDockerfile(`FROM `+minimalBaseImage()))
5117	res = inspectFieldJSON(c, name, "Config.Labels")
5118	if res != expected {
5119		c.Fatalf("Labels %s, expected %s", res, expected)
5120	}
5121
5122	// Command line option labels with env var
5123	name = "scratchz"
5124	expected = `{"bar":"$PATH"}`
5125	buildImageSuccessfully(c, name, cli.WithFlags("--label", "bar=$PATH"),
5126		build.WithDockerfile(`FROM `+minimalBaseImage()))
5127	res = inspectFieldJSON(c, name, "Config.Labels")
5128	if res != expected {
5129		c.Fatalf("Labels %s, expected %s", res, expected)
5130	}
5131}
5132
5133// Test case for #22855
5134func (s *DockerSuite) TestBuildDeleteCommittedFile(c *check.C) {
5135	name := "test-delete-committed-file"
5136	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
5137		RUN echo test > file
5138		RUN test -e file
5139		RUN rm file
5140		RUN sh -c "! test -e file"`))
5141}
5142
5143// #20083
5144func (s *DockerSuite) TestBuildDockerignoreComment(c *check.C) {
5145	// TODO Windows: Figure out why this test is flakey on TP5. If you add
5146	// something like RUN sleep 5, or even RUN ls /tmp after the ADD line,
5147	// it is more reliable, but that's not a good fix.
5148	testRequires(c, DaemonIsLinux)
5149
5150	name := "testbuilddockerignorecleanpaths"
5151	dockerfile := `
5152        FROM busybox
5153        ADD . /tmp/
5154        RUN sh -c "(ls -la /tmp/#1)"
5155        RUN sh -c "(! ls -la /tmp/#2)"
5156        RUN sh -c "(! ls /tmp/foo) && (! ls /tmp/foo2) && (ls /tmp/dir1/foo)"`
5157	buildImageSuccessfully(c, name, build.WithBuildContext(c,
5158		build.WithFile("Dockerfile", dockerfile),
5159		build.WithFile("foo", "foo"),
5160		build.WithFile("foo2", "foo2"),
5161		build.WithFile("dir1/foo", "foo in dir1"),
5162		build.WithFile("#1", "# file 1"),
5163		build.WithFile("#2", "# file 2"),
5164		build.WithFile(".dockerignore", `# Visual C++ cache files
5165# because we have git ;-)
5166# The above comment is from #20083
5167foo
5168#dir1/foo
5169foo2
5170# The following is considered as comment as # is at the beginning
5171#1
5172# The following is not considered as comment as # is not at the beginning
5173  #2
5174`)))
5175}
5176
5177// Test case for #23221
5178func (s *DockerSuite) TestBuildWithUTF8BOM(c *check.C) {
5179	name := "test-with-utf8-bom"
5180	dockerfile := []byte(`FROM busybox`)
5181	bomDockerfile := append([]byte{0xEF, 0xBB, 0xBF}, dockerfile...)
5182	buildImageSuccessfully(c, name, build.WithBuildContext(c,
5183		build.WithFile("Dockerfile", string(bomDockerfile)),
5184	))
5185}
5186
5187// Test case for UTF-8 BOM in .dockerignore, related to #23221
5188func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *check.C) {
5189	name := "test-with-utf8-bom-dockerignore"
5190	dockerfile := `
5191        FROM busybox
5192		ADD . /tmp/
5193		RUN ls -la /tmp
5194		RUN sh -c "! ls /tmp/Dockerfile"
5195		RUN ls /tmp/.dockerignore`
5196	dockerignore := []byte("./Dockerfile\n")
5197	bomDockerignore := append([]byte{0xEF, 0xBB, 0xBF}, dockerignore...)
5198	buildImageSuccessfully(c, name, build.WithBuildContext(c,
5199		build.WithFile("Dockerfile", dockerfile),
5200		build.WithFile(".dockerignore", string(bomDockerignore)),
5201	))
5202}
5203
5204// #22489 Shell test to confirm config gets updated correctly
5205func (s *DockerSuite) TestBuildShellUpdatesConfig(c *check.C) {
5206	name := "testbuildshellupdatesconfig"
5207
5208	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
5209        SHELL ["foo", "-bar"]`))
5210	expected := `["foo","-bar","#(nop) ","SHELL [foo -bar]"]`
5211	res := inspectFieldJSON(c, name, "ContainerConfig.Cmd")
5212	if res != expected {
5213		c.Fatalf("%s, expected %s", res, expected)
5214	}
5215	res = inspectFieldJSON(c, name, "ContainerConfig.Shell")
5216	if res != `["foo","-bar"]` {
5217		c.Fatalf(`%s, expected ["foo","-bar"]`, res)
5218	}
5219}
5220
5221// #22489 Changing the shell multiple times and CMD after.
5222func (s *DockerSuite) TestBuildShellMultiple(c *check.C) {
5223	name := "testbuildshellmultiple"
5224
5225	result := buildImage(name, build.WithDockerfile(`FROM busybox
5226		RUN echo defaultshell
5227		SHELL ["echo"]
5228		RUN echoshell
5229		SHELL ["ls"]
5230		RUN -l
5231		CMD -l`))
5232	result.Assert(c, icmd.Success)
5233
5234	// Must contain 'defaultshell' twice
5235	if len(strings.Split(result.Combined(), "defaultshell")) != 3 {
5236		c.Fatalf("defaultshell should have appeared twice in %s", result.Combined())
5237	}
5238
5239	// Must contain 'echoshell' twice
5240	if len(strings.Split(result.Combined(), "echoshell")) != 3 {
5241		c.Fatalf("echoshell should have appeared twice in %s", result.Combined())
5242	}
5243
5244	// Must contain "total " (part of ls -l)
5245	if !strings.Contains(result.Combined(), "total ") {
5246		c.Fatalf("%s should have contained 'total '", result.Combined())
5247	}
5248
5249	// A container started from the image uses the shell-form CMD.
5250	// Last shell is ls. CMD is -l. So should contain 'total '.
5251	outrun, _ := dockerCmd(c, "run", "--rm", name)
5252	if !strings.Contains(outrun, "total ") {
5253		c.Fatalf("Expected started container to run ls -l. %s", outrun)
5254	}
5255}
5256
5257// #22489. Changed SHELL with ENTRYPOINT
5258func (s *DockerSuite) TestBuildShellEntrypoint(c *check.C) {
5259	name := "testbuildshellentrypoint"
5260
5261	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
5262		SHELL ["ls"]
5263		ENTRYPOINT -l`))
5264	// A container started from the image uses the shell-form ENTRYPOINT.
5265	// Shell is ls. ENTRYPOINT is -l. So should contain 'total '.
5266	outrun, _ := dockerCmd(c, "run", "--rm", name)
5267	if !strings.Contains(outrun, "total ") {
5268		c.Fatalf("Expected started container to run ls -l. %s", outrun)
5269	}
5270}
5271
5272// #22489 Shell test to confirm shell is inherited in a subsequent build
5273func (s *DockerSuite) TestBuildShellInherited(c *check.C) {
5274	name1 := "testbuildshellinherited1"
5275	buildImageSuccessfully(c, name1, build.WithDockerfile(`FROM busybox
5276        SHELL ["ls"]`))
5277	name2 := "testbuildshellinherited2"
5278	buildImage(name2, build.WithDockerfile(`FROM `+name1+`
5279        RUN -l`)).Assert(c, icmd.Expected{
5280		// ls -l has "total " followed by some number in it, ls without -l does not.
5281		Out: "total ",
5282	})
5283}
5284
5285// #22489 Shell test to confirm non-JSON doesn't work
5286func (s *DockerSuite) TestBuildShellNotJSON(c *check.C) {
5287	name := "testbuildshellnotjson"
5288
5289	buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
5290        sHeLl exec -form`, // Casing explicit to ensure error is upper-cased.
5291	)).Assert(c, icmd.Expected{
5292		ExitCode: 1,
5293		Err:      "SHELL requires the arguments to be in JSON form",
5294	})
5295}
5296
5297// #22489 Windows shell test to confirm native is powershell if executing a PS command
5298// This would error if the default shell were still cmd.
5299func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
5300	testRequires(c, DaemonIsWindows)
5301	name := "testbuildshellpowershell"
5302	buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
5303        SHELL ["powershell", "-command"]
5304		RUN Write-Host John`)).Assert(c, icmd.Expected{
5305		Out: "\nJohn\n",
5306	})
5307}
5308
5309// Verify that escape is being correctly applied to words when escape directive is not \.
5310// Tests WORKDIR, ADD
5311func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
5312	testRequires(c, DaemonIsWindows)
5313	name := "testbuildescapenotbackslashwordtesta"
5314	buildImage(name, build.WithDockerfile(`# escape= `+"`"+`
5315		FROM `+minimalBaseImage()+`
5316        WORKDIR c:\windows
5317		RUN dir /w`)).Assert(c, icmd.Expected{
5318		Out: "[System32]",
5319	})
5320
5321	name = "testbuildescapenotbackslashwordtestb"
5322	buildImage(name, build.WithDockerfile(`# escape= `+"`"+`
5323		FROM `+minimalBaseImage()+`
5324		SHELL ["powershell.exe"]
5325        WORKDIR c:\foo
5326		ADD Dockerfile c:\foo\
5327		RUN dir Dockerfile`)).Assert(c, icmd.Expected{
5328		Out: "-a----",
5329	})
5330}
5331
5332// #22868. Make sure shell-form CMD is marked as escaped in the config of the image
5333func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
5334	testRequires(c, DaemonIsWindows)
5335	name := "testbuildcmdshellescaped"
5336	buildImageSuccessfully(c, name, build.WithDockerfile(`
5337  FROM `+minimalBaseImage()+`
5338  CMD "ipconfig"
5339  `))
5340	res := inspectFieldJSON(c, name, "Config.ArgsEscaped")
5341	if res != "true" {
5342		c.Fatalf("CMD did not update Config.ArgsEscaped on image: %v", res)
5343	}
5344	dockerCmd(c, "run", "--name", "inspectme", name)
5345	dockerCmd(c, "wait", "inspectme")
5346	res = inspectFieldJSON(c, name, "Config.Cmd")
5347
5348	if res != `["cmd","/S","/C","\"ipconfig\""]` {
5349		c.Fatalf("CMD was not escaped Config.Cmd: got %v", res)
5350	}
5351}
5352
5353// Test case for #24912.
5354func (s *DockerSuite) TestBuildStepsWithProgress(c *check.C) {
5355	name := "testbuildstepswithprogress"
5356	totalRun := 5
5357	result := buildImage(name, build.WithDockerfile("FROM busybox\n"+strings.Repeat("RUN echo foo\n", totalRun)))
5358	result.Assert(c, icmd.Success)
5359	c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun))
5360	for i := 2; i <= 1+totalRun; i++ {
5361		c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun))
5362	}
5363}
5364
5365func (s *DockerSuite) TestBuildWithFailure(c *check.C) {
5366	name := "testbuildwithfailure"
5367
5368	// First test case can only detect `nobody` in runtime so all steps will show up
5369	dockerfile := "FROM busybox\nRUN nobody"
5370	result := buildImage(name, build.WithDockerfile(dockerfile))
5371	c.Assert(result.Error, checker.NotNil)
5372	c.Assert(result.Stdout(), checker.Contains, "Step 1/2 : FROM busybox")
5373	c.Assert(result.Stdout(), checker.Contains, "Step 2/2 : RUN nobody")
5374
5375	// Second test case `FFOM` should have been detected before build runs so no steps
5376	dockerfile = "FFOM nobody\nRUN nobody"
5377	result = buildImage(name, build.WithDockerfile(dockerfile))
5378	c.Assert(result.Error, checker.NotNil)
5379	c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 1/2 : FROM busybox")
5380	c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 2/2 : RUN nobody")
5381}
5382
5383func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) {
5384	dockerfile := `
5385		FROM busybox
5386		RUN echo "test"
5387		ENTRYPOINT ["sh"]`
5388	ctx := fakecontext.New(c, "",
5389		fakecontext.WithDockerfile(dockerfile),
5390		fakecontext.WithFiles(map[string]string{
5391			"Dockerfile": dockerfile,
5392		}))
5393	defer ctx.Close()
5394
5395	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5396	id1 := getIDByName(c, "build1")
5397
5398	// rebuild with cache-from
5399	result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5400	id2 := getIDByName(c, "build2")
5401	c.Assert(id1, checker.Equals, id2)
5402	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
5403}
5404
5405func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
5406	testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows
5407	dockerfile := `
5408		FROM busybox
5409		ENV FOO=bar
5410		ADD baz /
5411		RUN touch bax`
5412	ctx := fakecontext.New(c, "",
5413		fakecontext.WithDockerfile(dockerfile),
5414		fakecontext.WithFiles(map[string]string{
5415			"Dockerfile": dockerfile,
5416			"baz":        "baz",
5417		}))
5418	defer ctx.Close()
5419
5420	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5421	id1 := getIDByName(c, "build1")
5422
5423	// rebuild with cache-from
5424	result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5425	id2 := getIDByName(c, "build2")
5426	c.Assert(id1, checker.Equals, id2)
5427	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
5428	cli.DockerCmd(c, "rmi", "build2")
5429
5430	// no cache match with unknown source
5431	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=nosuchtag"), build.WithExternalBuildContext(ctx))
5432	id2 = getIDByName(c, "build2")
5433	c.Assert(id1, checker.Not(checker.Equals), id2)
5434	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 0)
5435	cli.DockerCmd(c, "rmi", "build2")
5436
5437	// clear parent images
5438	tempDir, err := ioutil.TempDir("", "test-build-cache-from-")
5439	if err != nil {
5440		c.Fatalf("failed to create temporary directory: %s", tempDir)
5441	}
5442	defer os.RemoveAll(tempDir)
5443	tempFile := filepath.Join(tempDir, "img.tar")
5444	cli.DockerCmd(c, "save", "-o", tempFile, "build1")
5445	cli.DockerCmd(c, "rmi", "build1")
5446	cli.DockerCmd(c, "load", "-i", tempFile)
5447	parentID := cli.DockerCmd(c, "inspect", "-f", "{{.Parent}}", "build1").Combined()
5448	c.Assert(strings.TrimSpace(parentID), checker.Equals, "")
5449
5450	// cache still applies without parents
5451	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5452	id2 = getIDByName(c, "build2")
5453	c.Assert(id1, checker.Equals, id2)
5454	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
5455	history1 := cli.DockerCmd(c, "history", "-q", "build2").Combined()
5456
5457	// Retry, no new intermediate images
5458	result = cli.BuildCmd(c, "build3", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5459	id3 := getIDByName(c, "build3")
5460	c.Assert(id1, checker.Equals, id3)
5461	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
5462	history2 := cli.DockerCmd(c, "history", "-q", "build3").Combined()
5463
5464	c.Assert(history1, checker.Equals, history2)
5465	cli.DockerCmd(c, "rmi", "build2")
5466	cli.DockerCmd(c, "rmi", "build3")
5467	cli.DockerCmd(c, "rmi", "build1")
5468	cli.DockerCmd(c, "load", "-i", tempFile)
5469
5470	// Modify file, everything up to last command and layers are reused
5471	dockerfile = `
5472		FROM busybox
5473		ENV FOO=bar
5474		ADD baz /
5475		RUN touch newfile`
5476	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644)
5477	c.Assert(err, checker.IsNil)
5478
5479	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5480	id2 = getIDByName(c, "build2")
5481	c.Assert(id1, checker.Not(checker.Equals), id2)
5482	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
5483
5484	layers1Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build1").Combined()
5485	layers2Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build2").Combined()
5486
5487	var layers1 []string
5488	var layers2 []string
5489	c.Assert(json.Unmarshal([]byte(layers1Str), &layers1), checker.IsNil)
5490	c.Assert(json.Unmarshal([]byte(layers2Str), &layers2), checker.IsNil)
5491
5492	c.Assert(len(layers1), checker.Equals, len(layers2))
5493	for i := 0; i < len(layers1)-1; i++ {
5494		c.Assert(layers1[i], checker.Equals, layers2[i])
5495	}
5496	c.Assert(layers1[len(layers1)-1], checker.Not(checker.Equals), layers2[len(layers1)-1])
5497}
5498
5499func (s *DockerSuite) TestBuildMultiStageCache(c *check.C) {
5500	testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows
5501	dockerfile := `
5502		FROM busybox
5503		ADD baz /
5504		FROM busybox
5505    ADD baz /`
5506	ctx := fakecontext.New(c, "",
5507		fakecontext.WithDockerfile(dockerfile),
5508		fakecontext.WithFiles(map[string]string{
5509			"Dockerfile": dockerfile,
5510			"baz":        "baz",
5511		}))
5512	defer ctx.Close()
5513
5514	result := cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5515	// second part of dockerfile was a repeat of first so should be cached
5516	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1)
5517
5518	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
5519	// now both parts of dockerfile should be cached
5520	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
5521}
5522
5523func (s *DockerSuite) TestBuildNetNone(c *check.C) {
5524	testRequires(c, DaemonIsLinux)
5525	name := "testbuildnetnone"
5526	buildImage(name, cli.WithFlags("--network=none"), build.WithDockerfile(`
5527  FROM busybox
5528  RUN ping -c 1 8.8.8.8
5529  `)).Assert(c, icmd.Expected{
5530		ExitCode: 1,
5531		Out:      "unreachable",
5532	})
5533}
5534
5535func (s *DockerSuite) TestBuildNetContainer(c *check.C) {
5536	testRequires(c, DaemonIsLinux)
5537
5538	id, _ := dockerCmd(c, "run", "--hostname", "foobar", "-d", "busybox", "nc", "-ll", "-p", "1234", "-e", "hostname")
5539
5540	name := "testbuildnetcontainer"
5541	buildImageSuccessfully(c, name, cli.WithFlags("--network=container:"+strings.TrimSpace(id)),
5542		build.WithDockerfile(`
5543  FROM busybox
5544  RUN nc localhost 1234 > /otherhost
5545  `))
5546
5547	host, _ := dockerCmd(c, "run", "testbuildnetcontainer", "cat", "/otherhost")
5548	c.Assert(strings.TrimSpace(host), check.Equals, "foobar")
5549}
5550
5551func (s *DockerSuite) TestBuildWithExtraHost(c *check.C) {
5552	testRequires(c, DaemonIsLinux)
5553
5554	name := "testbuildwithextrahost"
5555	buildImageSuccessfully(c, name,
5556		cli.WithFlags(
5557			"--add-host", "foo:127.0.0.1",
5558			"--add-host", "bar:127.0.0.1",
5559		),
5560		build.WithDockerfile(`
5561  FROM busybox
5562  RUN ping -c 1 foo
5563  RUN ping -c 1 bar
5564  `))
5565}
5566
5567func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *check.C) {
5568	testRequires(c, DaemonIsLinux)
5569	dockerfile := `
5570		FROM busybox
5571		RUN ping -c 1 foo`
5572
5573	testCases := []struct {
5574		testName   string
5575		dockerfile string
5576		buildFlag  string
5577	}{
5578		{"extra_host_missing_ip", dockerfile, "--add-host=foo"},
5579		{"extra_host_missing_ip_with_delimiter", dockerfile, "--add-host=foo:"},
5580		{"extra_host_missing_hostname", dockerfile, "--add-host=:127.0.0.1"},
5581		{"extra_host_invalid_ipv4", dockerfile, "--add-host=foo:101.10.2"},
5582		{"extra_host_invalid_ipv6", dockerfile, "--add-host=foo:2001::1::3F"},
5583	}
5584
5585	for _, tc := range testCases {
5586		result := buildImage(tc.testName, cli.WithFlags(tc.buildFlag), build.WithDockerfile(tc.dockerfile))
5587		result.Assert(c, icmd.Expected{
5588			ExitCode: 125,
5589		})
5590	}
5591
5592}
5593
5594func (s *DockerSuite) TestBuildContChar(c *check.C) {
5595	name := "testbuildcontchar"
5596
5597	buildImage(name, build.WithDockerfile(`FROM busybox\`)).Assert(c, icmd.Expected{
5598		Out: "Step 1/1 : FROM busybox",
5599	})
5600
5601	result := buildImage(name, build.WithDockerfile(`FROM busybox
5602		 RUN echo hi \`))
5603	result.Assert(c, icmd.Success)
5604	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
5605	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi\n")
5606
5607	result = buildImage(name, build.WithDockerfile(`FROM busybox
5608		 RUN echo hi \\`))
5609	result.Assert(c, icmd.Success)
5610	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
5611	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\n")
5612
5613	result = buildImage(name, build.WithDockerfile(`FROM busybox
5614		 RUN echo hi \\\`))
5615	result.Assert(c, icmd.Success)
5616	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
5617	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\\\n")
5618}
5619
5620func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *check.C) {
5621	dockerfile := `
5622		FROM busybox AS first
5623		COPY foo bar
5624
5625		FROM busybox
5626		%s
5627		COPY baz baz
5628		RUN echo mno > baz/cc
5629
5630		FROM busybox
5631		COPY bar /
5632		COPY --from=1 baz sub/
5633		COPY --from=0 bar baz
5634		COPY --from=first bar bay`
5635
5636	ctx := fakecontext.New(c, "",
5637		fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, "")),
5638		fakecontext.WithFiles(map[string]string{
5639			"foo":    "abc",
5640			"bar":    "def",
5641			"baz/aa": "ghi",
5642			"baz/bb": "jkl",
5643		}))
5644	defer ctx.Close()
5645
5646	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5647
5648	cli.DockerCmd(c, "run", "build1", "cat", "bar").Assert(c, icmd.Expected{Out: "def"})
5649	cli.DockerCmd(c, "run", "build1", "cat", "sub/aa").Assert(c, icmd.Expected{Out: "ghi"})
5650	cli.DockerCmd(c, "run", "build1", "cat", "sub/cc").Assert(c, icmd.Expected{Out: "mno"})
5651	cli.DockerCmd(c, "run", "build1", "cat", "baz").Assert(c, icmd.Expected{Out: "abc"})
5652	cli.DockerCmd(c, "run", "build1", "cat", "bay").Assert(c, icmd.Expected{Out: "abc"})
5653
5654	result := cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
5655
5656	// all commands should be cached
5657	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 7)
5658	c.Assert(getIDByName(c, "build1"), checker.Equals, getIDByName(c, "build2"))
5659
5660	err := ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(fmt.Sprintf(dockerfile, "COPY baz/aa foo")), 0644)
5661	c.Assert(err, checker.IsNil)
5662
5663	// changing file in parent block should not affect last block
5664	result = cli.BuildCmd(c, "build3", build.WithExternalBuildContext(ctx))
5665	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5)
5666
5667	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("pqr"), 0644)
5668	c.Assert(err, checker.IsNil)
5669
5670	// changing file in parent block should affect both first and last block
5671	result = cli.BuildCmd(c, "build4", build.WithExternalBuildContext(ctx))
5672	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5)
5673
5674	cli.DockerCmd(c, "run", "build4", "cat", "bay").Assert(c, icmd.Expected{Out: "pqr"})
5675	cli.DockerCmd(c, "run", "build4", "cat", "baz").Assert(c, icmd.Expected{Out: "pqr"})
5676}
5677
5678func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *check.C) {
5679	testCases := []struct {
5680		dockerfile    string
5681		expectedError string
5682	}{
5683		{
5684			dockerfile: `
5685		FROM busybox
5686		COPY --from=foo foo bar`,
5687			expectedError: "invalid from flag value foo",
5688		},
5689		{
5690			dockerfile: `
5691		FROM busybox
5692		COPY --from=0 foo bar`,
5693			expectedError: "invalid from flag value 0: refers to current build stage",
5694		},
5695		{
5696			dockerfile: `
5697		FROM busybox AS foo
5698		COPY --from=bar foo bar`,
5699			expectedError: "invalid from flag value bar",
5700		},
5701		{
5702			dockerfile: `
5703		FROM busybox AS 1
5704		COPY --from=1 foo bar`,
5705			expectedError: "invalid name for build stage",
5706		},
5707	}
5708
5709	for _, tc := range testCases {
5710		ctx := fakecontext.New(c, "",
5711			fakecontext.WithDockerfile(tc.dockerfile),
5712			fakecontext.WithFiles(map[string]string{
5713				"foo": "abc",
5714			}))
5715
5716		cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx)).Assert(c, icmd.Expected{
5717			ExitCode: 1,
5718			Err:      tc.expectedError,
5719		})
5720
5721		ctx.Close()
5722	}
5723}
5724
5725func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *check.C) {
5726	dockerfile := `
5727		FROM busybox
5728		COPY foo bar`
5729	ctx := fakecontext.New(c, "",
5730		fakecontext.WithDockerfile(dockerfile),
5731		fakecontext.WithFiles(map[string]string{
5732			"foo": "abc",
5733		}))
5734	defer ctx.Close()
5735
5736	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5737
5738	dockerfile = `
5739		FROM build1:latest AS foo
5740    FROM busybox
5741		COPY --from=foo bar /
5742		COPY foo /`
5743	ctx = fakecontext.New(c, "",
5744		fakecontext.WithDockerfile(dockerfile),
5745		fakecontext.WithFiles(map[string]string{
5746			"foo": "def",
5747		}))
5748	defer ctx.Close()
5749
5750	cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
5751
5752	out := cli.DockerCmd(c, "run", "build2", "cat", "bar").Combined()
5753	c.Assert(strings.TrimSpace(out), check.Equals, "abc")
5754	out = cli.DockerCmd(c, "run", "build2", "cat", "foo").Combined()
5755	c.Assert(strings.TrimSpace(out), check.Equals, "def")
5756}
5757
5758func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *check.C) {
5759	dockerfile := `
5760		FROM busybox
5761		COPY --from=busybox /etc/passwd /mypasswd
5762		RUN cmp /etc/passwd /mypasswd`
5763
5764	if DaemonIsWindows() {
5765		dockerfile = `
5766			FROM busybox
5767			COPY --from=busybox License.txt foo`
5768	}
5769
5770	ctx := fakecontext.New(c, "",
5771		fakecontext.WithDockerfile(dockerfile),
5772	)
5773	defer ctx.Close()
5774
5775	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5776
5777	if DaemonIsWindows() {
5778		out := cli.DockerCmd(c, "run", "build1", "cat", "License.txt").Combined()
5779		c.Assert(len(out), checker.GreaterThan, 10)
5780		out2 := cli.DockerCmd(c, "run", "build1", "cat", "foo").Combined()
5781		c.Assert(out, check.Equals, out2)
5782	}
5783}
5784
5785func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *check.C) {
5786	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
5787
5788	dockerfile := `
5789		FROM busybox
5790		COPY foo bar`
5791	ctx := fakecontext.New(c, "",
5792		fakecontext.WithDockerfile(dockerfile),
5793		fakecontext.WithFiles(map[string]string{
5794			"foo": "abc",
5795		}))
5796	defer ctx.Close()
5797
5798	cli.BuildCmd(c, repoName, build.WithExternalBuildContext(ctx))
5799
5800	cli.DockerCmd(c, "push", repoName)
5801	cli.DockerCmd(c, "rmi", repoName)
5802
5803	dockerfile = `
5804		FROM busybox
5805		COPY --from=%s bar baz`
5806
5807	ctx = fakecontext.New(c, "", fakecontext.WithDockerfile(fmt.Sprintf(dockerfile, repoName)))
5808	defer ctx.Close()
5809
5810	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5811
5812	cli.Docker(cli.Args("run", "build1", "cat", "baz")).Assert(c, icmd.Expected{Out: "abc"})
5813}
5814
5815func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) {
5816	dockerfile := `
5817		FROM busybox as foo
5818		COPY foo /
5819		FROM foo as foo1
5820		RUN echo 1 >> foo
5821		FROM foo as foO2
5822		RUN echo 2 >> foo
5823		FROM foo
5824		COPY --from=foo1 foo f1
5825		COPY --from=FOo2 foo f2
5826		` // foo2 case also tests that names are case insensitive
5827	ctx := fakecontext.New(c, "",
5828		fakecontext.WithDockerfile(dockerfile),
5829		fakecontext.WithFiles(map[string]string{
5830			"foo": "bar",
5831		}))
5832	defer ctx.Close()
5833
5834	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5835	cli.Docker(cli.Args("run", "build1", "cat", "foo")).Assert(c, icmd.Expected{Out: "bar"})
5836	cli.Docker(cli.Args("run", "build1", "cat", "f1")).Assert(c, icmd.Expected{Out: "bar1"})
5837	cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"})
5838}
5839
5840func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) {
5841	testRequires(c, DaemonIsWindows)
5842	dockerfile := `
5843		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5844		COPY foo c:\\bar`
5845	ctx := fakecontext.New(c, "",
5846		fakecontext.WithDockerfile(dockerfile),
5847		fakecontext.WithFiles(map[string]string{
5848			"foo": "abc",
5849		}))
5850	defer ctx.Close()
5851
5852	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5853
5854	dockerfile = `
5855		FROM build1:latest
5856    	FROM ` + testEnv.PlatformDefaults.BaseImage + `
5857		COPY --from=0 c:\\bar /
5858		COPY foo /`
5859	ctx = fakecontext.New(c, "",
5860		fakecontext.WithDockerfile(dockerfile),
5861		fakecontext.WithFiles(map[string]string{
5862			"foo": "def",
5863		}))
5864	defer ctx.Close()
5865
5866	cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
5867
5868	out := cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\bar").Combined()
5869	c.Assert(strings.TrimSpace(out), check.Equals, "abc")
5870	out = cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\foo").Combined()
5871	c.Assert(strings.TrimSpace(out), check.Equals, "def")
5872}
5873
5874func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) {
5875	testRequires(c, DaemonIsWindows)
5876	dockerfile := `
5877		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5878		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5879		COPY --from=0 %s c:\\oscopy
5880		`
5881	exp := icmd.Expected{
5882		ExitCode: 1,
5883		Err:      "copy from c:\\ or c:\\windows is not allowed on windows",
5884	}
5885	buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\"))).Assert(c, exp)
5886	buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "C:\\\\"))).Assert(c, exp)
5887	buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\windows"))).Assert(c, exp)
5888	buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\wInDows"))).Assert(c, exp)
5889}
5890
5891func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *check.C) {
5892	testRequires(c, DaemonIsWindows)
5893	dockerfile := `
5894		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5895		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5896		COPY --from=0 %s c:\\oscopy
5897		`
5898	exp := icmd.Expected{
5899		ExitCode: 1,
5900		Err:      "copy from c:\\ or c:\\windows is not allowed on windows",
5901	}
5902	buildImage("testforbidsystempaths1", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:"))).Assert(c, exp)
5903	buildImage("testforbidsystempaths2", build.WithDockerfile(fmt.Sprintf(dockerfile, "."))).Assert(c, exp)
5904	buildImage("testforbidsystempaths3", build.WithDockerfile(fmt.Sprintf(dockerfile, "..\\\\"))).Assert(c, exp)
5905	buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, ".\\\\windows"))).Assert(c, exp)
5906	buildImage("testforbidsystempaths5", build.WithDockerfile(fmt.Sprintf(dockerfile, "\\\\windows"))).Assert(c, exp)
5907}
5908
5909func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
5910	testRequires(c, DaemonIsWindows)
5911	dockerfile := `
5912		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5913		COPY foo /
5914		FROM ` + testEnv.PlatformDefaults.BaseImage + `
5915		COPY --from=0 c:\\fOo c:\\copied
5916		RUN type c:\\copied
5917		`
5918	cli.Docker(cli.Build("copyfrom-windows-insensitive"), build.WithBuildContext(c,
5919		build.WithFile("Dockerfile", dockerfile),
5920		build.WithFile("foo", "hello world"),
5921	)).Assert(c, icmd.Expected{
5922		ExitCode: 0,
5923		Out:      "hello world",
5924	})
5925}
5926
5927// #33176
5928func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) {
5929	testRequires(c, DaemonIsLinux)
5930
5931	dockerfile := `
5932		FROM busybox
5933		WORKDIR /foo/bar
5934		FROM scratch
5935		ENV FOO=bar
5936		`
5937	ctx := fakecontext.New(c, "",
5938		fakecontext.WithDockerfile(dockerfile),
5939	)
5940	defer ctx.Close()
5941
5942	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
5943
5944	res := cli.InspectCmd(c, "build1", cli.Format(".Config.WorkingDir")).Combined()
5945	c.Assert(strings.TrimSpace(res), checker.Equals, "")
5946}
5947
5948func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) {
5949	//todo: need to be removed after 18.06 release
5950	if strings.Contains(testEnv.DaemonInfo.ServerVersion, "18.05.0") {
5951		c.Skip(fmt.Sprintf("Bug fixed in 18.06 or higher.Skipping it for %s", testEnv.DaemonInfo.ServerVersion))
5952	}
5953	dockerfile := `
5954		FROM busybox AS build-env
5955		CMD ["/dev"]
5956		FROM busybox
5957		CMD ["/dist"]
5958		`
5959	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile))
5960	defer ctx.Close()
5961
5962	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx),
5963		cli.WithFlags("--target", "build-env"))
5964
5965	res := cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined()
5966	c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`)
5967
5968	// Stage name is case-insensitive by design
5969	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx),
5970		cli.WithFlags("--target", "BUIld-EnV"))
5971
5972	res = cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined()
5973	c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`)
5974
5975	result := cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx),
5976		cli.WithFlags("--target", "nosuchtarget"))
5977	result.Assert(c, icmd.Expected{
5978		ExitCode: 1,
5979		Err:      "failed to reach build target",
5980	})
5981}
5982
5983// TestBuildOpaqueDirectory tests that a build succeeds which
5984// creates opaque directories.
5985// See https://github.com/docker/docker/issues/25244
5986func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
5987	testRequires(c, DaemonIsLinux)
5988	dockerFile := `
5989		FROM busybox
5990		RUN mkdir /dir1 && touch /dir1/f1
5991		RUN rm -rf /dir1 && mkdir /dir1 && touch /dir1/f2
5992		RUN touch /dir1/f3
5993		RUN [ -f /dir1/f2 ]
5994		`
5995	// Test that build succeeds, last command fails if opaque directory
5996	// was not handled correctly
5997	buildImageSuccessfully(c, "testopaquedirectory", build.WithDockerfile(dockerFile))
5998}
5999
6000// Windows test for USER in dockerfile
6001func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
6002	testRequires(c, DaemonIsWindows)
6003	name := "testbuildwindowsuser"
6004	buildImage(name, build.WithDockerfile(`FROM `+testEnv.PlatformDefaults.BaseImage+`
6005		RUN net user user /add
6006		USER user
6007		RUN set username
6008		`)).Assert(c, icmd.Expected{
6009		Out: "USERNAME=user",
6010	})
6011}
6012
6013// Verifies if COPY file . when WORKDIR is set to a non-existing directory,
6014// the directory is created and the file is copied into the directory,
6015// as opposed to the file being copied as a file with the name of the
6016// directory. Fix for 27545 (found on Windows, but regression good for Linux too).
6017// Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514.
6018func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) {
6019	name := "testbuildcopyfiledotwithworkdir"
6020	buildImageSuccessfully(c, name, build.WithBuildContext(c,
6021		build.WithFile("Dockerfile", `FROM busybox
6022WORKDIR /foo
6023COPY file .
6024RUN ["cat", "/foo/file"]
6025`),
6026		build.WithFile("file", "content"),
6027	))
6028}
6029
6030// Case-insensitive environment variables on Windows
6031func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) {
6032	testRequires(c, DaemonIsWindows)
6033	name := "testbuildwindowsenvcaseinsensitive"
6034	buildImageSuccessfully(c, name, build.WithDockerfile(`
6035		FROM `+testEnv.PlatformDefaults.BaseImage+`
6036		ENV FOO=bar foo=baz
6037  `))
6038	res := inspectFieldJSON(c, name, "Config.Env")
6039	if res != `["foo=baz"]` { // Should not have FOO=bar in it - takes the last one processed. And only one entry as deduped.
6040		c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res)
6041	}
6042}
6043
6044// Test case for 29667
6045func (s *DockerSuite) TestBuildWorkdirImageCmd(c *check.C) {
6046	image := "testworkdirimagecmd"
6047	buildImageSuccessfully(c, image, build.WithDockerfile(`
6048FROM busybox
6049WORKDIR /foo/bar
6050`))
6051	out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
6052	c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`)
6053
6054	image = "testworkdirlabelimagecmd"
6055	buildImageSuccessfully(c, image, build.WithDockerfile(`
6056FROM busybox
6057WORKDIR /foo/bar
6058LABEL a=b
6059`))
6060
6061	out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
6062	c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`)
6063}
6064
6065// Test case for 28902/28909
6066func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) {
6067	testRequires(c, DaemonIsLinux)
6068	name := "testbuildworkdircmd"
6069	dockerFile := `
6070                FROM busybox
6071                WORKDIR /
6072                `
6073	buildImageSuccessfully(c, name, build.WithDockerfile(dockerFile))
6074	result := buildImage(name, build.WithDockerfile(dockerFile))
6075	result.Assert(c, icmd.Success)
6076	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1)
6077}
6078
6079// FIXME(vdemeester) should be a unit test
6080func (s *DockerSuite) TestBuildLineErrorOnBuild(c *check.C) {
6081	name := "test_build_line_error_onbuild"
6082	buildImage(name, build.WithDockerfile(`FROM busybox
6083  ONBUILD
6084  `)).Assert(c, icmd.Expected{
6085		ExitCode: 1,
6086		Err:      "Dockerfile parse error line 2: ONBUILD requires at least one argument",
6087	})
6088}
6089
6090// FIXME(vdemeester) should be a unit test
6091func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *check.C) {
6092	name := "test_build_line_error_unknown_instruction"
6093	cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox
6094  RUN echo hello world
6095  NOINSTRUCTION echo ba
6096  RUN echo hello
6097  ERROR
6098  `)).Assert(c, icmd.Expected{
6099		ExitCode: 1,
6100		Err:      "Dockerfile parse error line 3: unknown instruction: NOINSTRUCTION",
6101	})
6102}
6103
6104// FIXME(vdemeester) should be a unit test
6105func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) {
6106	name := "test_build_line_error_with_empty_lines"
6107	cli.Docker(cli.Build(name), build.WithDockerfile(`
6108  FROM busybox
6109
6110  RUN echo hello world
6111
6112  NOINSTRUCTION echo ba
6113
6114  CMD ["/bin/init"]
6115  `)).Assert(c, icmd.Expected{
6116		ExitCode: 1,
6117		Err:      "Dockerfile parse error line 6: unknown instruction: NOINSTRUCTION",
6118	})
6119}
6120
6121// FIXME(vdemeester) should be a unit test
6122func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) {
6123	name := "test_build_line_error_with_comments"
6124	cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox
6125  # This will print hello world
6126  # and then ba
6127  RUN echo hello world
6128  NOINSTRUCTION echo ba
6129  `)).Assert(c, icmd.Expected{
6130		ExitCode: 1,
6131		Err:      "Dockerfile parse error line 5: unknown instruction: NOINSTRUCTION",
6132	})
6133}
6134
6135// #31957
6136func (s *DockerSuite) TestBuildSetCommandWithDefinedShell(c *check.C) {
6137	buildImageSuccessfully(c, "build1", build.WithDockerfile(`
6138FROM busybox
6139SHELL ["/bin/sh", "-c"]
6140`))
6141	buildImageSuccessfully(c, "build2", build.WithDockerfile(`
6142FROM build1
6143CMD echo foo
6144`))
6145
6146	out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", "build2")
6147	c.Assert(strings.TrimSpace(out), checker.Equals, `["/bin/sh","-c","echo foo"]`)
6148}
6149
6150// FIXME(vdemeester) should migrate to docker/cli tests
6151func (s *DockerSuite) TestBuildIidFile(c *check.C) {
6152	tmpDir, err := ioutil.TempDir("", "TestBuildIidFile")
6153	if err != nil {
6154		c.Fatal(err)
6155	}
6156	defer os.RemoveAll(tmpDir)
6157	tmpIidFile := filepath.Join(tmpDir, "iid")
6158
6159	name := "testbuildiidfile"
6160	// Use a Dockerfile with multiple stages to ensure we get the last one
6161	cli.BuildCmd(c, name,
6162		build.WithDockerfile(`FROM `+minimalBaseImage()+` AS stage1
6163ENV FOO FOO
6164FROM `+minimalBaseImage()+`
6165ENV BAR BAZ`),
6166		cli.WithFlags("--iidfile", tmpIidFile))
6167
6168	id, err := ioutil.ReadFile(tmpIidFile)
6169	c.Assert(err, check.IsNil)
6170	d, err := digest.Parse(string(id))
6171	c.Assert(err, check.IsNil)
6172	c.Assert(d.String(), checker.Equals, getIDByName(c, name))
6173}
6174
6175// FIXME(vdemeester) should migrate to docker/cli tests
6176func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *check.C) {
6177	tmpDir, err := ioutil.TempDir("", "TestBuildIidFileCleanupOnFail")
6178	if err != nil {
6179		c.Fatal(err)
6180	}
6181	defer os.RemoveAll(tmpDir)
6182	tmpIidFile := filepath.Join(tmpDir, "iid")
6183
6184	err = ioutil.WriteFile(tmpIidFile, []byte("Dummy"), 0666)
6185	c.Assert(err, check.IsNil)
6186
6187	cli.Docker(cli.Build("testbuildiidfilecleanuponfail"),
6188		build.WithDockerfile(`FROM `+minimalBaseImage()+`
6189	RUN /non/existing/command`),
6190		cli.WithFlags("--iidfile", tmpIidFile)).Assert(c, icmd.Expected{
6191		ExitCode: 1,
6192	})
6193	_, err = os.Stat(tmpIidFile)
6194	c.Assert(err, check.NotNil)
6195	c.Assert(os.IsNotExist(err), check.Equals, true)
6196}
6197