diff --git a/pkg/commands/workdir.go b/pkg/commands/workdir.go index ef41f8beb..4e93d6f70 100644 --- a/pkg/commands/workdir.go +++ b/pkg/commands/workdir.go @@ -88,11 +88,7 @@ func (w *WorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile // FilesToSnapshot returns the workingdir, which should have been created if it didn't already exist func (w *WorkdirCommand) FilesToSnapshot() []string { - return nil -} - -func (r *WorkdirCommand) ProvidesFilesToSnapshot() bool { - return false + return w.snapshotFiles } // String returns some information about the command for the image config history @@ -154,8 +150,11 @@ func (wr *CachingWorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *do return errors.Wrap(err, "retrieving image layers") } - if len(layers) != 1 { + if len(layers) > 1 { return errors.New(fmt.Sprintf("expected %d layers but got %d", 1, len(layers))) + } else if len(layers) == 0 { + // an empty image in cache indicates that no directory was created by WORKDIR + return nil } wr.layer = layers[0] diff --git a/pkg/executor/build.go b/pkg/executor/build.go index e693aa901..16c5dccc0 100644 --- a/pkg/executor/build.go +++ b/pkg/executor/build.go @@ -414,11 +414,13 @@ func (s *stageBuilder) build() error { continue } if isCacheCommand { - if files != nil && len(files) == 0 { + v := command.(commands.Cached) + layer := v.Layer() + if (files != nil || layer == nil) && len(files) == 0 { + // a cache image with a layer with no files indicates that no files were changed, ie. by 'RUN echo hello' + // a cache image without a layer indicates that no files were changed too, ie. by 'WORKDIR /' logrus.Info("No files were changed, appending empty layer to config. No layer added to image.") } else { - v := command.(commands.Cached) - layer := v.Layer() if err := s.saveLayerToImage(layer, command.String()); err != nil { return errors.Wrap(err, "failed to save layer") } diff --git a/pkg/executor/push.go b/pkg/executor/push.go index 2580e314c..3d98b220e 100644 --- a/pkg/executor/push.go +++ b/pkg/executor/push.go @@ -369,11 +369,6 @@ func pushLayerToCache(opts *config.KanikoOptions, cacheKey string, tarPath strin // layer already gzipped by default } - layer, err := tarball.LayerFromFile(tarPath, layerOpts...) - if err != nil { - return err - } - cache, err := cache.Destination(opts, cacheKey) if err != nil { return errors.Wrap(err, "getting cache destination") @@ -385,18 +380,27 @@ func pushLayerToCache(opts *config.KanikoOptions, cacheKey string, tarPath strin return errors.Wrap(err, "setting empty image created time") } - empty, err = mutate.Append(empty, - mutate.Addendum{ - Layer: layer, - History: v1.History{ - Author: constants.Author, - CreatedBy: createdBy, + // WORKDIR can create empty layers by design, yet still we must cache them + // to transfer the knowledge that they are empty. + if tarPath != "" { + layer, err := tarball.LayerFromFile(tarPath, layerOpts...) + if err != nil { + return err + } + empty, err = mutate.Append(empty, + mutate.Addendum{ + Layer: layer, + History: v1.History{ + Author: constants.Author, + CreatedBy: createdBy, + }, }, - }, - ) - if err != nil { - return errors.Wrap(err, "appending layer onto empty image") + ) + if err != nil { + return errors.Wrap(err, "appending layer onto empty image") + } } + cacheOpts := *opts cacheOpts.TarPath = "" // tarPath doesn't make sense for Docker layers cacheOpts.NoPush = opts.NoPushCache // we do not want to push cache if --no-push-cache is set.