diff --git a/pkg/commands/base_command.go b/pkg/commands/base_command.go index 3d2cd8fad4..b4be3aa40e 100644 --- a/pkg/commands/base_command.go +++ b/pkg/commands/base_command.go @@ -44,6 +44,10 @@ func (b *BaseCommand) RequiresUnpackedFS() bool { return false } +func (b *BaseCommand) CacheImage() v1.Image { + return nil +} + func (b *BaseCommand) ShouldCacheOutput() bool { return false } diff --git a/pkg/commands/commands.go b/pkg/commands/commands.go index 2615138be4..3d82db6699 100644 --- a/pkg/commands/commands.go +++ b/pkg/commands/commands.go @@ -40,6 +40,9 @@ type DockerCommand interface { // Return a cache-aware implementation of this command, if it exists. CacheCommand(v1.Image) DockerCommand + // Return an image with cached layer for this command, if it exists + CacheImage() v1.Image + // Return true if this command depends on the build context. FilesUsedFromContext(*v1.Config, *dockerfile.BuildArgs) ([]string, error) diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 7a9bf03dc2..6675034290 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -190,6 +190,10 @@ func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *docker return nil } +func (cr *CachingRunCommand) CacheImage() v1.Image { + return cr.img +} + func (cr *CachingRunCommand) FilesToSnapshot() []string { return cr.extractedFiles } diff --git a/pkg/executor/build.go b/pkg/executor/build.go index 6626577cf9..14b734d1f1 100644 --- a/pkg/executor/build.go +++ b/pkg/executor/build.go @@ -222,6 +222,7 @@ func (s *stageBuilder) build() error { if err := util.DetectFilesystemWhitelist(constants.WhitelistPath); err != nil { return err } + // Take initial snapshot t := timing.Start("Initial FS snapshot") if err := s.snapshotter.Init(); err != nil { @@ -253,9 +254,20 @@ func (s *stageBuilder) build() error { if err := command.ExecuteCommand(&s.cf.Config, s.args); err != nil { return err } - files = command.FilesToSnapshot() timing.DefaultRun.Stop(t) + cache_img := command.CacheImage() + if cache_img != nil { + layers, err := cache_img.Layers() + if err != nil { + logrus.Debugf("Failed to retrieve layer from cache image: %s", err) + continue + } + s.addLayerToImage(command.String(), layers[0]) + continue + } + + files = command.FilesToSnapshot() if !s.shouldTakeSnapshot(index, files) { continue } @@ -269,6 +281,7 @@ func (s *stageBuilder) build() error { if err != nil { return err } + // Push layer to cache (in parallel) now along with new config file if s.opts.Cache && command.ShouldCacheOutput() { cacheGroup.Go(func() error { @@ -326,6 +339,21 @@ func (s *stageBuilder) shouldTakeSnapshot(index int, files []string) bool { return true } +func (s *stageBuilder) addLayerToImage(createdBy string, layer v1.Layer) error { + logrus.Infof("Adding cached layer for command %s to the image", createdBy) + var err error + s.image, err = mutate.Append(s.image, + mutate.Addendum{ + Layer: layer, + History: v1.History{ + Author: constants.Author, + CreatedBy: createdBy, + }, + }, + ) + return err +} + func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) error { if tarPath == "" { return nil @@ -343,6 +371,7 @@ func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) err if err != nil { return err } + s.image, err = mutate.Append(s.image, mutate.Addendum{ Layer: layer, @@ -353,7 +382,6 @@ func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) err }, ) return err - } func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error) {