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 |