Skip to content

Commit

Permalink
Fix snapshotter ignore list; do not attempt to delete whiteouts of ig…
Browse files Browse the repository at this point in the history
…nored paths (#1652)

* avoid deleting whiteouts if they are in ignore list

* fix snapshotter ignore list

* include filesystem mounts in ignorelist of snapshotter
* clean up ignore list logic

* add unit and integration tests for #1652

* fix tests and ignore list updates
  • Loading branch information
kamaln7 authored Jun 4, 2021
1 parent 57ea150 commit f21639d
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 117 deletions.
14 changes: 12 additions & 2 deletions cmd/executor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,19 @@ var RootCmd = &cobra.Command{
return errors.New("You must provide --destination if setting ImageNameTagDigestFile")
}
// Update ignored paths
util.UpdateInitialIgnoreList(opts.IgnoreVarRun)
if opts.IgnoreVarRun {
// /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.
logrus.Trace("Adding /var/run to default ignore list")
util.AddToDefaultIgnoreList(util.IgnoreListEntry{
Path: "/var/run",
PrefixMatchOnly: false,
})
}
for _, p := range opts.IgnorePaths {
util.AddToBaseIgnoreList(util.IgnoreListEntry{
util.AddToDefaultIgnoreList(util.IgnoreListEntry{
Path: p,
PrefixMatchOnly: false,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM alpine@sha256:def822f9851ca422481ec6fee59a9966f12b351c62ccb9aca841526ffaa9f748
RUN ln -s /dev/null /hello
37 changes: 29 additions & 8 deletions integration/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,38 @@ var additionalDockerFlagsMap = map[string][]string{

// Arguments to build Dockerfiles with when building with kaniko
var additionalKanikoFlagsMap = map[string][]string{
"Dockerfile_test_add": {"--single-snapshot"},
"Dockerfile_test_run_new": {"--use-new-run=true"},
"Dockerfile_test_run_redo": {"--snapshotMode=redo"},
"Dockerfile_test_scratch": {"--single-snapshot"},
"Dockerfile_test_maintainer": {"--single-snapshot"},
"Dockerfile_test_target": {"--target=second"},
"Dockerfile_test_add": {"--single-snapshot"},
"Dockerfile_test_run_new": {"--use-new-run=true"},
"Dockerfile_test_run_redo": {"--snapshotMode=redo"},
"Dockerfile_test_scratch": {"--single-snapshot"},
"Dockerfile_test_maintainer": {"--single-snapshot"},
"Dockerfile_test_target": {"--target=second"},
"Dockerfile_test_snapshotter_ignorelist": {"--use-new-run=true", "-v=debug"},
}

// output check to do when building with kaniko
var outputChecks = map[string]func(string, []byte) error{
"Dockerfile_test_arg_secret": checkArgsNotPrinted,
"Dockerfile_test_snapshotter_ignorelist": func(_ string, out []byte) error {
for _, s := range []string{
"Adding whiteout for /dev",
} {
if strings.Contains(string(out), s) {
return fmt.Errorf("output must not contain %s", s)
}
}

for _, s := range []string{
"resolved symlink /hello to /dev/null",
"path /dev/null is ignored, ignoring it",
} {
if !strings.Contains(string(out), s) {
return fmt.Errorf("output must contain %s", s)
}
}

return nil
},
}

// Checks if argument are not printed in output.
Expand Down Expand Up @@ -450,11 +471,11 @@ func buildKanikoImage(

out, err := RunCommandWithoutTest(kanikoCmd)
if err != nil {
return "", fmt.Errorf("Failed to build image %s with kaniko command \"%s\": %s %s", kanikoImage, kanikoCmd.Args, err, string(out))
return "", fmt.Errorf("Failed to build image %s with kaniko command \"%s\": %s\n%s", kanikoImage, kanikoCmd.Args, err, string(out))
}
if outputCheck := outputChecks[dockerfile]; outputCheck != nil {
if err := outputCheck(dockerfile, out); err != nil {
return "", fmt.Errorf("Output check failed for image %s with kaniko command : %s %s", kanikoImage, err, string(out))
return "", fmt.Errorf("Output check failed for image %s with kaniko command : %s\n%s", kanikoImage, err, string(out))
}
}
return benchmarkDir, nil
Expand Down
9 changes: 5 additions & 4 deletions pkg/executor/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
return nil, err
}

err = util.InitIgnoreList(true)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize ignore list")
}

hasher, err := getHasher(opts.SnapshotMode)
if err != nil {
return nil, err
Expand Down Expand Up @@ -311,10 +316,6 @@ func (s *stageBuilder) build() error {
logrus.Info("Skipping unpacking as no commands require it.")
}

if err := util.DetectFilesystemIgnoreList(config.IgnoreListPath); err != nil {
return errors.Wrap(err, "failed to check filesystem mount paths")
}

initSnapshotTaken := false
if s.opts.SingleSnapshot || s.opts.RunV2 {
if err := s.initSnapshotWithTimings(); err != nil {
Expand Down
5 changes: 4 additions & 1 deletion pkg/filesystem/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,13 @@ func ResolvePaths(paths []string, wl []util.IgnoreListEntry) (pathsToAdd []strin
logrus.Debugf("symlink path %s, target does not exist", f)
continue
}
if f != evaled {
logrus.Debugf("resolved symlink %s to %s", f, evaled)
}

// If the given path is a symlink and the target is part of the ignorelist
// ignore the target
if util.IsInProvidedIgnoreList(evaled, wl) {
if util.CheckProvidedIgnoreList(evaled, wl) {
logrus.Debugf("path %s is ignored, ignoring it", evaled)
continue
}
Expand Down
80 changes: 47 additions & 33 deletions pkg/util/fs_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ var defaultIgnoreList = []IgnoreListEntry{
},
}

var baseIgnoreList = defaultIgnoreList
var ignorelist = baseIgnoreList
var ignorelist = append([]IgnoreListEntry{}, defaultIgnoreList...)

var volumes = []string{}

Expand All @@ -101,8 +100,8 @@ func AddToIgnoreList(entry IgnoreListEntry) {
ignorelist = append(ignorelist, entry)
}

func AddToBaseIgnoreList(entry IgnoreListEntry) {
baseIgnoreList = append(baseIgnoreList, entry)
func AddToDefaultIgnoreList(entry IgnoreListEntry) {
defaultIgnoreList = append(defaultIgnoreList, entry)
}

func IncludeWhiteout() FSOpt {
Expand Down Expand Up @@ -133,7 +132,12 @@ func GetFSFromImage(root string, img v1.Image, extract ExtractFunction) ([]strin
}

func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, error) {
volumes = []string{}
cfg := new(FSConfig)
if err := InitIgnoreList(true); err != nil {
return nil, errors.Wrap(err, "initializing filesystem ignore list")
}
logrus.Debugf("Ignore list: %v", ignorelist)

for _, opt := range opts {
opt(cfg)
Expand All @@ -143,12 +147,6 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
return nil, errors.New("must supply an extract function")
}

if err := DetectFilesystemIgnoreList(config.IgnoreListPath); err != nil {
return nil, err
}

logrus.Debugf("Mounted directories: %v", ignorelist)

extractedFiles := []string{}
for i, l := range layers {
if mediaType, err := l.MediaType(); err == nil {
Expand Down Expand Up @@ -182,7 +180,18 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
logrus.Debugf("Whiting out %s", path)

name := strings.TrimPrefix(base, ".wh.")
if err := os.RemoveAll(filepath.Join(dir, name)); err != nil {
path := filepath.Join(dir, name)

if CheckIgnoreList(path) {
logrus.Debugf("Not deleting %s, as it's ignored", path)
continue
}
if childDirInIgnoreList(path) {
logrus.Debugf("Not deleting %s, as it contains a ignored path", path)
continue
}

if err := os.RemoveAll(path); err != nil {
return nil, errors.Wrapf(err, "removing whiteout %s", hdr.Name)
}

Expand Down Expand Up @@ -382,20 +391,21 @@ func ExtractFile(dest string, hdr *tar.Header, tr io.Reader) error {
return nil
}

func IsInIgnoreList(path string) bool {
return IsInProvidedIgnoreList(path, ignorelist)
}

func IsInProvidedIgnoreList(path string, wl []IgnoreListEntry) bool {
for _, entry := range wl {
if !entry.PrefixMatchOnly && path == entry.Path {
return true
}
}

return false
}

func CheckIgnoreList(path string) bool {
func IsInIgnoreList(path string) bool {
return IsInProvidedIgnoreList(path, ignorelist)
}

func CheckProvidedIgnoreList(path string, wl []IgnoreListEntry) bool {
for _, wl := range ignorelist {
if HasFilepathPrefix(path, wl.Path, wl.PrefixMatchOnly) {
return true
Expand All @@ -405,6 +415,10 @@ func CheckIgnoreList(path string) bool {
return false
}

func CheckIgnoreList(path string) bool {
return CheckProvidedIgnoreList(path, ignorelist)
}

func checkIgnoreListRoot(root string) bool {
if root == config.RootDir {
return false
Expand All @@ -419,8 +433,7 @@ func checkIgnoreListRoot(root string) bool {
// Where (5) is the mount point relative to the process's root
// From: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
func DetectFilesystemIgnoreList(path string) error {
ignorelist = baseIgnoreList
volumes = []string{}
logrus.Trace("Detecting filesystem ignore list")
f, err := os.Open(path)
if err != nil {
return err
Expand All @@ -442,7 +455,7 @@ func DetectFilesystemIgnoreList(path string) error {
continue
}
if lineArr[4] != config.RootDir {
logrus.Tracef("Appending %s from line: %s", lineArr[4], line)
logrus.Tracef("Adding ignore list entry %s from line: %s", lineArr[4], line)
ignorelist = append(ignorelist, IgnoreListEntry{
Path: lineArr[4],
PrefixMatchOnly: false,
Expand Down Expand Up @@ -898,19 +911,20 @@ func createParentDirectory(path string) error {
return nil
}

// UpdateInitialIgnoreList will add /var/run to ignored paths if
func UpdateInitialIgnoreList(ignoreVarRun bool) {
if !ignoreVarRun {
return
}
logrus.Trace("Adding /var/run to initialIgnoreList ")
baseIgnoreList = append(baseIgnoreList, IgnoreListEntry{
// /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,
})
// InitIgnoreList will initialize the ignore list using:
// - defaultIgnoreList
// - mounted paths via DetectFilesystemIgnoreList()
func InitIgnoreList(detectFilesystem bool) error {
logrus.Trace("Initializing ignore list")
ignorelist = append([]IgnoreListEntry{}, defaultIgnoreList...)

if detectFilesystem {
if err := DetectFilesystemIgnoreList(config.IgnoreListPath); err != nil {
return errors.Wrap(err, "checking filesystem mount paths for ignore list")
}
}

return nil
}

type walkFSResult struct {
Expand Down Expand Up @@ -985,7 +999,7 @@ func GetFSInfoMap(dir string, existing map[string]os.FileInfo) (map[string]os.Fi
timer := timing.Start("Walking filesystem with Stat")
godirwalk.Walk(dir, &godirwalk.Options{
Callback: func(path string, ent *godirwalk.Dirent) error {
if IsInIgnoreList(path) {
if CheckIgnoreList(path) {
if IsDestDir(path) {
logrus.Tracef("Skipping paths under %s, as it is a ignored directory", path)
return filepath.SkipDir
Expand Down
Loading

0 comments on commit f21639d

Please sign in to comment.