Skip to content

Commit

Permalink
Merge pull request #943 from shortstories/bugfix/copy-with-symlink
Browse files Browse the repository at this point in the history
Fix #942 COPY or ADD to symlink destination breaks image
  • Loading branch information
cvgw authored Jan 21, 2020
2 parents 00a01f9 + df767bb commit b9b61e2
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 36 deletions.
61 changes: 41 additions & 20 deletions pkg/commands/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/GoogleContainerTools/kaniko/pkg/constants"

"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
"github.com/GoogleContainerTools/kaniko/pkg/util"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -59,11 +59,15 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
if err != nil {
return err
}
if fi.IsDir() && !strings.HasSuffix(fullPath, string(os.PathSeparator)) {
fullPath += "/"
}
cwd := config.WorkingDir
if cwd == "" {
cwd = constants.RootDir
}
destPath, err := util.DestinationFilepath(src, dest, cwd)

destPath, err := util.DestinationFilepath(fullPath, dest, cwd)
if err != nil {
return err
}
Expand All @@ -76,11 +80,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
}

if fi.IsDir() {
if !filepath.IsAbs(dest) {
// we need to add '/' to the end to indicate the destination is a directory
dest = filepath.Join(cwd, dest) + "/"
}
copiedFiles, err := util.CopyDir(fullPath, dest, c.buildcontext)
copiedFiles, err := util.CopyDir(fullPath, destPath, c.buildcontext)
if err != nil {
return err
}
Expand Down Expand Up @@ -197,21 +197,42 @@ func (cr *CachingCopyCommand) From() string {
}

func resolveIfSymlink(destPath string) (string, error) {
baseDir := filepath.Dir(destPath)
if info, err := os.Lstat(baseDir); err == nil {
switch mode := info.Mode(); {
case mode&os.ModeSymlink != 0:
linkPath, err := os.Readlink(baseDir)
if err != nil {
return "", errors.Wrap(err, "error reading symlink")
if !filepath.IsAbs(destPath) {
return "", errors.New("dest path must be abs")
}

var nonexistentPaths []string

newPath := destPath
for newPath != "/" {
_, err := os.Lstat(newPath)
if err != nil {
if os.IsNotExist(err) {
dir, file := filepath.Split(newPath)
newPath = filepath.Clean(dir)
nonexistentPaths = append(nonexistentPaths, file)
continue
} else {
return "", errors.Wrap(err, "failed to lstat")
}
absLinkPath := filepath.Join(filepath.Dir(baseDir), linkPath)
newPath := filepath.Join(absLinkPath, filepath.Base(destPath))
logrus.Tracef("Updating destination path from %v to %v due to symlink", destPath, newPath)
return newPath, nil
}

newPath, err = filepath.EvalSymlinks(newPath)
if err != nil {
return "", errors.Wrap(err, "failed to eval symlinks")
}
break
}
return destPath, nil

for i := len(nonexistentPaths) - 1; i >= 0; i-- {
newPath = filepath.Join(newPath, nonexistentPaths[i])
}

if destPath != newPath {
logrus.Tracef("Updating destination path from %v to %v due to symlink", destPath, newPath)
}

return filepath.Clean(newPath), nil
}

func copyCmdFilesUsedFromContext(
Expand Down
20 changes: 17 additions & 3 deletions pkg/commands/copy_test.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,28 @@ func Test_resolveIfSymlink(t *testing.T) {
if err != nil {
t.Error(err)
}
cases := []testCase{{destPath: thepath, expectedPath: thepath, err: nil}}

cases := []testCase{
{destPath: thepath, expectedPath: thepath, err: nil},
{destPath: "/", expectedPath: "/", err: nil},
}
baseDir = tmpDir
symLink := filepath.Join(baseDir, "symlink")
if err := os.Symlink(filepath.Base(thepath), symLink); err != nil {
t.Error(err)
}
cases = append(cases, testCase{filepath.Join(symLink, "foo.txt"), filepath.Join(thepath, "foo.txt"), nil})
cases = append(cases,
testCase{filepath.Join(symLink, "foo.txt"), filepath.Join(thepath, "foo.txt"), nil},
testCase{filepath.Join(symLink, "inner", "foo.txt"), filepath.Join(thepath, "inner", "foo.txt"), nil},
)

absSymlink := filepath.Join(tmpDir, "abs-symlink")
if err := os.Symlink(thepath, absSymlink); err != nil {
t.Error(err)
}
cases = append(cases,
testCase{filepath.Join(absSymlink, "foo.txt"), filepath.Join(thepath, "foo.txt"), nil},
testCase{filepath.Join(absSymlink, "inner", "foo.txt"), filepath.Join(thepath, "inner", "foo.txt"), nil},
)

for i, c := range cases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
Expand Down
25 changes: 15 additions & 10 deletions pkg/util/command_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,20 +164,25 @@ func IsDestDir(path string) bool {
// If dest is a dir, copy it to /dest/relpath
// If dest is a file, copy directly to dest
// If source is a dir:
// Assume dest is also a dir, and copy to dest/relpath
// Assume dest is also a dir, and copy to dest/
// If dest is not an absolute filepath, add /cwd to the beginning
func DestinationFilepath(src, dest, cwd string) (string, error) {
if IsDestDir(dest) {
destPath := filepath.Join(dest, filepath.Base(src))
if filepath.IsAbs(dest) {
return destPath, nil
}
return filepath.Join(cwd, destPath), nil
_, srcFileName := filepath.Split(src)
newDest := dest

if IsDestDir(newDest) {
newDest = filepath.Join(newDest, srcFileName)
}
if filepath.IsAbs(dest) {
return dest, nil

if !filepath.IsAbs(newDest) {
newDest = filepath.Join(cwd, newDest)
}
return filepath.Join(cwd, dest), nil

if len(srcFileName) <= 0 && !strings.HasSuffix(newDest, "/") {
newDest += "/"
}

return newDest, nil
}

// URLDestinationFilepath gives the destination a file from a remote URL should be saved to
Expand Down
6 changes: 3 additions & 3 deletions pkg/util/command_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,13 @@ var destinationFilepathTests = []struct {
src: "context/bar/",
cwd: "/",
dest: "pkg/",
expectedFilepath: "/pkg/bar",
expectedFilepath: "/pkg/",
},
{
src: "context/bar/",
cwd: "/newdir",
dest: "pkg/",
expectedFilepath: "/newdir/pkg/bar",
expectedFilepath: "/newdir/pkg/",
},
{
src: "./context/empty",
Expand All @@ -177,7 +177,7 @@ var destinationFilepathTests = []struct {
src: "./",
cwd: "/",
dest: "/dir",
expectedFilepath: "/dir",
expectedFilepath: "/dir/",
},
{
src: "context/foo",
Expand Down

0 comments on commit b9b61e2

Please sign in to comment.