diff --git a/integration/dockerfiles/Dockerfile_test_copy_symlink b/integration/dockerfiles/Dockerfile_test_copy_symlink index 82f5774c6..2f721ae04 100644 --- a/integration/dockerfiles/Dockerfile_test_copy_symlink +++ b/integration/dockerfiles/Dockerfile_test_copy_symlink @@ -1,14 +1,10 @@ FROM busybox as t -RUN echo "hello" > /tmp/target -RUN ln -s /tmp/target /tmp/link - -## Relative link -RUN cd tmp && ln -s target relative_link - +RUN mkdir temp +RUN echo "hello" > temp/target +RUN ln -s target temp/link ## Relative link with paths -RUN mkdir workdir && cd workdir && ln -s ../tmp/target relative_dir_link +RUN mkdir workdir && cd workdir && ln -s ../temp/target relative_link FROM scratch -COPY --from=t /tmp/link /tmp/ -COPY --from=t /tmp/relative_link /tmp/ -COPY --from=t /workdir/relative_dir_link /workdir/ \ No newline at end of file +COPY --from=t temp/ dest/ +COPY --from=t /workdir/relative_link /workdirAnother/ \ No newline at end of file diff --git a/pkg/commands/add.go b/pkg/commands/add.go index 3ade7ea69..57b6a08f2 100644 --- a/pkg/commands/add.go +++ b/pkg/commands/add.go @@ -21,6 +21,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/pkg/errors" "github.com/GoogleContainerTools/kaniko/pkg/dockerfile" @@ -66,18 +67,18 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui } logrus.Infof("Adding remote URL %s to %s", src, urlDest) if err := util.DownloadFileToDest(src, urlDest); err != nil { - return err + return errors.Wrap(err, "downloading remote source file") } a.snapshotFiles = append(a.snapshotFiles, urlDest) } else if util.IsFileLocalTarArchive(fullPath) { tarDest, err := util.DestinationFilepath("", dest, config.WorkingDir) if err != nil { - return err + return errors.Wrap(err, "determining dest for tar") } logrus.Infof("Unpacking local tar archive %s to %s", src, tarDest) extractedFiles, err := util.UnpackLocalTarArchive(fullPath, tarDest) if err != nil { - return err + return errors.Wrap(err, "unpacking local tar") } logrus.Debugf("Added %v from local tar archive %s", extractedFiles, src) a.snapshotFiles = append(a.snapshotFiles, extractedFiles...) @@ -98,7 +99,7 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui } if err := copyCmd.ExecuteCommand(config, buildArgs); err != nil { - return err + return errors.Wrap(err, "executing copy command") } a.snapshotFiles = append(a.snapshotFiles, copyCmd.snapshotFiles...) return nil diff --git a/pkg/commands/copy.go b/pkg/commands/copy.go index c4102ef81..fb084e208 100644 --- a/pkg/commands/copy.go +++ b/pkg/commands/copy.go @@ -54,17 +54,18 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu uid, gid, err := getUserGroup(c.cmd.Chown, replacementEnvs) if err != nil { - return err + return errors.Wrap(err, "getting user group from chowm") } srcs, dest, err := util.ResolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs) if err != nil { - return err + return errors.Wrap(err, "resolving src") } // For each source, iterate through and copy it over for _, src := range srcs { fullPath := filepath.Join(c.buildcontext, src) + fi, err := os.Lstat(fullPath) if err != nil { return errors.Wrap(err, "could not copy source") @@ -79,27 +80,27 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu destPath, err := util.DestinationFilepath(fullPath, dest, cwd) if err != nil { - return err + return errors.Wrap(err, "find destination path") } // If the destination dir is a symlink we need to resolve the path and use // that instead of the symlink path destPath, err = resolveIfSymlink(destPath) if err != nil { - return err + return errors.Wrap(err, "resolving dest symlink") } if fi.IsDir() { copiedFiles, err := util.CopyDir(fullPath, destPath, c.buildcontext, uid, gid) if err != nil { - return err + return errors.Wrap(err, "copying dir") } c.snapshotFiles = append(c.snapshotFiles, copiedFiles...) } else if util.IsSymlink(fi) { // If file is a symlink, we want to copy the target file to destPath - exclude, err := util.CopySymlink(fullPath, destPath, c.buildcontext, uid, gid) + exclude, err := util.CopySymlink(fullPath, destPath, c.buildcontext) if err != nil { - return err + return errors.Wrap(err, "copying symlink") } if exclude { continue @@ -109,7 +110,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu // ... Else, we want to copy over a file exclude, err := util.CopyFile(fullPath, destPath, c.buildcontext, uid, gid) if err != nil { - return err + return errors.Wrap(err, "copying file") } if exclude { continue diff --git a/pkg/commands/copy_test.go b/pkg/commands/copy_test.go index c69d007db..b57fda9c5 100755 --- a/pkg/commands/copy_test.go +++ b/pkg/commands/copy_test.go @@ -442,3 +442,449 @@ func TestGetUserGroup(t *testing.T) { }) } } + +func TestCopyCommand_ExecuteCommand_Extended(t *testing.T) { + setupDirs := func(t *testing.T) (string, string) { + testDir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + + dir := filepath.Join(testDir, "bar") + + if err := os.MkdirAll(dir, 0777); err != nil { + t.Fatal(err) + } + + file := filepath.Join(dir, "bam.txt") + + if err := ioutil.WriteFile(file, []byte("meow"), 0777); err != nil { + t.Fatal(err) + } + targetPath := filepath.Join(dir, "dam.txt") + if err := ioutil.WriteFile(targetPath, []byte("woof"), 0777); err != nil { + t.Fatal(err) + } + if err := os.Symlink("dam.txt", filepath.Join(dir, "sym.link")); err != nil { + t.Fatal(err) + } + return testDir, filepath.Base(dir) + } + + t.Run("copy dir to another dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + expected, err := ioutil.ReadDir(filepath.Join(testDir, srcDir)) + if err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{srcDir, "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err = cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + if err != nil { + t.Fatal(err) + } + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with contents of srcDir + actual, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + for i, f := range actual { + testutil.CheckDeepEqual(t, expected[i].Name(), f.Name()) + testutil.CheckDeepEqual(t, expected[i].Mode(), f.Mode()) + } + }) + + t.Run("copy file to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "bam.txt"), "dest/"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with file bam.txt + files, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, 1, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "bam.txt") + }) + + t.Run("copy file to a filepath", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "bam.txt"), "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if bam.txt is copied to dest file + if _, err := os.Lstat(filepath.Join(testDir, "dest")); err != nil { + t.Fatal(err) + } + }) + t.Run("copy file to a dir without trailing /", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + + destDir := filepath.Join(testDir, "dest") + if err := os.MkdirAll(destDir, 0777); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "bam.txt"), "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with file bam.txt + files, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, 1, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "bam.txt") + + }) + t.Run("copy symlink file to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "sym.link"), "dest/"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with link sym.link + files, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + // bam.txt and sym.link should be present + testutil.CheckDeepEqual(t, 1, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "sym.link") + testutil.CheckDeepEqual(t, true, files[0].Mode()&os.ModeSymlink != 0) + linkName, err := os.Readlink(filepath.Join(testDir, "dest", "sym.link")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, "dam.txt") + }) + t.Run("copy deadlink symlink file to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + doesNotExists := filepath.Join(testDir, "dead.txt") + if err := ioutil.WriteFile(doesNotExists, []byte("remove me"), 0777); err != nil { + t.Fatal(err) + } + if err := os.Symlink("../dead.txt", filepath.Join(testDir, srcDir, "dead.link")); err != nil { + t.Fatal(err) + } + if err := os.Remove(doesNotExists); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "dead.link"), "dest/"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with link dead.link + files, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, 1, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "dead.link") + testutil.CheckDeepEqual(t, true, files[0].Mode()&os.ModeSymlink != 0) + linkName, err := os.Readlink(filepath.Join(testDir, "dest", "dead.link")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, "../dead.txt") + }) + + t.Run("copy src symlink dir to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + expected, err := ioutil.ReadDir(filepath.Join(testDir, srcDir)) + if err != nil { + t.Fatal(err) + } + + another := filepath.Join(testDir, "another") + os.Symlink(filepath.Join(testDir, srcDir), another) + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{"another", "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err = cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with contents of srcDir + actual, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + for i, f := range actual { + testutil.CheckDeepEqual(t, expected[i].Name(), f.Name()) + testutil.CheckDeepEqual(t, expected[i].Mode(), f.Mode()) + } + }) + t.Run("copy dir with a symlink to a file outside of current src dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + expected, err := ioutil.ReadDir(filepath.Join(testDir, srcDir)) + if err != nil { + t.Fatal(err) + } + + anotherSrc := filepath.Join(testDir, "anotherSrc") + if err := os.MkdirAll(anotherSrc, 0777); err != nil { + t.Fatal(err) + } + targetPath := filepath.Join(anotherSrc, "target.txt") + if err := ioutil.WriteFile(targetPath, []byte("woof"), 0777); err != nil { + t.Fatal(err) + } + if err := os.Symlink(targetPath, filepath.Join(testDir, srcDir, "zSym.link")); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{srcDir, "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err = cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists contents of srcDir and an extra zSym.link created + // in this test + actual, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, 4, len(actual)) + for i, f := range expected { + testutil.CheckDeepEqual(t, f.Name(), actual[i].Name()) + testutil.CheckDeepEqual(t, f.Mode(), actual[i].Mode()) + } + linkName, err := os.Readlink(filepath.Join(testDir, "dest", "zSym.link")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, targetPath) + }) + t.Run("copy src symlink dir to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + expected, err := ioutil.ReadDir(filepath.Join(testDir, srcDir)) + if err != nil { + t.Fatal(err) + } + + another := filepath.Join(testDir, "another") + os.Symlink(filepath.Join(testDir, srcDir), another) + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{"another", "dest"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err = cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with bam.txt and "dest" dir is a symlink + actual, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + for i, f := range actual { + testutil.CheckDeepEqual(t, expected[i].Name(), f.Name()) + testutil.CheckDeepEqual(t, expected[i].Mode(), f.Mode()) + } + }) + t.Run("copy src dir to a dest dir which is a symlink", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + expected, err := ioutil.ReadDir(filepath.Join(testDir, srcDir)) + if err != nil { + t.Fatal(err) + } + + dest := filepath.Join(testDir, "dest") + if err := os.MkdirAll(dest, 0777); err != nil { + t.Fatal(err) + } + linkedDest := filepath.Join(testDir, "linkDest") + if err := os.Symlink(dest, linkedDest); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{srcDir, linkedDest}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err = cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "linkdest" dir exists with contents of srcDir + actual, err := ioutil.ReadDir(filepath.Join(testDir, "linkDest")) + if err != nil { + t.Fatal(err) + } + for i, f := range expected { + testutil.CheckDeepEqual(t, f.Name(), actual[i].Name()) + testutil.CheckDeepEqual(t, f.Mode(), actual[i].Mode()) + } + // Check if linkDest -> dest + linkName, err := os.Readlink(filepath.Join(testDir, "linkDest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, dest) + }) + t.Run("copy src file to a dest dir which is a symlink", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + + dest := filepath.Join(testDir, "dest") + if err := os.MkdirAll(dest, 0777); err != nil { + t.Fatal(err) + } + linkedDest := filepath.Join(testDir, "linkDest") + if err := os.Symlink(dest, linkedDest); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{fmt.Sprintf("%s/bam.txt", srcDir), linkedDest}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "linkDest" link is same. + actual, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, "bam.txt", actual[0].Name()) + c, err := ioutil.ReadFile(filepath.Join(testDir, "dest", "bam.txt")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, "meow", string(c)) + // Check if linkDest -> dest + linkName, err := os.Readlink(filepath.Join(testDir, "linkDest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, dest) + }) +} diff --git a/pkg/executor/build.go b/pkg/executor/build.go index dd7021a77..cf7e8bfbf 100644 --- a/pkg/executor/build.go +++ b/pkg/executor/build.go @@ -339,7 +339,7 @@ func (s *stageBuilder) build() error { v := command.(commands.Cached) layer := v.Layer() if err := s.saveLayerToImage(layer, command.String()); err != nil { - return err + return errors.Wrap(err, "failed to save layer") } } else { tarPath, err := s.takeSnapshot(files) diff --git a/pkg/filesystem/resolve.go b/pkg/filesystem/resolve.go index 63beff748..a753e8776 100644 --- a/pkg/filesystem/resolve.go +++ b/pkg/filesystem/resolve.go @@ -35,6 +35,7 @@ import ( // * Add all ancestors of each path to the output set. func ResolvePaths(paths []string, wl []util.WhitelistEntry) (pathsToAdd []string, err error) { logrus.Info("Resolving paths") + logrus.Debugf("Resolving paths %s", paths) fileSet := make(map[string]bool) @@ -47,8 +48,7 @@ func ResolvePaths(paths []string, wl []util.WhitelistEntry) (pathsToAdd []string link, e := resolveSymlinkAncestor(f) if e != nil { - err = e - return + continue } if f != link { @@ -64,9 +64,9 @@ func ResolvePaths(paths []string, wl []util.WhitelistEntry) (pathsToAdd []string // If the path is a symlink we need to also consider the target of that // link - evaled, err = filepath.EvalSymlinks(f) - if err != nil { - if !os.IsNotExist(err) { + evaled, e = filepath.EvalSymlinks(f) + if e != nil { + if !os.IsNotExist(e) { logrus.Errorf("couldn't eval %s with link %s", f, link) return } @@ -133,7 +133,7 @@ loop: for newPath != "/" { fi, err := os.Lstat(newPath) if err != nil { - return "", errors.Wrap(err, "failed to lstat") + return "", errors.Wrap(err, "resolvePaths: failed to lstat") } if util.IsSymlink(fi) { diff --git a/pkg/snapshot/snapshot.go b/pkg/snapshot/snapshot.go index 143f72ba8..e64e28dd3 100644 --- a/pkg/snapshot/snapshot.go +++ b/pkg/snapshot/snapshot.go @@ -26,7 +26,6 @@ import ( "github.com/GoogleContainerTools/kaniko/pkg/filesystem" "github.com/GoogleContainerTools/kaniko/pkg/timing" - "github.com/karrick/godirwalk" "github.com/GoogleContainerTools/kaniko/pkg/constants" diff --git a/pkg/util/.editorconfig b/pkg/util/.editorconfig new file mode 100644 index 000000000..c5319e7de --- /dev/null +++ b/pkg/util/.editorconfig @@ -0,0 +1,3 @@ +root = true + +[*] diff --git a/pkg/util/command_util.go b/pkg/util/command_util.go index 23f035ec4..2880c167d 100644 --- a/pkg/util/command_util.go +++ b/pkg/util/command_util.go @@ -35,6 +35,10 @@ import ( "github.com/sirupsen/logrus" ) +const ( + pathSeparator = "/" +) + // ResolveEnvironmentReplacementList resolves a list of values by calling resolveEnvironmentReplacement func ResolveEnvironmentReplacementList(values, envs []string, isFilepath bool) ([]string, error) { var resolvedValues []string @@ -114,13 +118,14 @@ func ResolveSources(srcs []string, root string) ([]string, error) { logrus.Infof("Resolving srcs %v...", srcs) files, err := RelativeFiles("", root) if err != nil { - return nil, err + return nil, errors.Wrap(err, "resolving sources") } resolved, err := matchSources(srcs, files) if err != nil { - return nil, err + return nil, errors.Wrap(err, "matching sources") } logrus.Debugf("Resolved sources to %v", resolved) + fmt.Println("end of resolve sources") return resolved, nil } @@ -154,7 +159,7 @@ func IsDestDir(path string) bool { fileInfo, err := os.Stat(path) if err != nil { // fall back to string-based determination - return strings.HasSuffix(path, "/") || path == "." + return strings.HasSuffix(path, pathSeparator) || path == "." } // if it's a real path, check the fs response return fileInfo.IsDir() @@ -171,16 +176,19 @@ func DestinationFilepath(src, dest, cwd string) (string, error) { _, srcFileName := filepath.Split(src) newDest := dest + if !filepath.IsAbs(newDest) { + newDest = filepath.Join(cwd, newDest) + // join call clean on all results. + if strings.HasSuffix(dest, pathSeparator) || strings.HasSuffix(dest, ".") { + newDest += pathSeparator + } + } if IsDestDir(newDest) { newDest = filepath.Join(newDest, srcFileName) } - if !filepath.IsAbs(newDest) { - newDest = filepath.Join(cwd, newDest) - } - - if len(srcFileName) <= 0 && !strings.HasSuffix(newDest, "/") { - newDest += "/" + if len(srcFileName) <= 0 && !strings.HasSuffix(newDest, pathSeparator) { + newDest += pathSeparator } return newDest, nil diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index 40c705dd8..457ee886a 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -442,7 +442,7 @@ func DetectFilesystemWhitelist(path string) error { func RelativeFiles(fp string, root string) ([]string, error) { var files []string fullPath := filepath.Join(root, fp) - logrus.Debugf("Getting files and contents at root %s", fullPath) + logrus.Debugf("Getting files and contents at root %s for %s", root, fullPath) err := filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -505,15 +505,16 @@ func FilepathExists(path string) bool { func CreateFile(path string, reader io.Reader, perm os.FileMode, uid uint32, gid uint32) error { // Create directory path if it doesn't exist if err := createParentDirectory(path); err != nil { - return err + return errors.Wrap(err, "creating parent dir") } + dest, err := os.Create(path) if err != nil { - return err + return errors.Wrap(err, "creating file") } defer dest.Close() if _, err := io.Copy(dest, reader); err != nil { - return err + return errors.Wrap(err, "copying file") } return setFilePermissions(path, perm, int(uid), int(gid)) } @@ -570,7 +571,7 @@ func DetermineTargetFileOwnership(fi os.FileInfo, uid, gid int64) (int64, int64) func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) { files, err := RelativeFiles("", src) if err != nil { - return nil, err + return nil, errors.Wrap(err, "copying dir") } var copiedFiles []string for _, file := range files { @@ -594,7 +595,7 @@ func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) { } } else if IsSymlink(fi) { // If file is a symlink, we want to create the same relative symlink - if _, err := CopySymlink(fullPath, destPath, buildcontext, uid, gid); err != nil { + if _, err := CopySymlink(fullPath, destPath, buildcontext); err != nil { return nil, err } } else { @@ -608,16 +609,12 @@ func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) { return copiedFiles, nil } -// CopySymlink copies the symlink at src to dest -func CopySymlink(src, dest, buildcontext string, uid int64, gid int64) (bool, error) { +// CopySymlink copies the symlink at src to dest. +func CopySymlink(src, dest, buildcontext string) (bool, error) { if ExcludeFile(src, buildcontext) { logrus.Debugf("%s found in .dockerignore, ignoring", src) return true, nil } - link, err := filepath.EvalSymlinks(src) - if err != nil { - return false, err - } if FilepathExists(dest) { if err := os.RemoveAll(dest); err != nil { return false, err @@ -626,7 +623,11 @@ func CopySymlink(src, dest, buildcontext string, uid int64, gid int64) (bool, er if err := createParentDirectory(dest); err != nil { return false, err } - return CopyFile(link, dest, buildcontext, uid, gid) + link, err := os.Readlink(src) + if err != nil { + logrus.Debugf("could not read link for %s", src) + } + return false, os.Symlink(link, dest) } // CopyFile copies the file at src to dest @@ -827,15 +828,14 @@ func getSymlink(path string) error { func CopyFileOrSymlink(src string, destDir string) error { destFile := filepath.Join(destDir, src) if fi, _ := os.Lstat(src); IsSymlink(fi) { - link, err := EvalSymLink(src) + link, err := os.Readlink(src) if err != nil { return err } - linkPath := filepath.Join(destDir, link) if err := createParentDirectory(destFile); err != nil { return err } - return os.Symlink(linkPath, destFile) + return os.Symlink(link, destFile) } return otiai10Cpy.Copy(src, destFile) } diff --git a/pkg/util/fs_util_test.go b/pkg/util/fs_util_test.go index b8eb6329e..93638b6d0 100644 --- a/pkg/util/fs_util_test.go +++ b/pkg/util/fs_util_test.go @@ -835,7 +835,7 @@ func TestCopySymlink(t *testing.T) { if err := os.Symlink(tc.linkTarget, link); err != nil { t.Fatal(err) } - if _, err := CopySymlink(link, dest, "", DoNotChangeUID, DoNotChangeGID); err != nil { + if _, err := CopySymlink(link, dest, ""); err != nil { t.Fatal(err) } if _, err := os.Lstat(dest); err != nil { diff --git a/testutil/util.go b/testutil/util.go index a1e72dd08..c983b53e4 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -68,6 +68,12 @@ func CheckError(t *testing.T, shouldErr bool, err error) { } } +func CheckNoError(t *testing.T, err error) { + if err != nil { + t.Error(err) + } +} + func checkErr(shouldErr bool, err error) error { if err == nil && shouldErr { return fmt.Errorf("Expected error, but returned none")