mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-21 22:56:30 +02:00
Merge branch 'master' into registry-mirror
This commit is contained in:
+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