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

Commit 061250f

Browse files
committed
Stop caching COPY layers
Cached COPY layers are expensive in that they both need to be retrieved over the network and occupy space in the layer cache. They are unnecessary in that we already have all resources needed to execute the COPY locally, and doing so is a trivial file-system operation. This is in contrast to RUN layers, which can do arbitrary and unbounded work. The end result is that cached COPY commands were more expensive when cached, not less. Remove them. Resolves #1357
1 parent f20f495 commit 061250f

6 files changed

Lines changed: 3 additions & 280 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ as a remote image destination:
378378
### Caching
379379

380380
#### Caching Layers
381-
kaniko can cache layers created by `RUN` and `COPY` commands in a remote repository.
381+
kaniko can cache layers created by `RUN` commands in a remote repository.
382382
Before executing a command, kaniko checks the cache for the layer.
383383
If it exists, kaniko will pull and extract the cached layer instead of executing the command.
384384
If not, kaniko will execute the command and then push the newly created layer to the cache.

pkg/commands/commands.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ type DockerCommand interface {
5151

5252
RequiresUnpackedFS() bool
5353

54+
// Whether the output layer of this command should be cached in and
55+
// retrieved from the layer cache.
5456
ShouldCacheOutput() bool
5557

5658
// ShouldDetectDeletedFiles returns true if the command could delete files.

pkg/commands/copy.go

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package commands
1818

1919
import (
20-
"fmt"
2120
"os"
2221
"path/filepath"
2322
"strings"
@@ -142,90 +141,10 @@ func (c *CopyCommand) RequiresUnpackedFS() bool {
142141
return true
143142
}
144143

145-
func (c *CopyCommand) ShouldCacheOutput() bool {
146-
return true
147-
}
148-
149-
// CacheCommand returns true since this command should be cached
150-
func (c *CopyCommand) CacheCommand(img v1.Image) DockerCommand {
151-
152-
return &CachingCopyCommand{
153-
img: img,
154-
cmd: c.cmd,
155-
buildcontext: c.buildcontext,
156-
extractFn: util.ExtractFile,
157-
}
158-
}
159-
160144
func (c *CopyCommand) From() string {
161145
return c.cmd.From
162146
}
163147

164-
type CachingCopyCommand struct {
165-
BaseCommand
166-
caching
167-
img v1.Image
168-
extractedFiles []string
169-
cmd *instructions.CopyCommand
170-
buildcontext string
171-
extractFn util.ExtractFunction
172-
}
173-
174-
func (cr *CachingCopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
175-
logrus.Infof("Found cached layer, extracting to filesystem")
176-
var err error
177-
178-
if cr.img == nil {
179-
return errors.New(fmt.Sprintf("cached command image is nil %v", cr.String()))
180-
}
181-
182-
layers, err := cr.img.Layers()
183-
if err != nil {
184-
return errors.Wrapf(err, "retrieve image layers")
185-
}
186-
187-
if len(layers) != 1 {
188-
return errors.New(fmt.Sprintf("expected %d layers but got %d", 1, len(layers)))
189-
}
190-
191-
cr.layer = layers[0]
192-
cr.extractedFiles, err = util.GetFSFromLayers(kConfig.RootDir, layers, util.ExtractFunc(cr.extractFn), util.IncludeWhiteout())
193-
194-
logrus.Debugf("extractedFiles: %s", cr.extractedFiles)
195-
if err != nil {
196-
return errors.Wrap(err, "extracting fs from image")
197-
}
198-
199-
return nil
200-
}
201-
202-
func (cr *CachingCopyCommand) FilesUsedFromContext(config *v1.Config, buildArgs *dockerfile.BuildArgs) ([]string, error) {
203-
return copyCmdFilesUsedFromContext(config, buildArgs, cr.cmd, cr.buildcontext)
204-
}
205-
206-
func (cr *CachingCopyCommand) FilesToSnapshot() []string {
207-
f := cr.extractedFiles
208-
logrus.Debugf("%d files extracted by caching copy command", len(f))
209-
logrus.Tracef("Extracted files: %s", f)
210-
211-
return f
212-
}
213-
214-
func (cr *CachingCopyCommand) MetadataOnly() bool {
215-
return false
216-
}
217-
218-
func (cr *CachingCopyCommand) String() string {
219-
if cr.cmd == nil {
220-
return "nil command"
221-
}
222-
return cr.cmd.String()
223-
}
224-
225-
func (cr *CachingCopyCommand) From() string {
226-
return cr.cmd.From
227-
}
228-
229148
func resolveIfSymlink(destPath string) (string, error) {
230149
if !filepath.IsAbs(destPath) {
231150
return "", errors.New("dest path must be abs")

pkg/commands/copy_test.go

Lines changed: 0 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ limitations under the License.
1616
package commands
1717

1818
import (
19-
"archive/tar"
2019
"fmt"
2120
"io"
2221
"io/ioutil"
@@ -236,159 +235,6 @@ func Test_resolveIfSymlink(t *testing.T) {
236235
}
237236
}
238237

239-
func Test_CachingCopyCommand_ExecuteCommand(t *testing.T) {
240-
tempDir := setupTestTemp()
241-
242-
tarContent, err := prepareTarFixture([]string{"foo.txt"})
243-
if err != nil {
244-
t.Errorf("couldn't prepare tar fixture %v", err)
245-
}
246-
247-
config := &v1.Config{}
248-
buildArgs := &dockerfile.BuildArgs{}
249-
250-
type testCase struct {
251-
desctiption string
252-
expectLayer bool
253-
expectErr bool
254-
count *int
255-
expectedCount int
256-
command *CachingCopyCommand
257-
extractedFiles []string
258-
contextFiles []string
259-
}
260-
testCases := []testCase{
261-
func() testCase {
262-
err = ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("meow"), 0644)
263-
if err != nil {
264-
t.Errorf("couldn't write tempfile %v", err)
265-
t.FailNow()
266-
}
267-
268-
c := &CachingCopyCommand{
269-
img: fakeImage{
270-
ImageLayers: []v1.Layer{
271-
fakeLayer{TarContent: tarContent},
272-
},
273-
},
274-
buildcontext: tempDir,
275-
cmd: &instructions.CopyCommand{
276-
SourcesAndDest: []string{
277-
"foo.txt", "foo.txt",
278-
},
279-
},
280-
}
281-
count := 0
282-
tc := testCase{
283-
desctiption: "with valid image and valid layer",
284-
count: &count,
285-
expectedCount: 1,
286-
expectLayer: true,
287-
extractedFiles: []string{"/foo.txt"},
288-
contextFiles: []string{"foo.txt"},
289-
}
290-
c.extractFn = func(_ string, _ *tar.Header, _ io.Reader) error {
291-
*tc.count++
292-
return nil
293-
}
294-
tc.command = c
295-
return tc
296-
}(),
297-
func() testCase {
298-
c := &CachingCopyCommand{}
299-
tc := testCase{
300-
desctiption: "with no image",
301-
expectErr: true,
302-
}
303-
c.extractFn = func(_ string, _ *tar.Header, _ io.Reader) error {
304-
return nil
305-
}
306-
tc.command = c
307-
return tc
308-
}(),
309-
func() testCase {
310-
c := &CachingCopyCommand{
311-
img: fakeImage{},
312-
}
313-
c.extractFn = func(_ string, _ *tar.Header, _ io.Reader) error {
314-
return nil
315-
}
316-
return testCase{
317-
desctiption: "with image containing no layers",
318-
expectErr: true,
319-
command: c,
320-
}
321-
}(),
322-
func() testCase {
323-
c := &CachingCopyCommand{
324-
img: fakeImage{
325-
ImageLayers: []v1.Layer{
326-
fakeLayer{},
327-
},
328-
},
329-
}
330-
c.extractFn = func(_ string, _ *tar.Header, _ io.Reader) error {
331-
return nil
332-
}
333-
tc := testCase{
334-
desctiption: "with image one layer which has no tar content",
335-
expectErr: false, // this one probably should fail but doesn't because of how ExecuteCommand and util.GetFSFromLayers are implemented - cvgw- 2019-11-25
336-
expectLayer: true,
337-
}
338-
tc.command = c
339-
return tc
340-
}(),
341-
}
342-
343-
for _, tc := range testCases {
344-
t.Run(tc.desctiption, func(t *testing.T) {
345-
c := tc.command
346-
err := c.ExecuteCommand(config, buildArgs)
347-
if !tc.expectErr && err != nil {
348-
t.Errorf("Expected err to be nil but was %v", err)
349-
} else if tc.expectErr && err == nil {
350-
t.Error("Expected err but was nil")
351-
}
352-
353-
if tc.count != nil {
354-
if *tc.count != tc.expectedCount {
355-
t.Errorf("Expected extractFn to be called %v times but was called %v times", tc.expectedCount, *tc.count)
356-
}
357-
for _, file := range tc.extractedFiles {
358-
match := false
359-
cFiles := c.FilesToSnapshot()
360-
for _, cFile := range cFiles {
361-
if file == cFile {
362-
match = true
363-
break
364-
}
365-
}
366-
if !match {
367-
t.Errorf("Expected extracted files to include %v but did not %v", file, cFiles)
368-
}
369-
}
370-
371-
cmdFiles, err := c.FilesUsedFromContext(
372-
config, buildArgs,
373-
)
374-
if err != nil {
375-
t.Errorf("failed to get files used from context from command %v", err)
376-
}
377-
378-
if len(cmdFiles) != len(tc.contextFiles) {
379-
t.Errorf("expected files used from context to equal %v but was %v", tc.contextFiles, cmdFiles)
380-
}
381-
}
382-
383-
if c.layer == nil && tc.expectLayer {
384-
t.Error("expected the command to have a layer set but instead was nil")
385-
} else if c.layer != nil && !tc.expectLayer {
386-
t.Error("expected the command to have no layer set but instead found a layer")
387-
}
388-
})
389-
}
390-
}
391-
392238
func TestCopyCommand_ExecuteCommand_Extended(t *testing.T) {
393239
setupDirs := func(t *testing.T) (string, string) {
394240
testDir, err := ioutil.TempDir("", "")

pkg/executor/build.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,6 @@ func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string
185185
switch v := command.(type) {
186186
case *commands.CopyCommand:
187187
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
188-
case *commands.CachingCopyCommand:
189-
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
190188
}
191189

192190
srcCtx := s.opts.SrcContext

pkg/executor/build_test.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -794,48 +794,6 @@ func Test_stageBuilder_build(t *testing.T) {
794794
retrieve: true,
795795
},
796796
},
797-
func() testcase {
798-
dir, filenames := tempDirAndFile(t)
799-
filename := filenames[0]
800-
filepath := filepath.Join(dir, filename)
801-
802-
tarContent := generateTar(t, dir, filename)
803-
804-
ch := NewCompositeCache("", "")
805-
ch.AddPath(filepath, "")
806-
807-
hash, err := ch.Hash()
808-
if err != nil {
809-
t.Errorf("couldn't create hash %v", err)
810-
}
811-
copyCommandCacheKey := hash
812-
return testcase{
813-
description: "copy command cache enabled and key in cache",
814-
opts: &config.KanikoOptions{Cache: true},
815-
layerCache: &fakeLayerCache{
816-
retrieve: true,
817-
img: fakeImage{
818-
ImageLayers: []v1.Layer{
819-
fakeLayer{
820-
TarContent: tarContent,
821-
},
822-
},
823-
},
824-
},
825-
rootDir: dir,
826-
expectedCacheKeys: []string{copyCommandCacheKey},
827-
// CachingCopyCommand is not pushed to the cache
828-
pushedCacheKeys: []string{},
829-
commands: getCommands(dir, []instructions.Command{
830-
&instructions.CopyCommand{
831-
SourcesAndDest: []string{
832-
filename, "foo.txt",
833-
},
834-
},
835-
}),
836-
fileName: filename,
837-
}
838-
}(),
839797
func() testcase {
840798
dir, filenames := tempDirAndFile(t)
841799
filename := filenames[0]

0 commit comments

Comments
 (0)