More cache cleanups: (#397)

- move the layer cache to an interface
- refactor the DockerCommand implementations to support Cached and non-cached implementations.
This commit is contained in:
dlorenc
2018-11-01 09:11:21 -07:00
committed by GitHub
parent 7afb11cdf7
commit 52a6ce6685
5 changed files with 87 additions and 61 deletions
+15 -5
View File
@@ -31,9 +31,16 @@ import (
"github.com/sirupsen/logrus"
)
// RetrieveLayer checks the specified cache for a layer with the tag :cacheKey
func RetrieveLayer(opts *config.KanikoOptions, cacheKey string) (v1.Image, error) {
cache, err := Destination(opts, cacheKey)
type LayerCache interface {
RetrieveLayer(string) (v1.Image, error)
}
type RegistryCache struct {
Opts *config.KanikoOptions
}
func (rc *RegistryCache) RetrieveLayer(ck string) (v1.Image, error) {
cache, err := Destination(rc.Opts, ck)
if err != nil {
return nil, errors.Wrap(err, "getting cache destination")
}
@@ -52,8 +59,11 @@ func RetrieveLayer(opts *config.KanikoOptions, cacheKey string) (v1.Image, error
if err != nil {
return nil, err
}
_, err = img.Layers()
return img, err
// Force the manifest to be populated
if _, err := img.RawManifest(); err != nil {
return nil, err
}
return img, nil
}
// Destination returns the repo where the layer should be stored
+2 -3
View File
@@ -22,11 +22,10 @@ import (
)
type BaseCommand struct {
cache bool
}
func (b *BaseCommand) CacheCommand() bool {
return b.cache
func (b *BaseCommand) CacheCommand(v1.Image) DockerCommand {
return nil
}
func (b *BaseCommand) FilesToSnapshot() []string {
+4 -3
View File
@@ -24,6 +24,8 @@ import (
"github.com/sirupsen/logrus"
)
type CurrentCacheKey func() (string, error)
type DockerCommand interface {
// ExecuteCommand is responsible for:
// 1. Making required changes to the filesystem (ex. copying files for ADD/COPY or setting ENV variables)
@@ -34,9 +36,8 @@ type DockerCommand interface {
String() string
// A list of files to snapshot, empty for metadata commands or nil if we don't know
FilesToSnapshot() []string
// Return true if this command should be true
// Currently only true for RUN
CacheCommand() bool
// Return a cache-aware implementation of this command, if it exists.
CacheCommand(v1.Image) DockerCommand
// Return true if this command depends on the build context.
FilesUsedFromContext(*v1.Config, *dockerfile.BuildArgs) ([]string, error)
+31 -2
View File
@@ -153,6 +153,35 @@ func (r *RunCommand) FilesToSnapshot() []string {
}
// CacheCommand returns true since this command should be cached
func (r *RunCommand) CacheCommand() bool {
return true
func (r *RunCommand) CacheCommand(img v1.Image) DockerCommand {
return &CachingRunCommand{
img: img,
cmd: r.cmd,
}
}
type CachingRunCommand struct {
BaseCommand
img v1.Image
extractedFiles []string
cmd *instructions.RunCommand
}
func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
logrus.Infof("Found cached layer, extracting to filesystem")
var err error
cr.extractedFiles, err = util.GetFSFromImage(constants.RootDir, cr.img)
if err != nil {
return errors.Wrap(err, "extracting fs from image")
}
return nil
}
func (cr *CachingRunCommand) FilesToSnapshot() []string {
return cr.extractedFiles
}
func (cr *CachingRunCommand) String() string {
return cr.cmd.String()
}
+35 -48
View File
@@ -86,33 +86,6 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage) (*sta
}, nil
}
// extractCachedLayer will extract the cached layer and append it to the config file
func (s *stageBuilder) extractCachedLayer(layer v1.Image, createdBy string) error {
logrus.Infof("Found cached layer, extracting to filesystem")
extractedFiles, err := util.GetFSFromImage(constants.RootDir, layer)
if err != nil {
return errors.Wrap(err, "extracting fs from image")
}
if _, err := s.snapshotter.TakeSnapshot(extractedFiles); err != nil {
return err
}
logrus.Infof("Appending cached layer to base image")
l, err := layer.Layers()
if err != nil {
return errors.Wrap(err, "getting cached layer from image")
}
s.image, err = mutate.Append(s.image,
mutate.Addendum{
Layer: l[0],
History: v1.History{
Author: constants.Author,
CreatedBy: createdBy,
},
},
)
return err
}
func (s *stageBuilder) build() error {
// Unpack file system to root
if _, err := util.GetFSFromImage(constants.RootDir, s.image); err != nil {
@@ -136,6 +109,32 @@ func (s *stageBuilder) build() error {
cmds = append(cmds, command)
}
layerCache := &cache.RegistryCache{
Opts: s.opts,
}
if s.opts.Cache {
// Possibly replace commands with their cached implementations.
for i, command := range cmds {
if command == nil {
continue
}
ck, err := compositeKey.Hash()
if err != nil {
return err
}
img, err := layerCache.RetrieveLayer(ck)
if err != nil {
logrus.Infof("No cached layer found for cmd %s", command.String())
break
}
if cacheCmd := command.CacheCommand(img); cacheCmd != nil {
logrus.Infof("Using caching version of cmd: %s", command.String())
cmds[i] = cacheCmd
}
}
}
args := dockerfile.NewBuildArgs(s.opts.BuildArgs)
for index, command := range cmds {
if command == nil {
@@ -157,22 +156,6 @@ func (s *stageBuilder) build() error {
}
logrus.Info(command.String())
ck, err := compositeKey.Hash()
if err != nil {
return err
}
if command.CacheCommand() && s.opts.Cache {
image, err := cache.RetrieveLayer(s.opts, ck)
if err == nil {
if err := s.extractCachedLayer(image, command.String()); err != nil {
return errors.Wrap(err, "extracting cached layer")
}
continue
}
logrus.Info("No cached layer found, executing command...")
}
if err := command.ExecuteCommand(&s.cf.Config, args); err != nil {
return err
}
@@ -196,7 +179,11 @@ func (s *stageBuilder) build() error {
if err != nil {
return err
}
if err := s.saveSnapshot(command, ck, contents); err != nil {
ck, err := compositeKey.Hash()
if err != nil {
return err
}
if err := s.saveSnapshot(command.String(), ck, contents); err != nil {
return err
}
}
@@ -228,7 +215,7 @@ func (s *stageBuilder) shouldTakeSnapshot(index int, files []string) bool {
return true
}
func (s *stageBuilder) saveSnapshot(command commands.DockerCommand, ck string, contents []byte) error {
func (s *stageBuilder) saveSnapshot(createdBy string, ck string, contents []byte) error {
if contents == nil {
logrus.Info("No files were changed, appending empty layer to config. No layer added to image.")
return nil
@@ -242,8 +229,8 @@ func (s *stageBuilder) saveSnapshot(command commands.DockerCommand, ck string, c
return err
}
// Push layer to cache now along with new config file
if command.CacheCommand() && s.opts.Cache {
if err := pushLayerToCache(s.opts, ck, layer, command.String()); err != nil {
if s.opts.Cache {
if err := pushLayerToCache(s.opts, ck, layer, createdBy); err != nil {
return err
}
}
@@ -252,7 +239,7 @@ func (s *stageBuilder) saveSnapshot(command commands.DockerCommand, ck string, c
Layer: layer,
History: v1.History{
Author: constants.Author,
CreatedBy: command.String(),
CreatedBy: createdBy,
},
},
)