Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't recreate cached layers #715

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions pkg/commands/base_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ import (
)

type BaseCommand struct {
}

func (b *BaseCommand) CacheCommand(v1.Image) DockerCommand {
return nil
cache v1.Image
}

func (b *BaseCommand) FilesToSnapshot() []string {
Expand All @@ -44,6 +41,18 @@ func (b *BaseCommand) RequiresUnpackedFS() bool {
return false
}

func (b *BaseCommand) CacheCommand() DockerCommand {
return nil
}

func (b *BaseCommand) CacheImage() v1.Image {
return b.cache
}

func (b *BaseCommand) SetCacheImage(cache v1.Image) {
b.cache = cache
}

func (b *BaseCommand) ShouldCacheOutput() bool {
return false
}
15 changes: 12 additions & 3 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ type DockerCommand interface {
// A list of files to snapshot, empty for metadata commands or nil if we don't know
FilesToSnapshot() []string

// 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)

Expand All @@ -48,6 +45,18 @@ type DockerCommand interface {
RequiresUnpackedFS() bool

ShouldCacheOutput() bool

// Return an implementation of the command to be executed if the cached layer
// exists. This command will be executed after the layer cache is unpacked,
// so it only needs to contain additional (for example, metadata changes)
// operatons.
CacheCommand() DockerCommand

// Returns a cache image for the layer created by this command, if set
CacheImage() v1.Image

// Sets the cache image for the command's layer
SetCacheImage(cache v1.Image)
}

func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) {
Expand Down
34 changes: 0 additions & 34 deletions pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,6 @@ func (r *RunCommand) FilesToSnapshot() []string {
return nil
}

// CacheCommand returns true since this command should be cached
func (r *RunCommand) CacheCommand(img v1.Image) DockerCommand {

return &CachingRunCommand{
img: img,
cmd: r.cmd,
}
}

func (r *RunCommand) MetadataOnly() bool {
return false
}
Expand All @@ -172,28 +163,3 @@ func (r *RunCommand) RequiresUnpackedFS() bool {
func (r *RunCommand) ShouldCacheOutput() bool {
return true
}

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()
}
2 changes: 1 addition & 1 deletion pkg/commands/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (v *VolumeCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.
existingVolumes[volume] = x
util.AddVolumePathToWhitelist(volume)

// Only create and snapshot the dir if it didn't exist already
// Only create the dir if it didn't exist already
if _, err := os.Stat(volume); os.IsNotExist(err) {
logrus.Infof("Creating directory %s", volume)
if err := os.MkdirAll(volume, 0755); err != nil {
Expand Down
50 changes: 45 additions & 5 deletions pkg/commands/workdir.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ type WorkdirCommand struct {
// For testing
var mkdir = os.MkdirAll

func (w *WorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
logrus.Info("cmd: workdir")
workdirPath := w.cmd.Path
func updateWorkdir(workdirPath string, config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
resolvedWorkingDir, err := util.ResolveEnvironmentReplacement(workdirPath, replacementEnvs, true)
if err != nil {
Expand All @@ -51,14 +49,25 @@ func (w *WorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile
config.WorkingDir = filepath.Join(config.WorkingDir, resolvedWorkingDir)
}
logrus.Infof("Changed working directory to %s", config.WorkingDir)
return nil
}

func (w *WorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
logrus.Info("cmd: workdir")

workdirPath := w.cmd.Path
err := updateWorkdir(workdirPath, config, buildArgs)
if err != nil {
return err
}

// Only create and snapshot the dir if it didn't exist already
w.snapshotFiles = []string{}
if _, err := os.Stat(config.WorkingDir); os.IsNotExist(err) {
logrus.Infof("Creating directory %s", config.WorkingDir)
w.snapshotFiles = append(w.snapshotFiles, config.WorkingDir)
return mkdir(config.WorkingDir, 0755)
}
// Cache the empty layer so we don't have to unpack FS on rerun
w.snapshotFiles = nil
return nil
}

Expand All @@ -75,3 +84,34 @@ func (w *WorkdirCommand) String() string {
func (w *WorkdirCommand) MetadataOnly() bool {
return false
}

func (w *WorkdirCommand) RequiresUnpackedFS() bool {
return true
}

func (w *WorkdirCommand) ShouldCacheOutput() bool {
return true
}

func (w *WorkdirCommand) CacheCommand() DockerCommand {
return &CachedWorkdirCommand{cmd: w.cmd}
}

type CachedWorkdirCommand struct {
BaseCommand
cmd *instructions.WorkdirCommand
}

func (cw *CachedWorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
logrus.Info("cmd: workdir")

return updateWorkdir(cw.cmd.Path, config, buildArgs)
}

func (cw *CachedWorkdirCommand) MetadataOnly() bool {
return true
}

func (cw *CachedWorkdirCommand) String() string {
return cw.cmd.String()
}
4 changes: 2 additions & 2 deletions pkg/commands/workdir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ var workdirTests = []struct {
{
path: "$home",
expectedPath: "/root",
snapshotFiles: []string{},
snapshotFiles: nil,
},
{
path: "/foo/$path/$home",
Expand All @@ -73,7 +73,7 @@ var workdirTests = []struct {
{
path: "/tmp",
expectedPath: "/tmp",
snapshotFiles: []string{},
snapshotFiles: nil,
},
}

Expand Down
Loading