Fixes #5110
Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)
... | ... |
@@ -418,6 +418,9 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error { |
418 | 418 |
// the layer is also a directory. Then we want to merge them (i.e. |
419 | 419 |
// just apply the metadata from the layer). |
420 | 420 |
if fi, err := os.Lstat(path); err == nil { |
421 |
+ if fi.IsDir() && hdr.Name == "." { |
|
422 |
+ continue |
|
423 |
+ } |
|
421 | 424 |
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { |
422 | 425 |
if err := os.RemoveAll(path); err != nil { |
423 | 426 |
return err |
424 | 427 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,10 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN mkdir /exists |
|
4 |
+RUN touch /exists/exists_file |
|
5 |
+RUN chown -R dockerio.dockerio /exists |
|
6 |
+ADD test_dir/ /exists/ |
|
7 |
+RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
|
8 |
+RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
|
9 |
+RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] |
1 | 11 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,8 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN touch /exists |
|
4 |
+RUN chown dockerio.dockerio exists |
|
5 |
+ADD test_dir / |
|
6 |
+RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] |
|
7 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
1 | 9 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,10 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN mkdir /exists |
|
4 |
+RUN touch /exists/exists_file |
|
5 |
+RUN chown -R dockerio.dockerio /exists |
|
6 |
+ADD test_file /exists/ |
|
7 |
+RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
|
8 |
+RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] |
|
9 |
+RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
1 | 11 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,9 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN touch /exists |
|
4 |
+RUN chown dockerio.dockerio /exists |
|
5 |
+ADD test_file /test_dir/ |
|
6 |
+RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] |
|
7 |
+RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] |
|
8 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
1 | 10 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,8 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN touch /exists |
|
4 |
+RUN chown dockerio.dockerio /exists |
|
5 |
+ADD test_file / |
|
6 |
+RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] |
|
7 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
1 | 9 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,9 @@ |
0 |
+FROM busybox |
|
1 |
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
|
2 |
+RUN echo 'dockerio:x:1001:' >> /etc/group |
|
3 |
+RUN touch /exists |
|
4 |
+RUN chown dockerio.dockerio exists |
|
5 |
+ADD test_dir /test_dir |
|
6 |
+RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] |
|
7 |
+RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] |
|
8 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] |
... | ... |
@@ -23,6 +23,102 @@ func TestBuildSixtySteps(t *testing.T) { |
23 | 23 |
logDone("build - build an image with sixty build steps") |
24 | 24 |
} |
25 | 25 |
|
26 |
+func TestAddSingleFileToRoot(t *testing.T) { |
|
27 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
28 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToRoot") |
|
29 |
+ buildCmd.Dir = buildDirectory |
|
30 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
31 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
32 |
+ |
|
33 |
+ if err != nil || exitCode != 0 { |
|
34 |
+ t.Fatal("failed to build the image") |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ deleteImages("testaddimg") |
|
38 |
+ |
|
39 |
+ logDone("build - add single file to root") |
|
40 |
+} |
|
41 |
+ |
|
42 |
+func TestAddSingleFileToExistDir(t *testing.T) { |
|
43 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
44 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToExistDir") |
|
45 |
+ buildCmd.Dir = buildDirectory |
|
46 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
47 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
48 |
+ |
|
49 |
+ if err != nil || exitCode != 0 { |
|
50 |
+ t.Fatal("failed to build the image") |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ deleteImages("testaddimg") |
|
54 |
+ |
|
55 |
+ logDone("build - add single file to existing dir") |
|
56 |
+} |
|
57 |
+ |
|
58 |
+func TestAddSingleFileToNonExistDir(t *testing.T) { |
|
59 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
60 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToNonExistDir") |
|
61 |
+ buildCmd.Dir = buildDirectory |
|
62 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
63 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
64 |
+ |
|
65 |
+ if err != nil || exitCode != 0 { |
|
66 |
+ t.Fatal("failed to build the image") |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ deleteImages("testaddimg") |
|
70 |
+ |
|
71 |
+ logDone("build - add single file to non-existing dir") |
|
72 |
+} |
|
73 |
+ |
|
74 |
+func TestAddDirContentToRoot(t *testing.T) { |
|
75 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
76 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToRoot") |
|
77 |
+ buildCmd.Dir = buildDirectory |
|
78 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
79 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
80 |
+ |
|
81 |
+ if err != nil || exitCode != 0 { |
|
82 |
+ t.Fatal("failed to build the image") |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ deleteImages("testaddimg") |
|
86 |
+ |
|
87 |
+ logDone("build - add directory contents to root") |
|
88 |
+} |
|
89 |
+ |
|
90 |
+func TestAddDirContentToExistDir(t *testing.T) { |
|
91 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
92 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToExistDir") |
|
93 |
+ buildCmd.Dir = buildDirectory |
|
94 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
95 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
96 |
+ |
|
97 |
+ if err != nil || exitCode != 0 { |
|
98 |
+ t.Fatal("failed to build the image") |
|
99 |
+ } |
|
100 |
+ |
|
101 |
+ deleteImages("testaddimg") |
|
102 |
+ |
|
103 |
+ logDone("build - add directory contents to existing dir") |
|
104 |
+} |
|
105 |
+ |
|
106 |
+func TestAddWholeDirToRoot(t *testing.T) { |
|
107 |
+ buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") |
|
108 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "WholeDirToRoot") |
|
109 |
+ buildCmd.Dir = buildDirectory |
|
110 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
111 |
+ errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) |
|
112 |
+ |
|
113 |
+ if err != nil || exitCode != 0 { |
|
114 |
+ t.Fatal("failed to build the image") |
|
115 |
+ } |
|
116 |
+ |
|
117 |
+ deleteImages("testaddimg") |
|
118 |
+ |
|
119 |
+ logDone("build - add whole directory to root") |
|
120 |
+} |
|
121 |
+ |
|
26 | 122 |
// TODO: TestCaching |
27 | 123 |
|
28 | 124 |
// TODO: TestADDCacheInvalidation |
... | ... |
@@ -386,9 +386,10 @@ func (b *buildFile) checkPathForAddition(orig string) error { |
386 | 386 |
|
387 | 387 |
func (b *buildFile) addContext(container *daemon.Container, orig, dest string, remote bool) error { |
388 | 388 |
var ( |
389 |
- err error |
|
390 |
- origPath = path.Join(b.contextPath, orig) |
|
391 |
- destPath = path.Join(container.RootfsPath(), dest) |
|
389 |
+ err error |
|
390 |
+ destExists = true |
|
391 |
+ origPath = path.Join(b.contextPath, orig) |
|
392 |
+ destPath = path.Join(container.RootfsPath(), dest) |
|
392 | 393 |
) |
393 | 394 |
|
394 | 395 |
if destPath != container.RootfsPath() { |
... | ... |
@@ -402,6 +403,14 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r |
402 | 402 |
if strings.HasSuffix(dest, "/") { |
403 | 403 |
destPath = destPath + "/" |
404 | 404 |
} |
405 |
+ destStat, err := os.Stat(destPath) |
|
406 |
+ if err != nil { |
|
407 |
+ if os.IsNotExist(err) { |
|
408 |
+ destExists = false |
|
409 |
+ } else { |
|
410 |
+ return err |
|
411 |
+ } |
|
412 |
+ } |
|
405 | 413 |
fi, err := os.Stat(origPath) |
406 | 414 |
if err != nil { |
407 | 415 |
if os.IsNotExist(err) { |
... | ... |
@@ -423,8 +432,20 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r |
423 | 423 |
if err := archive.CopyWithTar(origPath, destPath); err != nil { |
424 | 424 |
return err |
425 | 425 |
} |
426 |
- if err := chownR(destPath, 0, 0); err != nil { |
|
427 |
- return err |
|
426 |
+ if destExists { |
|
427 |
+ files, err := ioutil.ReadDir(origPath) |
|
428 |
+ if err != nil { |
|
429 |
+ return err |
|
430 |
+ } |
|
431 |
+ for _, file := range files { |
|
432 |
+ if err := chownR(filepath.Join(destPath, file.Name()), 0, 0); err != nil { |
|
433 |
+ return err |
|
434 |
+ } |
|
435 |
+ } |
|
436 |
+ } else { |
|
437 |
+ if err := chownR(destPath, 0, 0); err != nil { |
|
438 |
+ return err |
|
439 |
+ } |
|
428 | 440 |
} |
429 | 441 |
return nil |
430 | 442 |
} |
... | ... |
@@ -456,7 +477,12 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r |
456 | 456 |
return err |
457 | 457 |
} |
458 | 458 |
|
459 |
- if err := chownR(destPath, 0, 0); err != nil { |
|
459 |
+ resPath := destPath |
|
460 |
+ if destExists && destStat.IsDir() { |
|
461 |
+ resPath = path.Join(destPath, path.Base(origPath)) |
|
462 |
+ } |
|
463 |
+ |
|
464 |
+ if err := chownR(resPath, 0, 0); err != nil { |
|
460 | 465 |
return err |
461 | 466 |
} |
462 | 467 |
return nil |