Fixed an issue where the image is broken if there are symlink in the destination path when ADD, COPY

This commit is contained in:
ohchang-kwon 2020-01-03 16:28:12 +09:00
parent eafb80227b
commit d6fe98aa49
4 changed files with 68 additions and 33 deletions

View File

@ -18,14 +18,13 @@ package commands
import (
"fmt"
"os"
"path/filepath"
"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"
"os"
"path/filepath"
"strings"
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
"github.com/GoogleContainerTools/kaniko/pkg/util"
@ -63,6 +62,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
if cwd == "" {
cwd = constants.RootDir
}
destPath, err := util.DestinationFilepath(src, dest, cwd)
if err != nil {
return err
@ -76,11 +76,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
}
@ -197,21 +193,43 @@ func (cr *CachingCopyCommand) From() string {
}
func resolveIfSymlink(destPath string) (string, error) {
baseDir := filepath.Dir(destPath)
if info, err := os.Lstat(baseDir); err == nil {
if !filepath.IsAbs(destPath) {
return "", errors.New("dest path must be abs")
}
pathPrefix := "/"
pathTokens := strings.Split(destPath, string(os.PathSeparator))
for i, pathToken := range pathTokens {
currentPath := filepath.Join(pathPrefix, pathToken)
info, err := os.Lstat(currentPath)
if err != nil {
if os.IsNotExist(err) {
pathPrefix = filepath.Join(append([]string{currentPath}, pathTokens[i+1:]...)...)
break
} else {
return "", errors.Wrap(err, "failed to lstat")
}
}
switch mode := info.Mode(); {
case mode&os.ModeSymlink != 0:
linkPath, err := os.Readlink(baseDir)
linkPath, err := os.Readlink(currentPath)
if err != nil {
return "", errors.Wrap(err, "error reading symlink")
return "", errors.Wrap(err, "failed to read symlink")
}
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
if filepath.IsAbs(linkPath) {
pathPrefix = linkPath
} else {
pathPrefix = filepath.Join(pathPrefix, linkPath)
}
default:
pathPrefix = filepath.Join(pathPrefix, pathToken)
}
}
return destPath, nil
if destPath != pathPrefix {
logrus.Tracef("Updating destination path from %v to %v due to symlink", destPath, pathPrefix)
}
return filepath.Clean(pathPrefix), nil
}
func copyCmdFilesUsedFromContext(

14
pkg/commands/copy_test.go Normal file → Executable file
View File

@ -201,7 +201,19 @@ func Test_resolveIfSymlink(t *testing.T) {
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) {

View File

@ -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

View File

@ -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",
@ -177,7 +177,7 @@ var destinationFilepathTests = []struct {
src: "./",
cwd: "/",
dest: "/dir",
expectedFilepath: "/dir",
expectedFilepath: "/dir/",
},
{
src: "context/foo",