diff --git a/archive/archive.go b/archive/archive.go index 2fac18e99f..e21b10ca0c 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -418,6 +418,9 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error { // the layer is also a directory. Then we want to merge them (i.e. // just apply the metadata from the layer). if fi, err := os.Lstat(path); err == nil { + if fi.IsDir() && hdr.Name == "." { + continue + } if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { if err := os.RemoveAll(path); err != nil { return err diff --git a/integration-cli/build_tests/TestAdd/DirContentToExistDir/Dockerfile b/integration-cli/build_tests/TestAdd/DirContentToExistDir/Dockerfile new file mode 100644 index 0000000000..6ab0e98f49 --- /dev/null +++ b/integration-cli/build_tests/TestAdd/DirContentToExistDir/Dockerfile @@ -0,0 +1,10 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN mkdir /exists +RUN touch /exists/exists_file +RUN chown -R dockerio.dockerio /exists +ADD test_dir/ /exists/ +RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] +RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] +RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] diff --git a/integration-cli/build_tests/TestAdd/DirContentToExistDir/test_dir/test_file b/integration-cli/build_tests/TestAdd/DirContentToExistDir/test_dir/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/build_tests/TestAdd/DirContentToRoot/Dockerfile b/integration-cli/build_tests/TestAdd/DirContentToRoot/Dockerfile new file mode 100644 index 0000000000..03a9c052fd --- /dev/null +++ b/integration-cli/build_tests/TestAdd/DirContentToRoot/Dockerfile @@ -0,0 +1,8 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN touch /exists +RUN chown dockerio.dockerio exists +ADD test_dir / +RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] diff --git a/integration-cli/build_tests/TestAdd/DirContentToRoot/test_dir/test_file b/integration-cli/build_tests/TestAdd/DirContentToRoot/test_dir/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/build_tests/TestAdd/SingleFileToExistDir/Dockerfile b/integration-cli/build_tests/TestAdd/SingleFileToExistDir/Dockerfile new file mode 100644 index 0000000000..fefbd09f0c --- /dev/null +++ b/integration-cli/build_tests/TestAdd/SingleFileToExistDir/Dockerfile @@ -0,0 +1,10 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN mkdir /exists +RUN touch /exists/exists_file +RUN chown -R dockerio.dockerio /exists +ADD test_file /exists/ +RUN [ $(ls -l / | grep exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] +RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ] diff --git a/integration-cli/build_tests/TestAdd/SingleFileToExistDir/test_file b/integration-cli/build_tests/TestAdd/SingleFileToExistDir/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/build_tests/TestAdd/SingleFileToNonExistDir/Dockerfile b/integration-cli/build_tests/TestAdd/SingleFileToNonExistDir/Dockerfile new file mode 100644 index 0000000000..661990b7f4 --- /dev/null +++ b/integration-cli/build_tests/TestAdd/SingleFileToNonExistDir/Dockerfile @@ -0,0 +1,9 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN touch /exists +RUN chown dockerio.dockerio /exists +ADD test_file /test_dir/ +RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] diff --git a/integration-cli/build_tests/TestAdd/SingleFileToNonExistDir/test_file b/integration-cli/build_tests/TestAdd/SingleFileToNonExistDir/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/build_tests/TestAdd/SingleFileToRoot/Dockerfile b/integration-cli/build_tests/TestAdd/SingleFileToRoot/Dockerfile new file mode 100644 index 0000000000..d6375debe1 --- /dev/null +++ b/integration-cli/build_tests/TestAdd/SingleFileToRoot/Dockerfile @@ -0,0 +1,8 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN touch /exists +RUN chown dockerio.dockerio /exists +ADD test_file / +RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] diff --git a/integration-cli/build_tests/TestAdd/SingleFileToRoot/test_file b/integration-cli/build_tests/TestAdd/SingleFileToRoot/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/build_tests/TestAdd/WholeDirToRoot/Dockerfile b/integration-cli/build_tests/TestAdd/WholeDirToRoot/Dockerfile new file mode 100644 index 0000000000..3db6d3fd95 --- /dev/null +++ b/integration-cli/build_tests/TestAdd/WholeDirToRoot/Dockerfile @@ -0,0 +1,9 @@ +FROM busybox +RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd +RUN echo 'dockerio:x:1001:' >> /etc/group +RUN touch /exists +RUN chown dockerio.dockerio exists +ADD test_dir /test_dir +RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ] diff --git a/integration-cli/build_tests/TestAdd/WholeDirToRoot/test_dir/test_file b/integration-cli/build_tests/TestAdd/WholeDirToRoot/test_dir/test_file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 7cd42dc69c..cd3e06d60c 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -23,6 +23,102 @@ func TestBuildSixtySteps(t *testing.T) { logDone("build - build an image with sixty build steps") } +func TestAddSingleFileToRoot(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToRoot") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add single file to root") +} + +func TestAddSingleFileToExistDir(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToExistDir") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add single file to existing dir") +} + +func TestAddSingleFileToNonExistDir(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "SingleFileToNonExistDir") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add single file to non-existing dir") +} + +func TestAddDirContentToRoot(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToRoot") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add directory contents to root") +} + +func TestAddDirContentToExistDir(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "DirContentToExistDir") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add directory contents to existing dir") +} + +func TestAddWholeDirToRoot(t *testing.T) { + buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestAdd") + buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", "WholeDirToRoot") + buildCmd.Dir = buildDirectory + out, exitCode, err := runCommandWithOutput(buildCmd) + errorOut(err, t, fmt.Sprintf("build failed to complete: %v %v", out, err)) + + if err != nil || exitCode != 0 { + t.Fatal("failed to build the image") + } + + deleteImages("testaddimg") + + logDone("build - add whole directory to root") +} + // TODO: TestCaching // TODO: TestADDCacheInvalidation diff --git a/server/buildfile.go b/server/buildfile.go index 24b0b58f25..253993528b 100644 --- a/server/buildfile.go +++ b/server/buildfile.go @@ -386,9 +386,10 @@ func (b *buildFile) checkPathForAddition(orig string) error { func (b *buildFile) addContext(container *daemon.Container, orig, dest string, remote bool) error { var ( - err error - origPath = path.Join(b.contextPath, orig) - destPath = path.Join(container.RootfsPath(), dest) + err error + destExists = true + origPath = path.Join(b.contextPath, orig) + destPath = path.Join(container.RootfsPath(), dest) ) if destPath != container.RootfsPath() { @@ -402,6 +403,14 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r if strings.HasSuffix(dest, "/") { destPath = destPath + "/" } + destStat, err := os.Stat(destPath) + if err != nil { + if os.IsNotExist(err) { + destExists = false + } else { + return err + } + } fi, err := os.Stat(origPath) if err != nil { if os.IsNotExist(err) { @@ -423,8 +432,20 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r if err := archive.CopyWithTar(origPath, destPath); err != nil { return err } - if err := chownR(destPath, 0, 0); err != nil { - return err + if destExists { + files, err := ioutil.ReadDir(origPath) + if err != nil { + return err + } + for _, file := range files { + if err := chownR(filepath.Join(destPath, file.Name()), 0, 0); err != nil { + return err + } + } + } else { + if err := chownR(destPath, 0, 0); err != nil { + return err + } } return nil } @@ -456,7 +477,12 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, r return err } - if err := chownR(destPath, 0, 0); err != nil { + resPath := destPath + if destExists && destStat.IsDir() { + resPath = path.Join(destPath, path.Base(origPath)) + } + + if err := chownR(resPath, 0, 0); err != nil { return err } return nil