Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit f8507eb

Browse files
authored
Merge pull request #883 from cvgw/u/cvgw/include_source_stage_digest_in_cache_key_copy_from_commands
Include source stage cache key in cache key for COPY commands using --from
2 parents acb5b9f + b19214a commit f8507eb

5 files changed

Lines changed: 102 additions & 42 deletions

File tree

pkg/commands/copy.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ func (c *CopyCommand) CacheCommand(img v1.Image) DockerCommand {
161161
}
162162
}
163163

164+
func (c *CopyCommand) From() string {
165+
return c.cmd.From
166+
}
167+
164168
type CachingCopyCommand struct {
165169
BaseCommand
166170
img v1.Image
@@ -187,6 +191,10 @@ func (cr *CachingCopyCommand) String() string {
187191
return cr.cmd.String()
188192
}
189193

194+
func (cr *CachingCopyCommand) From() string {
195+
return cr.cmd.From
196+
}
197+
190198
func resolveIfSymlink(destPath string) (string, error) {
191199
baseDir := filepath.Dir(destPath)
192200
if info, err := os.Lstat(baseDir); err == nil {

pkg/config/stage.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ limitations under the License.
1616

1717
package config
1818

19-
import "github.com/moby/buildkit/frontend/dockerfile/instructions"
19+
import (
20+
"github.com/moby/buildkit/frontend/dockerfile/instructions"
21+
)
2022

2123
// KanikoStage wraps a stage of the Dockerfile and provides extra information
2224
type KanikoStage struct {

pkg/executor/build.go

Lines changed: 87 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,24 @@ type snapShotter interface {
6161

6262
// stageBuilder contains all fields necessary to build one stage of a Dockerfile
6363
type stageBuilder struct {
64-
stage config.KanikoStage
65-
image v1.Image
66-
cf *v1.ConfigFile
67-
snapshotter snapShotter
68-
layerCache cache.LayerCache
69-
pushCache cachePusher
70-
baseImageDigest string
71-
finalCacheKey string
72-
opts *config.KanikoOptions
73-
cmds []commands.DockerCommand
74-
args *dockerfile.BuildArgs
75-
crossStageDeps map[int][]string
76-
digestToCacheKeyMap map[string]string
64+
stage config.KanikoStage
65+
image v1.Image
66+
cf *v1.ConfigFile
67+
baseImageDigest string
68+
finalCacheKey string
69+
opts *config.KanikoOptions
70+
cmds []commands.DockerCommand
71+
args *dockerfile.BuildArgs
72+
crossStageDeps map[int][]string
73+
digestToCacheKey map[string]string
74+
stageIdxToDigest map[string]string
75+
snapshotter snapShotter
76+
layerCache cache.LayerCache
77+
pushCache cachePusher
7778
}
7879

7980
// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
80-
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string) (*stageBuilder, error) {
81+
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string, sid map[string]string) (*stageBuilder, error) {
8182
sourceImage, err := util.RetrieveSourceImage(stage, opts)
8283
if err != nil {
8384
return nil, err
@@ -104,14 +105,15 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
104105
return nil, err
105106
}
106107
s := &stageBuilder{
107-
stage: stage,
108-
image: sourceImage,
109-
cf: imageConfig,
110-
snapshotter: snapshotter,
111-
baseImageDigest: digest.String(),
112-
opts: opts,
113-
crossStageDeps: crossStageDeps,
114-
digestToCacheKeyMap: dcm,
108+
stage: stage,
109+
image: sourceImage,
110+
cf: imageConfig,
111+
snapshotter: snapshotter,
112+
baseImageDigest: digest.String(),
113+
opts: opts,
114+
crossStageDeps: crossStageDeps,
115+
digestToCacheKey: dcm,
116+
stageIdxToDigest: sid,
115117
layerCache: &cache.RegistryCache{
116118
Opts: opts,
117119
},
@@ -146,6 +148,40 @@ func initializeConfig(img partial.WithConfigFile) (*v1.ConfigFile, error) {
146148
return imageConfig, nil
147149
}
148150

151+
func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string, compositeKey CompositeCache) (CompositeCache, error) {
152+
// Add the next command to the cache key.
153+
compositeKey.AddKey(command.String())
154+
switch v := command.(type) {
155+
case *commands.CopyCommand:
156+
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
157+
case *commands.CachingCopyCommand:
158+
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
159+
}
160+
161+
for _, f := range files {
162+
if err := compositeKey.AddPath(f); err != nil {
163+
return compositeKey, err
164+
}
165+
}
166+
return compositeKey, nil
167+
}
168+
169+
func (s *stageBuilder) populateCopyCmdCompositeKey(command fmt.Stringer, from string, compositeKey CompositeCache) CompositeCache {
170+
if from != "" {
171+
digest, ok := s.stageIdxToDigest[from]
172+
if ok {
173+
ds := digest
174+
cacheKey, ok := s.digestToCacheKey[ds]
175+
if ok {
176+
logrus.Debugf("adding digest %v from previous stage to composite key for %v", ds, command.String())
177+
compositeKey.AddKey(cacheKey)
178+
}
179+
}
180+
}
181+
182+
return compositeKey
183+
}
184+
149185
func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) error {
150186
if !s.opts.Cache {
151187
return nil
@@ -160,22 +196,22 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) erro
160196
if command == nil {
161197
continue
162198
}
163-
compositeKey.AddKey(command.String())
164-
// If the command uses files from the context, add them.
165199
files, err := command.FilesUsedFromContext(&cfg, s.args)
166200
if err != nil {
167201
return errors.Wrap(err, "failed to get files used from context")
168202
}
169-
for _, f := range files {
170-
if err := compositeKey.AddPath(f); err != nil {
171-
return errors.Wrap(err, "failed to add path to composite key")
172-
}
203+
204+
compositeKey, err = s.populateCompositeKey(command, files, compositeKey)
205+
if err != nil {
206+
return err
173207
}
174208

209+
logrus.Debugf("optimize: composite key for command %v %v", command.String(), compositeKey)
175210
ck, err := compositeKey.Hash()
176211
if err != nil {
177212
return errors.Wrap(err, "failed to hash composite key")
178213
}
214+
logrus.Debugf("optimize: cache key for command %v %v", command.String(), ck)
179215
s.finalCacheKey = ck
180216
if command.ShouldCacheOutput() && !stopCache {
181217
img, err := s.layerCache.RetrieveLayer(ck)
@@ -206,7 +242,7 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) erro
206242
func (s *stageBuilder) build() error {
207243
// Set the initial cache key to be the base image digest, the build args and the SrcContext.
208244
var compositeKey *CompositeCache
209-
if cacheKey, ok := s.digestToCacheKeyMap[s.baseImageDigest]; ok {
245+
if cacheKey, ok := s.digestToCacheKey[s.baseImageDigest]; ok {
210246
compositeKey = NewCompositeCache(cacheKey)
211247
} else {
212248
compositeKey = NewCompositeCache(s.baseImageDigest)
@@ -256,19 +292,19 @@ func (s *stageBuilder) build() error {
256292
continue
257293
}
258294

259-
// Add the next command to the cache key.
260-
compositeKey.AddKey(command.String())
261295
t := timing.Start("Command: " + command.String())
296+
262297
// If the command uses files from the context, add them.
263298
files, err := command.FilesUsedFromContext(&s.cf.Config, s.args)
264299
if err != nil {
265300
return errors.Wrap(err, "failed to get files used from context")
266301
}
267-
for _, f := range files {
268-
if err := compositeKey.AddPath(f); err != nil {
269-
return errors.Wrap(err, fmt.Sprintf("failed to add path to composite key %v", f))
270-
}
302+
303+
*compositeKey, err = s.populateCompositeKey(command, files, *compositeKey)
304+
if err != nil {
305+
return err
271306
}
307+
272308
logrus.Info(command.String())
273309

274310
if err := command.ExecuteCommand(&s.cf.Config, s.args); err != nil {
@@ -286,6 +322,7 @@ func (s *stageBuilder) build() error {
286322
return errors.Wrap(err, "failed to take snapshot")
287323
}
288324

325+
logrus.Debugf("build: composite key for command %v %v", command.String(), compositeKey)
289326
ck, err := compositeKey.Hash()
290327
if err != nil {
291328
return errors.Wrap(err, "failed to hash composite key")
@@ -303,6 +340,7 @@ func (s *stageBuilder) build() error {
303340
if err := cacheGroup.Wait(); err != nil {
304341
logrus.Warnf("error uploading layer to cache: %s", err)
305342
}
343+
306344
return nil
307345
}
308346

@@ -374,7 +412,6 @@ func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) err
374412
},
375413
)
376414
return err
377-
378415
}
379416

380417
func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error) {
@@ -442,7 +479,9 @@ func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error)
442479
// DoBuild executes building the Dockerfile
443480
func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
444481
t := timing.Start("Total Build Time")
445-
digestToCacheKeyMap := make(map[string]string)
482+
digestToCacheKey := make(map[string]string)
483+
stageIdxToDigest := make(map[string]string)
484+
446485
// Parse dockerfile and unpack base image to root
447486
stages, err := dockerfile.Stages(opts)
448487
if err != nil {
@@ -463,23 +502,32 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
463502
logrus.Infof("Built cross stage deps: %v", crossStageDependencies)
464503

465504
for index, stage := range stages {
466-
sb, err := newStageBuilder(opts, stage, crossStageDependencies, digestToCacheKeyMap)
505+
sb, err := newStageBuilder(opts, stage, crossStageDependencies, digestToCacheKey, stageIdxToDigest)
467506
if err != nil {
468507
return nil, err
469508
}
470509
if err := sb.build(); err != nil {
471510
return nil, errors.Wrap(err, "error building stage")
472511
}
512+
473513
reviewConfig(stage, &sb.cf.Config)
514+
474515
sourceImage, err := mutate.Config(sb.image, sb.cf.Config)
475516
if err != nil {
476517
return nil, err
477518
}
519+
478520
d, err := sourceImage.Digest()
479521
if err != nil {
480522
return nil, err
481523
}
482-
digestToCacheKeyMap[d.String()] = sb.finalCacheKey
524+
525+
stageIdxToDigest[fmt.Sprintf("%d", sb.stage.Index)] = d.String()
526+
logrus.Debugf("mapping stage idx %v to digest %v", sb.stage.Index, d.String())
527+
528+
digestToCacheKey[d.String()] = sb.finalCacheKey
529+
logrus.Debugf("mapping digest %v to cachekey %v", d.String(), sb.finalCacheKey)
530+
483531
if stage.Final {
484532
sourceImage, err = mutate.CreatedAt(sourceImage, v1.Time{Time: time.Now()})
485533
if err != nil {

pkg/executor/build_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ func tempDirAndFile(t *testing.T) (string, []string) {
888888

889889
return dir, filenames
890890
}
891+
891892
func generateTar(t *testing.T, dir string, fileNames ...string) []byte {
892893
buf := bytes.NewBuffer([]byte{})
893894
writer := tar.NewWriter(buf)

pkg/executor/composite_cache.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package executor
1818

1919
import (
2020
"crypto/sha256"
21+
"fmt"
2122
"os"
2223
"path/filepath"
2324
"strings"
@@ -75,7 +76,7 @@ func (s *CompositeCache) AddPath(p string) error {
7576
return err
7677
}
7778

78-
s.keys = append(s.keys, string(sha.Sum(nil)))
79+
s.keys = append(s.keys, fmt.Sprintf("%x", sha.Sum(nil)))
7980
return nil
8081
}
8182

@@ -98,5 +99,5 @@ func HashDir(p string) (string, error) {
9899
return "", err
99100
}
100101

101-
return string(sha.Sum(nil)), nil
102+
return fmt.Sprintf("%x", sha.Sum(nil)), nil
102103
}

0 commit comments

Comments
 (0)