Merge branch 'master' into registry-mirror

This commit is contained in:
Tejal Desai
2020-01-29 11:42:06 -08:00
committed by GitHub
1221 changed files with 110154 additions and 38757 deletions
+56 -26
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+3 -4
View File
@@ -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
}
+13 -7
View File
@@ -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{
+6 -3
View File
@@ -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.
+9 -2
View File
@@ -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
View File
@@ -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
+9 -9
View File
@@ -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{
+23 -6
View File
@@ -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
}
+435 -2
View File
@@ -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))
}
+26 -5
View File
@@ -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
}
+95 -20
View File
@@ -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
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
@@ -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++
+3 -3
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",
+158 -36
View File
@@ -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
View File
@@ -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)
})
}
}
+8 -5
View File
@@ -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) {
+11
View File
@@ -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,
}
}