mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 04:18:57 +02:00
Merge branch 'master' into registry-mirror
This commit is contained in:
+56
-26
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -76,16 +80,12 @@ 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
|
||||
}
|
||||
c.snapshotFiles = append(c.snapshotFiles, copiedFiles...)
|
||||
} else if fi.Mode()&os.ModeSymlink != 0 {
|
||||
} else if util.IsSymlink(fi) {
|
||||
// If file is a symlink, we want to create the same relative symlink
|
||||
exclude, err := util.CopySymlink(fullPath, destPath, c.buildcontext)
|
||||
if err != nil {
|
||||
@@ -167,9 +167,15 @@ func (cr *CachingCopyCommand) ExecuteCommand(config *v1.Config, buildArgs *docke
|
||||
if cr.img == nil {
|
||||
return errors.New(fmt.Sprintf("cached command image is nil %v", cr.String()))
|
||||
}
|
||||
cr.extractedFiles, err = util.GetFSFromImage(RootDir, cr.img, cr.extractFn)
|
||||
|
||||
logrus.Infof("extractedFiles: %s", cr.extractedFiles)
|
||||
layers, err := cr.img.Layers()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "retrieve image layers")
|
||||
}
|
||||
|
||||
cr.extractedFiles, err = util.GetFSFromLayers(RootDir, layers, util.ExtractFunc(cr.extractFn), util.IncludeWhiteout())
|
||||
|
||||
logrus.Debugf("extractedFiles: %s", cr.extractedFiles)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "extracting fs from image")
|
||||
}
|
||||
@@ -182,7 +188,10 @@ func (cr *CachingCopyCommand) FilesUsedFromContext(config *v1.Config, buildArgs
|
||||
}
|
||||
|
||||
func (cr *CachingCopyCommand) FilesToSnapshot() []string {
|
||||
return cr.extractedFiles
|
||||
f := cr.extractedFiles
|
||||
logrus.Debugf("files extracted by caching copy command %s", f)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (cr *CachingCopyCommand) String() string {
|
||||
@@ -197,21 +206,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")
|
||||
}
|
||||
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(destPath) {
|
||||
return "", errors.New("dest path must be abs")
|
||||
}
|
||||
return destPath, nil
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
newPath, err = filepath.EvalSymlinks(newPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to eval symlinks")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
Regular → Executable
+17
-3
@@ -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) {
|
||||
|
||||
+19
-5
@@ -68,13 +68,12 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
cmd.Env = addDefaultHOME(config.User, replacementEnvs)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
var userStr string
|
||||
// If specified, run the command as a specific user
|
||||
if config.User != "" {
|
||||
userAndGroup := strings.Split(config.User, ":")
|
||||
userStr := userAndGroup[0]
|
||||
userStr = userAndGroup[0]
|
||||
var groupStr string
|
||||
if len(userAndGroup) > 1 {
|
||||
groupStr = userAndGroup[1]
|
||||
@@ -101,6 +100,7 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
|
||||
}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||
}
|
||||
cmd.Env = addDefaultHOME(userStr, replacementEnvs)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return errors.Wrap(err, "starting command")
|
||||
@@ -197,7 +197,18 @@ func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *docker
|
||||
if cr.img == nil {
|
||||
return errors.New(fmt.Sprintf("command image is nil %v", cr.String()))
|
||||
}
|
||||
cr.extractedFiles, err = util.GetFSFromImage(constants.RootDir, cr.img, cr.extractFn)
|
||||
|
||||
layers, err := cr.img.Layers()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "retrieving image layers")
|
||||
}
|
||||
|
||||
cr.extractedFiles, err = util.GetFSFromLayers(
|
||||
constants.RootDir,
|
||||
layers,
|
||||
util.ExtractFunc(cr.extractFn),
|
||||
util.IncludeWhiteout(),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "extracting fs from image")
|
||||
}
|
||||
@@ -206,7 +217,10 @@ func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *docker
|
||||
}
|
||||
|
||||
func (cr *CachingRunCommand) FilesToSnapshot() []string {
|
||||
return cr.extractedFiles
|
||||
f := cr.extractedFiles
|
||||
logrus.Debugf("files extracted from caching run command %s", f)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (cr *CachingRunCommand) String() string {
|
||||
|
||||
@@ -40,7 +40,7 @@ func (r *UserCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var groupStr string
|
||||
groupStr := userStr
|
||||
if len(userAndGroup) > 1 {
|
||||
groupStr, err = util.ResolveEnvironmentReplacement(userAndGroup[1], replacementEnvs, false)
|
||||
if err != nil {
|
||||
@@ -48,9 +48,8 @@ func (r *UserCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
|
||||
}
|
||||
}
|
||||
|
||||
if groupStr != "" {
|
||||
userStr = userStr + ":" + groupStr
|
||||
}
|
||||
userStr = userStr + ":" + groupStr
|
||||
|
||||
config.User = userStr
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,22 +28,23 @@ import (
|
||||
var userTests = []struct {
|
||||
user string
|
||||
expectedUID string
|
||||
expectedGID string
|
||||
}{
|
||||
{
|
||||
user: "root",
|
||||
expectedUID: "root",
|
||||
expectedUID: "root:root",
|
||||
},
|
||||
{
|
||||
user: "root-add",
|
||||
expectedUID: "root-add",
|
||||
expectedUID: "root-add:root-add",
|
||||
},
|
||||
{
|
||||
user: "0",
|
||||
expectedUID: "0",
|
||||
expectedUID: "0:0",
|
||||
},
|
||||
{
|
||||
user: "fakeUser",
|
||||
expectedUID: "fakeUser",
|
||||
expectedUID: "fakeUser:fakeUser",
|
||||
},
|
||||
{
|
||||
user: "root:root",
|
||||
@@ -56,6 +57,7 @@ var userTests = []struct {
|
||||
{
|
||||
user: "root:0",
|
||||
expectedUID: "root:0",
|
||||
expectedGID: "f0",
|
||||
},
|
||||
{
|
||||
user: "0:0",
|
||||
@@ -63,11 +65,15 @@ var userTests = []struct {
|
||||
},
|
||||
{
|
||||
user: "$envuser",
|
||||
expectedUID: "root",
|
||||
expectedUID: "root:root",
|
||||
},
|
||||
{
|
||||
user: "root:$envgroup",
|
||||
expectedUID: "root:root",
|
||||
expectedUID: "root:grp",
|
||||
},
|
||||
{
|
||||
user: "some:grp",
|
||||
expectedUID: "some:grp",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -76,7 +82,7 @@ func TestUpdateUser(t *testing.T) {
|
||||
cfg := &v1.Config{
|
||||
Env: []string{
|
||||
"envuser=root",
|
||||
"envgroup=root",
|
||||
"envgroup=grp",
|
||||
},
|
||||
}
|
||||
cmd := UserCommand{
|
||||
|
||||
@@ -39,8 +39,13 @@ type KanikoOptions struct {
|
||||
DigestFile string
|
||||
ImageNameDigestFile string
|
||||
OCILayoutPath string
|
||||
RegistryMirror string
|
||||
InsecureRegistries multiArg
|
||||
SkipTLSVerifyRegistries multiArg
|
||||
Destinations multiArg
|
||||
BuildArgs multiArg
|
||||
InsecureRegistries multiArg
|
||||
SkipTLSVerifyRegistries multiArg
|
||||
Insecure bool
|
||||
SkipTLSVerify bool
|
||||
InsecurePull bool
|
||||
@@ -50,9 +55,7 @@ type KanikoOptions struct {
|
||||
NoPush bool
|
||||
Cache bool
|
||||
Cleanup bool
|
||||
InsecureRegistries multiArg
|
||||
SkipTLSVerifyRegistries multiArg
|
||||
RegistryMirror string
|
||||
WhitelistVarRun bool
|
||||
}
|
||||
|
||||
// WarmerOptions are options that are set by command line arguments to the cache warmer.
|
||||
|
||||
@@ -90,14 +90,17 @@ func Stages(opts *config.KanikoOptions) ([]config.KanikoStage, error) {
|
||||
// baseImageIndex returns the index of the stage the current stage is built off
|
||||
// returns -1 if the current stage isn't built off a previous stage
|
||||
func baseImageIndex(currentStage int, stages []instructions.Stage) int {
|
||||
currentStageBaseName := strings.ToLower(stages[currentStage].BaseName)
|
||||
|
||||
for i, stage := range stages {
|
||||
if i > currentStage {
|
||||
break
|
||||
}
|
||||
if stage.Name == stages[currentStage].BaseName {
|
||||
if stage.Name == currentStageBaseName {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -245,15 +248,19 @@ func ParseCommands(cmdArray []string) ([]instructions.Command, error) {
|
||||
|
||||
// SaveStage returns true if the current stage will be needed later in the Dockerfile
|
||||
func saveStage(index int, stages []instructions.Stage) bool {
|
||||
currentStageName := stages[index].Name
|
||||
|
||||
for stageIndex, stage := range stages {
|
||||
if stageIndex <= index {
|
||||
continue
|
||||
}
|
||||
if stage.BaseName == stages[index].Name {
|
||||
|
||||
if strings.ToLower(stage.BaseName) == currentStageName {
|
||||
if stage.BaseName != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
+16
-9
@@ -23,8 +23,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
otiai10Cpy "github.com/otiai10/copy"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
@@ -158,8 +156,10 @@ func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string
|
||||
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
|
||||
}
|
||||
|
||||
srcCtx := s.opts.SrcContext
|
||||
|
||||
for _, f := range files {
|
||||
if err := compositeKey.AddPath(f); err != nil {
|
||||
if err := compositeKey.AddPath(f, srcCtx); err != nil {
|
||||
return compositeKey, err
|
||||
}
|
||||
}
|
||||
@@ -574,8 +574,8 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range filesToSave {
|
||||
logrus.Infof("Saving file %s for later use.", p)
|
||||
otiai10Cpy.Copy(p, filepath.Join(dstDir, p))
|
||||
logrus.Infof("Saving file %s for later use", p)
|
||||
util.CopyFileOrSymlink(p, dstDir)
|
||||
}
|
||||
|
||||
// Delete the filesystem
|
||||
@@ -587,16 +587,23 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fileToSave returns all the files matching the given pattern in deps.
|
||||
// If a file is a symlink, it also returns the target file.
|
||||
func filesToSave(deps []string) ([]string, error) {
|
||||
allFiles := []string{}
|
||||
srcFiles := []string{}
|
||||
for _, src := range deps {
|
||||
srcs, err := filepath.Glob(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allFiles = append(allFiles, srcs...)
|
||||
for _, f := range srcs {
|
||||
if link, err := util.EvalSymLink(f); err == nil {
|
||||
srcFiles = append(srcFiles, link)
|
||||
}
|
||||
srcFiles = append(srcFiles, f)
|
||||
}
|
||||
}
|
||||
return allFiles, nil
|
||||
return srcFiles, nil
|
||||
}
|
||||
|
||||
func fetchExtraStages(stages []config.KanikoStage, opts *config.KanikoOptions) error {
|
||||
@@ -684,7 +691,7 @@ func getHasher(snapshotMode string) (func(string) (string, error), error) {
|
||||
}
|
||||
|
||||
func resolveOnBuild(stage *config.KanikoStage, config *v1.Config) error {
|
||||
if config.OnBuild == nil {
|
||||
if config.OnBuild == nil || len(config.OnBuild) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Otherwise, parse into commands
|
||||
|
||||
@@ -538,7 +538,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
filePath := filepath.Join(dir, file)
|
||||
ch := NewCompositeCache("", "meow")
|
||||
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
@@ -570,7 +570,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
filePath := filepath.Join(dir, file)
|
||||
ch := NewCompositeCache("", "meow")
|
||||
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
@@ -618,7 +618,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
tarContent := generateTar(t, dir, filename)
|
||||
|
||||
ch := NewCompositeCache("", "")
|
||||
ch.AddPath(filepath)
|
||||
ch.AddPath(filepath, "")
|
||||
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
@@ -662,7 +662,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
}
|
||||
filePath := filepath.Join(dir, filename)
|
||||
ch := NewCompositeCache("", "")
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
@@ -713,7 +713,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
}
|
||||
|
||||
ch.AddKey(fmt.Sprintf("COPY %s bar.txt", filename))
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
hash2, err := ch.Hash()
|
||||
if err != nil {
|
||||
@@ -721,7 +721,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
}
|
||||
ch = NewCompositeCache("", fmt.Sprintf("COPY %s foo.txt", filename))
|
||||
ch.AddKey(fmt.Sprintf("COPY %s bar.txt", filename))
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
image := fakeImage{
|
||||
ImageLayers: []v1.Layer{
|
||||
@@ -777,14 +777,14 @@ COPY %s bar.txt
|
||||
}
|
||||
filePath := filepath.Join(dir, filename)
|
||||
ch := NewCompositeCache("", fmt.Sprintf("COPY %s foo.txt", filename))
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
hash1, err := ch.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
ch.AddKey(fmt.Sprintf("COPY %s bar.txt", filename))
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
hash2, err := ch.Hash()
|
||||
if err != nil {
|
||||
@@ -792,7 +792,7 @@ COPY %s bar.txt
|
||||
}
|
||||
ch = NewCompositeCache("", fmt.Sprintf("COPY %s foo.txt", filename))
|
||||
ch.AddKey(fmt.Sprintf("COPY %s bar.txt", filename))
|
||||
ch.AddPath(filePath)
|
||||
ch.AddPath(filePath, "")
|
||||
|
||||
image := fakeImage{
|
||||
ImageLayers: []v1.Layer{
|
||||
|
||||
@@ -54,18 +54,28 @@ func (s *CompositeCache) Hash() (string, error) {
|
||||
return util.SHA256(strings.NewReader(s.Key()))
|
||||
}
|
||||
|
||||
func (s *CompositeCache) AddPath(p string) error {
|
||||
func (s *CompositeCache) AddPath(p, context string) error {
|
||||
sha := sha256.New()
|
||||
fi, err := os.Lstat(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.Mode().IsDir() {
|
||||
k, err := HashDir(p)
|
||||
empty, k, err := hashDir(p, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.keys = append(s.keys, k)
|
||||
|
||||
// Only add the hash of this directory to the key
|
||||
// if there is any whitelisted content.
|
||||
if !empty || !util.ExcludeFile(p, context) {
|
||||
s.keys = append(s.keys, k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if util.ExcludeFile(p, context) {
|
||||
return nil
|
||||
}
|
||||
fh, err := util.CacheHasher()(p)
|
||||
@@ -81,12 +91,18 @@ func (s *CompositeCache) AddPath(p string) error {
|
||||
}
|
||||
|
||||
// HashDir returns a hash of the directory.
|
||||
func HashDir(p string) (string, error) {
|
||||
func hashDir(p, context string) (bool, string, error) {
|
||||
sha := sha256.New()
|
||||
empty := true
|
||||
if err := filepath.Walk(p, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exclude := util.ExcludeFile(path, context)
|
||||
if exclude {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileHash, err := util.CacheHasher()(path)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -94,10 +110,11 @@ func HashDir(p string) (string, error) {
|
||||
if _, err := sha.Write([]byte(fileHash)); err != nil {
|
||||
return err
|
||||
}
|
||||
empty = false
|
||||
return nil
|
||||
}); err != nil {
|
||||
return "", err
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", sha.Sum(nil)), nil
|
||||
return empty, fmt.Sprintf("%x", sha.Sum(nil)), nil
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ package executor
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
)
|
||||
|
||||
func Test_NewCompositeCache(t *testing.T) {
|
||||
@@ -77,7 +80,7 @@ func Test_CompositeCache_AddPath_dir(t *testing.T) {
|
||||
|
||||
fn := func() string {
|
||||
r := NewCompositeCache()
|
||||
if err := r.AddPath(tmpDir); err != nil {
|
||||
if err := r.AddPath(tmpDir, ""); err != nil {
|
||||
t.Errorf("expected error to be nil but was %v", err)
|
||||
}
|
||||
|
||||
@@ -115,7 +118,7 @@ func Test_CompositeCache_AddPath_file(t *testing.T) {
|
||||
p := tmpfile.Name()
|
||||
fn := func() string {
|
||||
r := NewCompositeCache()
|
||||
if err := r.AddPath(p); err != nil {
|
||||
if err := r.AddPath(p, ""); err != nil {
|
||||
t.Errorf("expected error to be nil but was %v", err)
|
||||
}
|
||||
|
||||
@@ -135,3 +138,433 @@ func Test_CompositeCache_AddPath_file(t *testing.T) {
|
||||
t.Errorf("expected hash %v to equal hash %v", hash1, hash2)
|
||||
}
|
||||
}
|
||||
|
||||
func createFilesystemStructure(root string, directories, files []string) error {
|
||||
for _, d := range directories {
|
||||
dirPath := path.Join(root, d)
|
||||
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, fileName := range files {
|
||||
filePath := path.Join(root, fileName)
|
||||
err := ioutil.WriteFile(filePath, []byte(fileName), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setIgnoreContext(content string) error {
|
||||
dockerIgnoreDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(dockerIgnoreDir)
|
||||
err = ioutil.WriteFile(dockerIgnoreDir+".dockerignore", []byte(content), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = util.GetExcludedFiles(dockerIgnoreDir, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashDirectory(dirpath string) (string, error) {
|
||||
cache1 := NewCompositeCache()
|
||||
err := cache1.AddPath(dirpath, dirpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash, err := cache1.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
func Test_CompositeKey_AddPath_Works(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directories []string
|
||||
files []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
directories: []string{},
|
||||
files: []string{},
|
||||
},
|
||||
{
|
||||
name: "dirs",
|
||||
directories: []string{"foo", "bar", "foobar", "f/o/o"},
|
||||
files: []string{},
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
directories: []string{},
|
||||
files: []string{"foo", "bar", "foobar"},
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
directories: []string{"foo", "bar"},
|
||||
files: []string{"foo/bar", "bar/baz", "foobar"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir1)
|
||||
err = createFilesystemStructure(testDir1, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
testDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir2)
|
||||
err = createFilesystemStructure(testDir2, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
hash1, err := hashDirectory(testDir1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
hash2, err := hashDirectory(testDir2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
|
||||
if hash1 != hash2 {
|
||||
t.Errorf("Expected equal hashes, got: %s and %s", hash1, hash2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CompositeKey_AddPath_WithExtraFile_Works(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directories []string
|
||||
files []string
|
||||
extraFile string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
directories: []string{},
|
||||
files: []string{},
|
||||
extraFile: "file",
|
||||
},
|
||||
{
|
||||
name: "dirs",
|
||||
directories: []string{"foo", "bar", "foobar", "f/o/o"},
|
||||
files: []string{},
|
||||
extraFile: "f/o/o/extra",
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
directories: []string{},
|
||||
files: []string{"foo", "bar", "foobar"},
|
||||
extraFile: "foo.extra",
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
directories: []string{"foo", "bar"},
|
||||
files: []string{"foo/bar", "bar/baz", "foobar"},
|
||||
extraFile: "bar/extra",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir1)
|
||||
err = createFilesystemStructure(testDir1, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
testDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir2)
|
||||
err = createFilesystemStructure(testDir2, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
extraPath := path.Join(testDir2, test.extraFile)
|
||||
err = ioutil.WriteFile(extraPath, []byte(test.extraFile), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
hash1, err := hashDirectory(testDir1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
hash2, err := hashDirectory(testDir2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Errorf("Expected different hashes, got: %s and %s", hash1, hash2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CompositeKey_AddPath_WithExtraDir_Works(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directories []string
|
||||
files []string
|
||||
extraDir string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
directories: []string{},
|
||||
files: []string{},
|
||||
extraDir: "extra",
|
||||
},
|
||||
{
|
||||
name: "dirs",
|
||||
directories: []string{"foo", "bar", "foobar", "f/o/o"},
|
||||
files: []string{},
|
||||
extraDir: "f/o/o/extra",
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
directories: []string{},
|
||||
files: []string{"foo", "bar", "foobar"},
|
||||
extraDir: "foo.extra",
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
directories: []string{"foo", "bar"},
|
||||
files: []string{"foo/bar", "bar/baz", "foobar"},
|
||||
extraDir: "bar/extra",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir1)
|
||||
err = createFilesystemStructure(testDir1, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
testDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir2)
|
||||
err = createFilesystemStructure(testDir2, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
extraPath := path.Join(testDir2, test.extraDir)
|
||||
err = os.MkdirAll(extraPath, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
hash1, err := hashDirectory(testDir1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
hash2, err := hashDirectory(testDir2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Errorf("Expected different hashes, got: %s and %s", hash1, hash2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CompositeKey_AddPath_WithExtraFilIgnored_Works(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directories []string
|
||||
files []string
|
||||
extraFile string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
directories: []string{},
|
||||
files: []string{},
|
||||
extraFile: "extra",
|
||||
},
|
||||
{
|
||||
name: "dirs",
|
||||
directories: []string{"foo", "bar", "foobar", "f/o/o"},
|
||||
files: []string{},
|
||||
extraFile: "f/o/o/extra",
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
directories: []string{},
|
||||
files: []string{"foo", "bar", "foobar"},
|
||||
extraFile: "extra",
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
directories: []string{"foo", "bar"},
|
||||
files: []string{"foo/bar", "bar/baz", "foobar"},
|
||||
extraFile: "bar/extra",
|
||||
},
|
||||
}
|
||||
|
||||
err := setIgnoreContext("**/extra")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting exlusion context: %s", err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir1)
|
||||
err = createFilesystemStructure(testDir1, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
testDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir2)
|
||||
err = createFilesystemStructure(testDir2, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
extraPath := path.Join(testDir2, test.extraFile)
|
||||
err = ioutil.WriteFile(extraPath, []byte(test.extraFile), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
hash1, err := hashDirectory(testDir1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
hash2, err := hashDirectory(testDir2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
|
||||
if hash1 != hash2 {
|
||||
t.Errorf("Expected equal hashes, got: %s and %s", hash1, hash2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CompositeKey_AddPath_WithExtraDirIgnored_Works(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directories []string
|
||||
files []string
|
||||
extraDir string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
directories: []string{},
|
||||
files: []string{},
|
||||
extraDir: "extra",
|
||||
},
|
||||
{
|
||||
name: "dirs",
|
||||
directories: []string{"foo", "bar", "foobar", "f/o/o"},
|
||||
files: []string{},
|
||||
extraDir: "f/o/o/extra",
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
directories: []string{},
|
||||
files: []string{"foo", "bar", "foobar"},
|
||||
extraDir: "extra",
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
directories: []string{"foo", "bar"},
|
||||
files: []string{"foo/bar", "bar/baz", "foobar"},
|
||||
extraDir: "bar/extra",
|
||||
},
|
||||
}
|
||||
|
||||
err := setIgnoreContext("**/extra")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting exlusion context: %s", err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir1)
|
||||
err = createFilesystemStructure(testDir1, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
testDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir2)
|
||||
err = createFilesystemStructure(testDir2, test.directories, test.files)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
extraPath := path.Join(testDir2, test.extraDir)
|
||||
err = os.MkdirAll(extraPath, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating filesytem structure: %s", err)
|
||||
}
|
||||
|
||||
hash1, err := hashDirectory(testDir1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
hash2, err := hashDirectory(testDir2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate hash: %s", err)
|
||||
}
|
||||
|
||||
if hash1 != hash2 {
|
||||
t.Errorf("Expected equal hashes, got: %s and %s", hash1, hash2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/google/go-containerregistry/pkg/v1 (interfaces: Layer)
|
||||
|
||||
// Package mockv1 is a generated GoMock package.
|
||||
package mockv1
|
||||
|
||||
import (
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
types "github.com/google/go-containerregistry/pkg/v1/types"
|
||||
)
|
||||
|
||||
// MockLayer is a mock of Layer interface
|
||||
type MockLayer struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLayerMockRecorder
|
||||
}
|
||||
|
||||
// MockLayerMockRecorder is the mock recorder for MockLayer
|
||||
type MockLayerMockRecorder struct {
|
||||
mock *MockLayer
|
||||
}
|
||||
|
||||
// NewMockLayer creates a new mock instance
|
||||
func NewMockLayer(ctrl *gomock.Controller) *MockLayer {
|
||||
mock := &MockLayer{ctrl: ctrl}
|
||||
mock.recorder = &MockLayerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockLayer) EXPECT() *MockLayerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Compressed mocks base method
|
||||
func (m *MockLayer) Compressed() (io.ReadCloser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Compressed")
|
||||
ret0, _ := ret[0].(io.ReadCloser)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Compressed indicates an expected call of Compressed
|
||||
func (mr *MockLayerMockRecorder) Compressed() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compressed", reflect.TypeOf((*MockLayer)(nil).Compressed))
|
||||
}
|
||||
|
||||
// DiffID mocks base method
|
||||
func (m *MockLayer) DiffID() (v1.Hash, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DiffID")
|
||||
ret0, _ := ret[0].(v1.Hash)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DiffID indicates an expected call of DiffID
|
||||
func (mr *MockLayerMockRecorder) DiffID() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiffID", reflect.TypeOf((*MockLayer)(nil).DiffID))
|
||||
}
|
||||
|
||||
// Digest mocks base method
|
||||
func (m *MockLayer) Digest() (v1.Hash, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Digest")
|
||||
ret0, _ := ret[0].(v1.Hash)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Digest indicates an expected call of Digest
|
||||
func (mr *MockLayerMockRecorder) Digest() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Digest", reflect.TypeOf((*MockLayer)(nil).Digest))
|
||||
}
|
||||
|
||||
// MediaType mocks base method
|
||||
func (m *MockLayer) MediaType() (types.MediaType, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MediaType")
|
||||
ret0, _ := ret[0].(types.MediaType)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MediaType indicates an expected call of MediaType
|
||||
func (mr *MockLayerMockRecorder) MediaType() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MediaType", reflect.TypeOf((*MockLayer)(nil).MediaType))
|
||||
}
|
||||
|
||||
// Size mocks base method
|
||||
func (m *MockLayer) Size() (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Size")
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Size indicates an expected call of Size
|
||||
func (mr *MockLayerMockRecorder) Size() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockLayer)(nil).Size))
|
||||
}
|
||||
|
||||
// Uncompressed mocks base method
|
||||
func (m *MockLayer) Uncompressed() (io.ReadCloser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Uncompressed")
|
||||
ret0, _ := ret[0].(io.ReadCloser)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Uncompressed indicates an expected call of Uncompressed
|
||||
func (mr *MockLayerMockRecorder) Uncompressed() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Uncompressed", reflect.TypeOf((*MockLayer)(nil).Uncompressed))
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package snapshot
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"syscall"
|
||||
@@ -112,7 +113,6 @@ func (s *Snapshotter) TakeSnapshotFS() (string, error) {
|
||||
if err := writeToTar(t, filesToAdd, filesToWhiteOut); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
@@ -179,8 +179,13 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
|
||||
return nil, nil, fmt.Errorf("could not check if file has changed %s %s", path, err)
|
||||
}
|
||||
if fileChanged {
|
||||
logrus.Tracef("Adding %s to layer, because it was changed.", path)
|
||||
filesToAdd = append(filesToAdd, path)
|
||||
// Get target file for symlinks so the symlink is not a dead link.
|
||||
files, err := filesWithLinks(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
logrus.Tracef("Adding files %s to layer, because it was changed.", files)
|
||||
filesToAdd = append(filesToAdd, files...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,14 +193,12 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
|
||||
filesToAdd = filesWithParentDirs(filesToAdd)
|
||||
|
||||
sort.Strings(filesToAdd)
|
||||
|
||||
// Add files to the layered map
|
||||
for _, file := range filesToAdd {
|
||||
if err := s.l.Add(file); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to add file %s to layered map: %s", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
return filesToAdd, filesToWhiteOut, nil
|
||||
}
|
||||
|
||||
@@ -236,3 +239,21 @@ func filesWithParentDirs(files []string) []string {
|
||||
|
||||
return newFiles
|
||||
}
|
||||
|
||||
// filesWithLinks returns the symlink and the target path if its exists.
|
||||
func filesWithLinks(path string) ([]string, error) {
|
||||
link, err := util.GetSymLink(path)
|
||||
if err == util.ErrNotSymLink {
|
||||
return []string{path}, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Add symlink if it exists in the FS
|
||||
if !filepath.IsAbs(link) {
|
||||
link = filepath.Join(filepath.Dir(path), link)
|
||||
}
|
||||
if _, err := os.Stat(link); err != nil {
|
||||
return []string{path}, nil
|
||||
}
|
||||
return []string{path, link}, nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
func TestSnapshotFSFileChange(t *testing.T) {
|
||||
testDir, snapshotter, cleanup, err := setUpTestDir()
|
||||
testDir, snapshotter, cleanup, err := setUpTest()
|
||||
testDirWithoutLeadingSlash := strings.TrimLeft(testDir, "/")
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
@@ -90,7 +90,7 @@ func TestSnapshotFSFileChange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapshotFSIsReproducible(t *testing.T) {
|
||||
testDir, snapshotter, cleanup, err := setUpTestDir()
|
||||
testDir, snapshotter, cleanup, err := setUpTest()
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -129,7 +129,7 @@ func TestSnapshotFSIsReproducible(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapshotFSChangePermissions(t *testing.T) {
|
||||
testDir, snapshotter, cleanup, err := setUpTestDir()
|
||||
testDir, snapshotter, cleanup, err := setUpTest()
|
||||
testDirWithoutLeadingSlash := strings.TrimLeft(testDir, "/")
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
@@ -180,7 +180,7 @@ func TestSnapshotFSChangePermissions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapshotFiles(t *testing.T) {
|
||||
testDir, snapshotter, cleanup, err := setUpTestDir()
|
||||
testDir, snapshotter, cleanup, err := setUpTest()
|
||||
testDirWithoutLeadingSlash := strings.TrimLeft(testDir, "/")
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
@@ -230,7 +230,7 @@ func TestSnapshotFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEmptySnapshotFS(t *testing.T) {
|
||||
_, snapshotter, cleanup, err := setUpTestDir()
|
||||
_, snapshotter, cleanup, err := setUpTest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -253,12 +253,97 @@ func TestEmptySnapshotFS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func setUpTestDir() (string, *Snapshotter, func(), error) {
|
||||
testDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", nil, nil, errors.Wrap(err, "setting up temp dir")
|
||||
func TestFileWithLinks(t *testing.T) {
|
||||
|
||||
link := "baz/link"
|
||||
tcs := []struct {
|
||||
name string
|
||||
path string
|
||||
linkFileTarget string
|
||||
expected []string
|
||||
shouldErr bool
|
||||
}{
|
||||
{
|
||||
name: "given path is a symlink that points to a valid target",
|
||||
path: link,
|
||||
linkFileTarget: "file",
|
||||
expected: []string{link, "baz/file"},
|
||||
},
|
||||
{
|
||||
name: "given path is a symlink points to non existing path",
|
||||
path: link,
|
||||
linkFileTarget: "does-not-exists",
|
||||
expected: []string{link},
|
||||
},
|
||||
{
|
||||
name: "given path is a regular file",
|
||||
path: "kaniko/file",
|
||||
linkFileTarget: "file",
|
||||
expected: []string{"kaniko/file"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tcs {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testDir, cleanup, err := setUpTestDir()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
if err := setupSymlink(testDir, link, tt.linkFileTarget); err != nil {
|
||||
t.Fatalf("could not set up symlink due to %s", err)
|
||||
}
|
||||
actual, err := filesWithLinks(filepath.Join(testDir, tt.path))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
sortAndCompareFilepaths(t, testDir, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupSymlink(dir string, link string, target string) error {
|
||||
return os.Symlink(target, filepath.Join(dir, link))
|
||||
}
|
||||
|
||||
func sortAndCompareFilepaths(t *testing.T, testDir string, expected []string, actual []string) {
|
||||
expectedFullPaths := make([]string, len(expected))
|
||||
for i, file := range expected {
|
||||
expectedFullPaths[i] = filepath.Join(testDir, file)
|
||||
}
|
||||
sort.Strings(expectedFullPaths)
|
||||
sort.Strings(actual)
|
||||
testutil.CheckDeepEqual(t, expectedFullPaths, actual)
|
||||
}
|
||||
|
||||
func setUpTestDir() (string, func(), error) {
|
||||
testDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "setting up temp dir")
|
||||
}
|
||||
files := map[string]string{
|
||||
"foo": "baz1",
|
||||
"bar/bat": "baz2",
|
||||
"kaniko/file": "file",
|
||||
"baz/file": "testfile",
|
||||
}
|
||||
// Set up initial files
|
||||
if err := testutil.SetupFiles(testDir, files); err != nil {
|
||||
return "", nil, errors.Wrap(err, "setting up file system")
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
os.RemoveAll(testDir)
|
||||
}
|
||||
|
||||
return testDir, cleanup, nil
|
||||
}
|
||||
|
||||
func setUpTest() (string, *Snapshotter, func(), error) {
|
||||
testDir, dirCleanUp, err := setUpTestDir()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
snapshotPath, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", nil, nil, errors.Wrap(err, "setting up temp dir")
|
||||
@@ -266,16 +351,6 @@ func setUpTestDir() (string, *Snapshotter, func(), error) {
|
||||
|
||||
snapshotPathPrefix = snapshotPath
|
||||
|
||||
files := map[string]string{
|
||||
"foo": "baz1",
|
||||
"bar/bat": "baz2",
|
||||
"kaniko/file": "file",
|
||||
}
|
||||
// Set up initial files
|
||||
if err := testutil.SetupFiles(testDir, files); err != nil {
|
||||
return "", nil, nil, errors.Wrap(err, "setting up file system")
|
||||
}
|
||||
|
||||
// Take the initial snapshot
|
||||
l := NewLayeredMap(util.Hasher(), util.CacheHasher())
|
||||
snapshotter := NewSnapshotter(l, testDir)
|
||||
@@ -285,7 +360,7 @@ func setUpTestDir() (string, *Snapshotter, func(), error) {
|
||||
|
||||
cleanup := func() {
|
||||
os.RemoveAll(snapshotPath)
|
||||
os.RemoveAll(testDir)
|
||||
dirCleanUp()
|
||||
}
|
||||
|
||||
return testDir, snapshotter, cleanup, nil
|
||||
|
||||
+17
-12
@@ -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
|
||||
@@ -208,7 +213,7 @@ func IsSrcsValid(srcsAndDest instructions.SourcesAndDest, resolvedSources []stri
|
||||
if !ContainsWildcards(srcs) {
|
||||
totalSrcs := 0
|
||||
for _, src := range srcs {
|
||||
if excludeFile(src, root) {
|
||||
if ExcludeFile(src, root) {
|
||||
continue
|
||||
}
|
||||
totalSrcs++
|
||||
@@ -245,7 +250,7 @@ func IsSrcsValid(srcsAndDest instructions.SourcesAndDest, resolvedSources []stri
|
||||
return errors.Wrap(err, "failed to get relative files")
|
||||
}
|
||||
for _, file := range files {
|
||||
if excludeFile(file, root) {
|
||||
if ExcludeFile(file, root) {
|
||||
continue
|
||||
}
|
||||
totalFiles++
|
||||
|
||||
@@ -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",
|
||||
|
||||
+158
-36
@@ -30,6 +30,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
otiai10Cpy "github.com/otiai10/copy"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/constants"
|
||||
"github.com/docker/docker/builder/dockerignore"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
@@ -48,13 +50,6 @@ var initialWhitelist = []WhitelistEntry{
|
||||
Path: "/kaniko",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
{
|
||||
// /var/run is a special case. It's common to mount in /var/run/docker.sock or something similar
|
||||
// which leads to a special mount on the /var/run/docker.sock file itself, but the directory to exist
|
||||
// in the image with no way to tell if it came from the base image or not.
|
||||
Path: "/var/run",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
{
|
||||
// similarly, we whitelist /etc/mtab, since there is no way to know if the file was mounted or came
|
||||
// from the base image
|
||||
@@ -71,25 +66,58 @@ var excluded []string
|
||||
|
||||
type ExtractFunction func(string, *tar.Header, io.Reader) error
|
||||
|
||||
type FSConfig struct {
|
||||
includeWhiteout bool
|
||||
extractFunc ExtractFunction
|
||||
}
|
||||
|
||||
type FSOpt func(*FSConfig)
|
||||
|
||||
func IncludeWhiteout() FSOpt {
|
||||
return func(opts *FSConfig) {
|
||||
opts.includeWhiteout = true
|
||||
}
|
||||
}
|
||||
|
||||
func ExtractFunc(extractFunc ExtractFunction) FSOpt {
|
||||
return func(opts *FSConfig) {
|
||||
opts.extractFunc = extractFunc
|
||||
}
|
||||
}
|
||||
|
||||
// GetFSFromImage extracts the layers of img to root
|
||||
// It returns a list of all files extracted
|
||||
func GetFSFromImage(root string, img v1.Image, extract ExtractFunction) ([]string, error) {
|
||||
if extract == nil {
|
||||
return nil, errors.New("must supply an extract function")
|
||||
}
|
||||
if img == nil {
|
||||
return nil, errors.New("image cannot be nil")
|
||||
}
|
||||
if err := DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("Mounted directories: %v", whitelist)
|
||||
|
||||
layers, err := img.Layers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extractedFiles := []string{}
|
||||
|
||||
return GetFSFromLayers(root, layers, ExtractFunc(extract))
|
||||
}
|
||||
|
||||
func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, error) {
|
||||
cfg := new(FSConfig)
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
if cfg.extractFunc == nil {
|
||||
return nil, errors.New("must supply an extract function")
|
||||
}
|
||||
|
||||
if err := DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logrus.Debugf("Mounted directories: %v", whitelist)
|
||||
|
||||
extractedFiles := []string{}
|
||||
for i, l := range layers {
|
||||
if mediaType, err := l.MediaType(); err == nil {
|
||||
logrus.Tracef("Extracting layer %d of media type %s", i, mediaType)
|
||||
@@ -102,29 +130,41 @@ func GetFSFromImage(root string, img v1.Image, extract ExtractFunction) ([]strin
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
tr := tar.NewReader(r)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("error reading tar %d", i))
|
||||
}
|
||||
|
||||
path := filepath.Join(root, filepath.Clean(hdr.Name))
|
||||
base := filepath.Base(path)
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
if strings.HasPrefix(base, ".wh.") {
|
||||
logrus.Debugf("Whiting out %s", path)
|
||||
|
||||
name := strings.TrimPrefix(base, ".wh.")
|
||||
if err := os.RemoveAll(filepath.Join(dir, name)); err != nil {
|
||||
return nil, errors.Wrapf(err, "removing whiteout %s", hdr.Name)
|
||||
}
|
||||
continue
|
||||
|
||||
if !cfg.includeWhiteout {
|
||||
logrus.Debug("not including whiteout files")
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
if err := extract(root, hdr, tr); err != nil {
|
||||
|
||||
if err := cfg.extractFunc(root, hdr, tr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extractedFiles = append(extractedFiles, filepath.Join(root, filepath.Clean(hdr.Name)))
|
||||
}
|
||||
}
|
||||
@@ -190,7 +230,7 @@ func unTar(r io.Reader, dest string) ([]string, error) {
|
||||
if err := ExtractFile(dest, hdr, tr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extractedFiles = append(extractedFiles, dest)
|
||||
extractedFiles = append(extractedFiles, filepath.Join(dest, filepath.Clean(hdr.Name)))
|
||||
}
|
||||
return extractedFiles, nil
|
||||
}
|
||||
@@ -215,6 +255,7 @@ func ExtractFile(dest string, hdr *tar.Header, tr io.Reader) error {
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeReg:
|
||||
logrus.Tracef("creating file %s", path)
|
||||
|
||||
// It's possible a file is in the tar before its directory,
|
||||
// or a file was copied over a directory prior to now
|
||||
fi, err := os.Stat(dir)
|
||||
@@ -225,6 +266,7 @@ func ExtractFile(dest string, hdr *tar.Header, tr io.Reader) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if something already exists at path (symlinks etc.)
|
||||
// If so, delete it
|
||||
if FilepathExists(path) {
|
||||
@@ -232,16 +274,26 @@ func ExtractFile(dest string, hdr *tar.Header, tr io.Reader) error {
|
||||
return errors.Wrapf(err, "error removing %s to make way for new file.", path)
|
||||
}
|
||||
}
|
||||
|
||||
currFile, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(currFile, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = setFilePermissions(path, mode, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We set AccessTime because its a required arg but we only care about
|
||||
// ModTime. The file will get accessed again so AccessTime will change.
|
||||
if err := os.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currFile.Close()
|
||||
case tar.TypeDir:
|
||||
logrus.Tracef("creating dir %s", path)
|
||||
@@ -431,18 +483,8 @@ func FilepathExists(path string) bool {
|
||||
// CreateFile creates a file at path and copies over contents from the reader
|
||||
func CreateFile(path string, reader io.Reader, perm os.FileMode, uid uint32, gid uint32) error {
|
||||
// Create directory path if it doesn't exist
|
||||
baseDir := filepath.Dir(path)
|
||||
if info, err := os.Lstat(baseDir); os.IsNotExist(err) {
|
||||
logrus.Tracef("baseDir %s for file %s does not exist. Creating.", baseDir, path)
|
||||
if err := os.MkdirAll(baseDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
switch mode := info.Mode(); {
|
||||
case mode&os.ModeSymlink != 0:
|
||||
logrus.Infof("destination cannot be a symlink %v", baseDir)
|
||||
return errors.New("destination cannot be a symlink")
|
||||
}
|
||||
if err := createParentDirectory(path); err != nil {
|
||||
return err
|
||||
}
|
||||
dest, err := os.Create(path)
|
||||
if err != nil {
|
||||
@@ -502,9 +544,10 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
|
||||
fullPath := filepath.Join(src, file)
|
||||
fi, err := os.Lstat(fullPath)
|
||||
if err != nil {
|
||||
fmt.Println(" i am returning from here this", err)
|
||||
return nil, err
|
||||
}
|
||||
if excludeFile(fullPath, buildcontext) {
|
||||
if ExcludeFile(fullPath, buildcontext) {
|
||||
logrus.Debugf("%s found in .dockerignore, ignoring", src)
|
||||
continue
|
||||
}
|
||||
@@ -519,7 +562,7 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
|
||||
if err := mkdirAllWithPermissions(destPath, mode, uid, gid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if fi.Mode()&os.ModeSymlink != 0 {
|
||||
} else if IsSymlink(fi) {
|
||||
// If file is a symlink, we want to create the same relative symlink
|
||||
if _, err := CopySymlink(fullPath, destPath, buildcontext); err != nil {
|
||||
return nil, err
|
||||
@@ -537,7 +580,7 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
|
||||
|
||||
// CopySymlink copies the symlink at src to dest
|
||||
func CopySymlink(src, dest, buildcontext string) (bool, error) {
|
||||
if excludeFile(src, buildcontext) {
|
||||
if ExcludeFile(src, buildcontext) {
|
||||
logrus.Debugf("%s found in .dockerignore, ignoring", src)
|
||||
return true, nil
|
||||
}
|
||||
@@ -550,12 +593,15 @@ func CopySymlink(src, dest, buildcontext string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if err := createParentDirectory(dest); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, os.Symlink(link, dest)
|
||||
}
|
||||
|
||||
// CopyFile copies the file at src to dest
|
||||
func CopyFile(src, dest, buildcontext string) (bool, error) {
|
||||
if excludeFile(src, buildcontext) {
|
||||
if ExcludeFile(src, buildcontext) {
|
||||
logrus.Debugf("%s found in .dockerignore, ignoring", src)
|
||||
return true, nil
|
||||
}
|
||||
@@ -599,8 +645,8 @@ func GetExcludedFiles(dockerfilepath string, buildcontext string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// excludeFile returns true if the .dockerignore specified this file should be ignored
|
||||
func excludeFile(path, buildcontext string) bool {
|
||||
// ExcludeFile returns true if the .dockerignore specified this file should be ignored
|
||||
func ExcludeFile(path, buildcontext string) bool {
|
||||
if HasFilepathPrefix(path, buildcontext, false) {
|
||||
var err error
|
||||
path, err = filepath.Rel(buildcontext, path)
|
||||
@@ -647,6 +693,7 @@ func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int) error
|
||||
if err := os.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -677,3 +724,78 @@ func CreateTargetTarfile(tarpath string) (*os.File, error) {
|
||||
return os.Create(tarpath)
|
||||
|
||||
}
|
||||
|
||||
// Returns true if a file is a symlink
|
||||
func IsSymlink(fi os.FileInfo) bool {
|
||||
return fi.Mode()&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
var ErrNotSymLink = fmt.Errorf("not a symlink")
|
||||
|
||||
func GetSymLink(path string) (string, error) {
|
||||
if err := getSymlink(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return os.Readlink(path)
|
||||
}
|
||||
|
||||
func EvalSymLink(path string) (string, error) {
|
||||
if err := getSymlink(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(path)
|
||||
}
|
||||
|
||||
func getSymlink(path string) error {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !IsSymlink(fi) {
|
||||
return ErrNotSymLink
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// For cross stage dependencies kaniko must persist the referenced path so that it can be used in
|
||||
// the dependent stage. For symlinks we copy the target path because copying the symlink would
|
||||
// result in a dead link
|
||||
func CopyFileOrSymlink(src string, destDir string) error {
|
||||
destFile := filepath.Join(destDir, src)
|
||||
if fi, _ := os.Lstat(src); IsSymlink(fi) {
|
||||
link, err := os.Readlink(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Symlink(link, destFile)
|
||||
}
|
||||
return otiai10Cpy.Copy(src, destFile)
|
||||
}
|
||||
|
||||
func createParentDirectory(path string) error {
|
||||
baseDir := filepath.Dir(path)
|
||||
if info, err := os.Lstat(baseDir); os.IsNotExist(err) {
|
||||
logrus.Tracef("baseDir %s for file %s does not exist. Creating.", baseDir, path)
|
||||
if err := os.MkdirAll(baseDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if IsSymlink(info) {
|
||||
logrus.Infof("destination cannot be a symlink %v", baseDir)
|
||||
return errors.New("destination cannot be a symlink")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateInitialWhitelist will add /var/run to whitelisted paths if
|
||||
func UpdateWhitelist(whitelistVarRun bool) {
|
||||
if !whitelistVarRun {
|
||||
return
|
||||
}
|
||||
whitelist = append(initialWhitelist, WhitelistEntry{
|
||||
// /var/run is a special case. It's common to mount in /var/run/docker.sock or something similar
|
||||
// which leads to a special mount on the /var/run/docker.sock file itself, but the directory to exist
|
||||
// in the image with no way to tell if it came from the base image or not.
|
||||
Path: "/var/run",
|
||||
PrefixMatchOnly: false,
|
||||
})
|
||||
}
|
||||
|
||||
+448
-16
@@ -20,6 +20,7 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -27,8 +28,13 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/mocks/go-containerregistry/mockv1"
|
||||
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
)
|
||||
|
||||
func Test_DetectFilesystemWhitelist(t *testing.T) {
|
||||
@@ -58,7 +64,6 @@ func Test_DetectFilesystemWhitelist(t *testing.T) {
|
||||
{"/dev", false},
|
||||
{"/dev/pts", false},
|
||||
{"/sys", false},
|
||||
{"/var/run", false},
|
||||
{"/etc/mtab", false},
|
||||
}
|
||||
actualWhitelist := whitelist
|
||||
@@ -445,6 +450,19 @@ func fileMatches(p string, c []byte) checker {
|
||||
}
|
||||
}
|
||||
|
||||
func timesMatch(p string, fTime time.Time) checker {
|
||||
return func(root string, t *testing.T) {
|
||||
fi, err := os.Stat(filepath.Join(root, p))
|
||||
if err != nil {
|
||||
t.Fatalf("error statting file %s", p)
|
||||
}
|
||||
|
||||
if fi.ModTime().UTC() != fTime.UTC() {
|
||||
t.Errorf("Expected modtime to equal %v but was %v", fTime, fi.ModTime())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func permissionsMatch(p string, perms os.FileMode) checker {
|
||||
return func(root string, t *testing.T) {
|
||||
fi, err := os.Stat(filepath.Join(root, p))
|
||||
@@ -488,14 +506,16 @@ func filesAreHardlinks(first, second string) checker {
|
||||
}
|
||||
}
|
||||
|
||||
func fileHeader(name string, contents string, mode int64) *tar.Header {
|
||||
func fileHeader(name string, contents string, mode int64, fTime time.Time) *tar.Header {
|
||||
return &tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(contents)),
|
||||
Mode: mode,
|
||||
Typeflag: tar.TypeReg,
|
||||
Uid: os.Getuid(),
|
||||
Gid: os.Getgid(),
|
||||
Name: name,
|
||||
Size: int64(len(contents)),
|
||||
Mode: mode,
|
||||
Typeflag: tar.TypeReg,
|
||||
Uid: os.Getuid(),
|
||||
Gid: os.Getgid(),
|
||||
AccessTime: fTime,
|
||||
ModTime: fTime,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +548,87 @@ func dirHeader(name string, mode int64) *tar.Header {
|
||||
}
|
||||
}
|
||||
|
||||
func createUncompressedTar(fileContents map[string]string, tarFileName, testDir string) error {
|
||||
if err := testutil.SetupFiles(testDir, fileContents); err != nil {
|
||||
return err
|
||||
}
|
||||
tarFile, err := os.Create(filepath.Join(testDir, tarFileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t := NewTar(tarFile)
|
||||
defer t.Close()
|
||||
for file := range fileContents {
|
||||
filePath := filepath.Join(testDir, file)
|
||||
if err := t.AddFileToTar(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_unTar(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
setupTarContents map[string]string
|
||||
tarFileName string
|
||||
destination string
|
||||
expectedFileList []string
|
||||
errorExpected bool
|
||||
}{
|
||||
{
|
||||
name: "multfile tar",
|
||||
setupTarContents: map[string]string{
|
||||
"foo/file1": "hello World",
|
||||
"bar/file1": "hello World",
|
||||
"bar/file2": "hello World",
|
||||
"file1": "hello World",
|
||||
},
|
||||
tarFileName: "test.tar",
|
||||
destination: "/",
|
||||
expectedFileList: []string{"foo/file1", "bar/file1", "bar/file2", "file1"},
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
name: "empty tar",
|
||||
setupTarContents: map[string]string{},
|
||||
tarFileName: "test.tar",
|
||||
destination: "/",
|
||||
expectedFileList: nil,
|
||||
errorExpected: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
testDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(testDir)
|
||||
if err := createUncompressedTar(tc.setupTarContents, tc.tarFileName, testDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file, err := os.Open(filepath.Join(testDir, tc.tarFileName))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fileList, err := unTar(file, tc.destination)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// update expectedFileList to take into factor temp directory
|
||||
for i, file := range tc.expectedFileList {
|
||||
tc.expectedFileList[i] = filepath.Join(testDir, file)
|
||||
}
|
||||
// sort both slices to ensure objects are in the same order for deep equals
|
||||
sort.Strings(tc.expectedFileList)
|
||||
sort.Strings(fileList)
|
||||
testutil.CheckErrorAndDeepEqual(t, tc.errorExpected, err, tc.expectedFileList, fileList)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractFile(t *testing.T) {
|
||||
type tc struct {
|
||||
name string
|
||||
@@ -537,21 +638,27 @@ func TestExtractFile(t *testing.T) {
|
||||
checkers []checker
|
||||
}
|
||||
|
||||
defaultTestTime, err := time.Parse(time.RFC3339, "1912-06-23T00:00:00Z")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tcs := []tc{
|
||||
{
|
||||
name: "normal file",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{fileHeader("./bar", "helloworld", 0644)},
|
||||
hdrs: []*tar.Header{fileHeader("./bar", "helloworld", 0644, defaultTestTime)},
|
||||
checkers: []checker{
|
||||
fileExists("/bar"),
|
||||
fileMatches("/bar", []byte("helloworld")),
|
||||
permissionsMatch("/bar", 0644),
|
||||
timesMatch("/bar", defaultTestTime),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normal file, directory does not exist",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{fileHeader("./foo/bar", "helloworld", 0644)},
|
||||
hdrs: []*tar.Header{fileHeader("./foo/bar", "helloworld", 0644, defaultTestTime)},
|
||||
checkers: []checker{
|
||||
fileExists("/foo/bar"),
|
||||
fileMatches("/foo/bar", []byte("helloworld")),
|
||||
@@ -563,7 +670,7 @@ func TestExtractFile(t *testing.T) {
|
||||
name: "normal file, directory is created after",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{
|
||||
fileHeader("./foo/bar", "helloworld", 0644),
|
||||
fileHeader("./foo/bar", "helloworld", 0644, defaultTestTime),
|
||||
dirHeader("./foo", 0722),
|
||||
},
|
||||
checkers: []checker{
|
||||
@@ -607,7 +714,7 @@ func TestExtractFile(t *testing.T) {
|
||||
name: "hardlink",
|
||||
tmpdir: "/tmp/hardlink",
|
||||
hdrs: []*tar.Header{
|
||||
fileHeader("/bin/gzip", "gzip-binary", 0751),
|
||||
fileHeader("/bin/gzip", "gzip-binary", 0751, defaultTestTime),
|
||||
hardlinkHeader("/bin/uncompress", "/bin/gzip"),
|
||||
},
|
||||
checkers: []checker{
|
||||
@@ -618,7 +725,7 @@ func TestExtractFile(t *testing.T) {
|
||||
{
|
||||
name: "file with setuid bit",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{fileHeader("./bar", "helloworld", 04644)},
|
||||
hdrs: []*tar.Header{fileHeader("./bar", "helloworld", 04644, defaultTestTime)},
|
||||
checkers: []checker{
|
||||
fileExists("/bar"),
|
||||
fileMatches("/bar", []byte("helloworld")),
|
||||
@@ -630,7 +737,7 @@ func TestExtractFile(t *testing.T) {
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{
|
||||
dirHeader("./foo", 01755),
|
||||
fileHeader("./foo/bar", "helloworld", 0644),
|
||||
fileHeader("./foo/bar", "helloworld", 0644, defaultTestTime),
|
||||
},
|
||||
checkers: []checker{
|
||||
fileExists("/foo/bar"),
|
||||
@@ -698,6 +805,9 @@ func TestCopySymlink(t *testing.T) {
|
||||
tc := tc
|
||||
t.Parallel()
|
||||
r, err := ioutil.TempDir("", "")
|
||||
os.MkdirAll(filepath.Join(r, filepath.Dir(tc.linkTarget)), 0777)
|
||||
tc.linkTarget = filepath.Join(r, tc.linkTarget)
|
||||
ioutil.WriteFile(tc.linkTarget, nil, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -811,14 +921,14 @@ func Test_correctDockerignoreFileIsUsed(t *testing.T) {
|
||||
}
|
||||
for _, excl := range tt.args.excluded {
|
||||
t.Run(tt.name+" to exclude "+excl, func(t *testing.T) {
|
||||
if !excludeFile(excl, tt.args.buildcontext) {
|
||||
if !ExcludeFile(excl, tt.args.buildcontext) {
|
||||
t.Errorf("'%v' not excluded", excl)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, incl := range tt.args.included {
|
||||
t.Run(tt.name+" to include "+incl, func(t *testing.T) {
|
||||
if excludeFile(incl, tt.args.buildcontext) {
|
||||
if ExcludeFile(incl, tt.args.buildcontext) {
|
||||
t.Errorf("'%v' not included", incl)
|
||||
}
|
||||
})
|
||||
@@ -863,3 +973,325 @@ func Test_CopyFile_skips_self(t *testing.T) {
|
||||
t.Fatalf("expected file contents to be %q, but got %q", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeExtract(dest string, hdr *tar.Header, tr io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_GetFSFromLayers_with_whiteouts_include_whiteout_enabled(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
root, err := ioutil.TempDir("", "layers-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(root)
|
||||
|
||||
opts := []FSOpt{
|
||||
// I'd rather use the real func (util.ExtractFile)
|
||||
// but you have to be root to chown
|
||||
ExtractFunc(fakeExtract),
|
||||
IncludeWhiteout(),
|
||||
}
|
||||
|
||||
expectErr := false
|
||||
|
||||
f := func(expectedFiles []string, tw *tar.Writer) {
|
||||
for _, f := range expectedFiles {
|
||||
f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: f,
|
||||
Mode: 0644,
|
||||
Size: int64(len("Hello World\n")),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tw.Write([]byte("Hello World\n")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
expectedFiles := []string{
|
||||
filepath.Join(root, "foobar"),
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
|
||||
f(expectedFiles, tw)
|
||||
|
||||
mockLayer := mockv1.NewMockLayer(ctrl)
|
||||
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
|
||||
|
||||
rc := ioutil.NopCloser(buf)
|
||||
mockLayer.EXPECT().Uncompressed().Return(rc, nil)
|
||||
|
||||
secondLayerFiles := []string{
|
||||
filepath.Join(root, ".wh.foobar"),
|
||||
}
|
||||
|
||||
buf = new(bytes.Buffer)
|
||||
tw = tar.NewWriter(buf)
|
||||
|
||||
f(secondLayerFiles, tw)
|
||||
|
||||
mockLayer2 := mockv1.NewMockLayer(ctrl)
|
||||
mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
|
||||
|
||||
rc = ioutil.NopCloser(buf)
|
||||
mockLayer2.EXPECT().Uncompressed().Return(rc, nil)
|
||||
|
||||
layers := []v1.Layer{
|
||||
mockLayer,
|
||||
mockLayer2,
|
||||
}
|
||||
|
||||
expectedFiles = append(expectedFiles, secondLayerFiles...)
|
||||
|
||||
actualFiles, err := GetFSFromLayers(root, layers, opts...)
|
||||
|
||||
assertGetFSFromLayers(
|
||||
t,
|
||||
actualFiles,
|
||||
expectedFiles,
|
||||
err,
|
||||
expectErr,
|
||||
)
|
||||
}
|
||||
|
||||
func Test_GetFSFromLayers_with_whiteouts_include_whiteout_disabled(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
root, err := ioutil.TempDir("", "layers-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(root)
|
||||
|
||||
opts := []FSOpt{
|
||||
// I'd rather use the real func (util.ExtractFile)
|
||||
// but you have to be root to chown
|
||||
ExtractFunc(fakeExtract),
|
||||
}
|
||||
|
||||
expectErr := false
|
||||
|
||||
f := func(expectedFiles []string, tw *tar.Writer) {
|
||||
for _, f := range expectedFiles {
|
||||
f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: f,
|
||||
Mode: 0644,
|
||||
Size: int64(len("Hello world\n")),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tw.Write([]byte("Hello world\n")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
expectedFiles := []string{
|
||||
filepath.Join(root, "foobar"),
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
|
||||
f(expectedFiles, tw)
|
||||
|
||||
mockLayer := mockv1.NewMockLayer(ctrl)
|
||||
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
|
||||
|
||||
rc := ioutil.NopCloser(buf)
|
||||
mockLayer.EXPECT().Uncompressed().Return(rc, nil)
|
||||
|
||||
secondLayerFiles := []string{
|
||||
filepath.Join(root, ".wh.foobar"),
|
||||
}
|
||||
|
||||
buf = new(bytes.Buffer)
|
||||
tw = tar.NewWriter(buf)
|
||||
|
||||
f(secondLayerFiles, tw)
|
||||
|
||||
mockLayer2 := mockv1.NewMockLayer(ctrl)
|
||||
mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
|
||||
|
||||
rc = ioutil.NopCloser(buf)
|
||||
mockLayer2.EXPECT().Uncompressed().Return(rc, nil)
|
||||
|
||||
layers := []v1.Layer{
|
||||
mockLayer,
|
||||
mockLayer2,
|
||||
}
|
||||
|
||||
actualFiles, err := GetFSFromLayers(root, layers, opts...)
|
||||
|
||||
assertGetFSFromLayers(
|
||||
t,
|
||||
actualFiles,
|
||||
expectedFiles,
|
||||
err,
|
||||
expectErr,
|
||||
)
|
||||
}
|
||||
|
||||
func Test_GetFSFromLayers(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
root, err := ioutil.TempDir("", "layers-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(root)
|
||||
|
||||
opts := []FSOpt{
|
||||
// I'd rather use the real func (util.ExtractFile)
|
||||
// but you have to be root to chown
|
||||
ExtractFunc(fakeExtract),
|
||||
}
|
||||
|
||||
expectErr := false
|
||||
expectedFiles := []string{
|
||||
filepath.Join(root, "foobar"),
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
|
||||
for _, f := range expectedFiles {
|
||||
f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: f,
|
||||
Mode: 0644,
|
||||
Size: int64(len("Hello world\n")),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tw.Write([]byte("Hello world\n")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mockLayer := mockv1.NewMockLayer(ctrl)
|
||||
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
|
||||
|
||||
rc := ioutil.NopCloser(buf)
|
||||
mockLayer.EXPECT().Uncompressed().Return(rc, nil)
|
||||
|
||||
layers := []v1.Layer{
|
||||
mockLayer,
|
||||
}
|
||||
|
||||
actualFiles, err := GetFSFromLayers(root, layers, opts...)
|
||||
|
||||
assertGetFSFromLayers(
|
||||
t,
|
||||
actualFiles,
|
||||
expectedFiles,
|
||||
err,
|
||||
expectErr,
|
||||
)
|
||||
}
|
||||
|
||||
func assertGetFSFromLayers(
|
||||
t *testing.T,
|
||||
actualFiles []string,
|
||||
expectedFiles []string,
|
||||
err error,
|
||||
expectErr bool,
|
||||
) {
|
||||
if !expectErr && err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
} else if expectErr && err == nil {
|
||||
t.Error("expected err to not be nil")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(actualFiles) != len(expectedFiles) {
|
||||
t.Errorf("expected %s to equal %s", actualFiles, expectedFiles)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for i := range expectedFiles {
|
||||
if actualFiles[i] != expectedFiles[i] {
|
||||
t.Errorf("expected %s to equal %s", actualFiles[i], expectedFiles[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateWhitelist(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
whitelistVarRun bool
|
||||
expected []WhitelistEntry
|
||||
}{
|
||||
{
|
||||
name: "var/run whitelisted",
|
||||
whitelistVarRun: true,
|
||||
expected: []WhitelistEntry{
|
||||
{
|
||||
Path: "/kaniko",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
{
|
||||
Path: "/etc/mtab",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
{
|
||||
Path: "/var/run",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "var/run not whitelisted",
|
||||
expected: []WhitelistEntry{
|
||||
{
|
||||
Path: "/kaniko",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
{
|
||||
Path: "/etc/mtab",
|
||||
PrefixMatchOnly: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
whitelist = initialWhitelist
|
||||
defer func() { whitelist = initialWhitelist }()
|
||||
UpdateWhitelist(tt.whitelistVarRun)
|
||||
testutil.CheckDeepEqual(t, tt.expected, whitelist)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,12 @@ var (
|
||||
func RetrieveSourceImage(stage config.KanikoStage, opts *config.KanikoOptions) (v1.Image, error) {
|
||||
t := timing.Start("Retrieving Source Image")
|
||||
defer timing.DefaultRun.Stop(t)
|
||||
buildArgs := opts.BuildArgs
|
||||
var metaArgsString []string
|
||||
var buildArgs []string
|
||||
|
||||
for _, arg := range stage.MetaArgs {
|
||||
metaArgsString = append(metaArgsString, fmt.Sprintf("%s=%s", arg.Key, arg.ValueString()))
|
||||
buildArgs = append(buildArgs, fmt.Sprintf("%s=%s", arg.Key, arg.ValueString()))
|
||||
}
|
||||
buildArgs = append(buildArgs, metaArgsString...)
|
||||
buildArgs = append(buildArgs, opts.BuildArgs...)
|
||||
currentBaseName, err := ResolveEnvironmentReplacement(stage.BaseName, buildArgs, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -148,7 +148,10 @@ func remoteOptions(registryName string, opts *config.KanikoOptions) []remote.Opt
|
||||
}
|
||||
}
|
||||
|
||||
return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain())}
|
||||
// on which v1.Platform is this currently running?
|
||||
platform := currentPlatform()
|
||||
|
||||
return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(platform)}
|
||||
}
|
||||
|
||||
func cachedImage(opts *config.KanikoOptions, image string) (v1.Image, error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -29,6 +30,8 @@ import (
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
)
|
||||
|
||||
// ConfigureLogging sets the logrus logging level and forces logs to be colorful (!)
|
||||
@@ -138,3 +141,11 @@ func SHA256(r io.Reader) (string, error) {
|
||||
}
|
||||
return hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), nil
|
||||
}
|
||||
|
||||
// CurrentPlatform returns the v1.Platform on which the code runs
|
||||
func currentPlatform() v1.Platform {
|
||||
return v1.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user