Fix multistage caching with COPY --from (#2559)
* Removed block on use --cache-copy-layers with multistage builds * Removed using digest in composite key with command COPY --from * COPY --from command uses src as file context (only changed files will be reason for change hash) * ARG and ENV changed before COPY dont change composite key * Add and fix some tests * Caching work same as caching in docker buildx Co-authored-by: Sergei Kraev <skraev@tradingview.com>
This commit is contained in:
parent
de3032f982
commit
eea12bd025
|
|
@ -24,6 +24,10 @@ import (
|
||||||
type BaseCommand struct {
|
type BaseCommand struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BaseCommand) CacheCommand(v1.Image) DockerCommand {
|
func (b *BaseCommand) CacheCommand(v1.Image) DockerCommand {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ type DockerCommand interface {
|
||||||
|
|
||||||
// ShouldDetectDeletedFiles returns true if the command could delete files.
|
// ShouldDetectDeletedFiles returns true if the command could delete files.
|
||||||
ShouldDetectDeletedFiles() bool
|
ShouldDetectDeletedFiles() bool
|
||||||
|
|
||||||
|
// True if need add ARGs and EVNs to composite cache string with resolved command
|
||||||
|
// need only for RUN instruction
|
||||||
|
IsArgsEnvsRequiredInCache() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommand(cmd instructions.Command, fileContext util.FileContext, useNewRun bool, cacheCopy bool, cacheRun bool) (DockerCommand, error) {
|
func GetCommand(cmd instructions.Command, fileContext util.FileContext, useNewRun bool, cacheCopy bool, cacheRun bool) (DockerCommand, error) {
|
||||||
|
|
|
||||||
|
|
@ -270,9 +270,8 @@ func copyCmdFilesUsedFromContext(
|
||||||
config *v1.Config, buildArgs *dockerfile.BuildArgs, cmd *instructions.CopyCommand,
|
config *v1.Config, buildArgs *dockerfile.BuildArgs, cmd *instructions.CopyCommand,
|
||||||
fileContext util.FileContext,
|
fileContext util.FileContext,
|
||||||
) ([]string, error) {
|
) ([]string, error) {
|
||||||
// We don't use the context if we're performing a copy --from.
|
|
||||||
if cmd.From != "" {
|
if cmd.From != "" {
|
||||||
return nil, nil
|
fileContext = util.FileContext{Root: filepath.Join(kConfig.KanikoDir, cmd.From)}
|
||||||
}
|
}
|
||||||
|
|
||||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ var (
|
||||||
userLookup = util.LookupUser
|
userLookup = util.LookupUser
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (r *RunCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||||
return runCommandInExec(config, buildArgs, r.cmd)
|
return runCommandInExec(config, buildArgs, r.cmd)
|
||||||
}
|
}
|
||||||
|
|
@ -200,6 +204,10 @@ type CachingRunCommand struct {
|
||||||
extractFn util.ExtractFunction
|
extractFn util.ExtractFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cr *CachingRunCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
func (cr *CachingRunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||||
logrus.Infof("Found cached layer, extracting to filesystem")
|
logrus.Infof("Found cached layer, extracting to filesystem")
|
||||||
var err error
|
var err error
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ func (r *RunMarkerCommand) ProvidesFilesToSnapshot() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// CacheCommand returns true since this command should be cached
|
// CacheCommand returns true since this command should be cached
|
||||||
func (r *RunMarkerCommand) CacheCommand(img v1.Image) DockerCommand {
|
func (r *RunMarkerCommand) CacheCommand(img v1.Image) DockerCommand {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,6 @@ func ParseStages(opts *config.KanikoOptions) ([]instructions.Stage, []instructio
|
||||||
return nil, nil, errors.Wrap(err, "parsing dockerfile")
|
return nil, nil, errors.Wrap(err, "parsing dockerfile")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.CacheCopyLayers && len(stages) >= 2 {
|
|
||||||
return nil, nil, errors.New("kaniko does not support caching copy layers in multistage builds")
|
|
||||||
}
|
|
||||||
|
|
||||||
metaArgs, err = expandNested(metaArgs, opts.BuildArgs)
|
metaArgs, err = expandNested(metaArgs, opts.BuildArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "expanding meta ARGs")
|
return nil, nil, errors.Wrap(err, "expanding meta ARGs")
|
||||||
|
|
|
||||||
|
|
@ -29,39 +29,6 @@ import (
|
||||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_ParseStages_NoMultistageWithCacheCopy(t *testing.T) {
|
|
||||||
dockerfile := `
|
|
||||||
FROM scratch as first
|
|
||||||
COPY testfile /
|
|
||||||
|
|
||||||
FROM scratch as second
|
|
||||||
COPY --from=second testfile /
|
|
||||||
`
|
|
||||||
tmpfile, err := ioutil.TempFile("", "Dockerfile.test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.Remove(tmpfile.Name())
|
|
||||||
|
|
||||||
if _, err := tmpfile.Write([]byte(dockerfile)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := tmpfile.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := &config.KanikoOptions{
|
|
||||||
DockerfilePath: tmpfile.Name(),
|
|
||||||
CacheCopyLayers: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = ParseStages(opts)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected ParseStages to fail on MultiStage build if CacheCopyLayers=true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ParseStages_ArgValueWithQuotes(t *testing.T) {
|
func Test_ParseStages_ArgValueWithQuotes(t *testing.T) {
|
||||||
dockerfile := `
|
dockerfile := `
|
||||||
ARG IMAGE="ubuntu:16.04"
|
ARG IMAGE="ubuntu:16.04"
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ func isOCILayout(path string) bool {
|
||||||
return strings.HasPrefix(path, "oci:")
|
return strings.HasPrefix(path, "oci:")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string, compositeKey CompositeCache, args *dockerfile.BuildArgs, env []string) (CompositeCache, error) {
|
func (s *stageBuilder) populateCompositeKey(command commands.DockerCommand, files []string, compositeKey CompositeCache, args *dockerfile.BuildArgs, env []string) (CompositeCache, error) {
|
||||||
// First replace all the environment variables or args in the command
|
// First replace all the environment variables or args in the command
|
||||||
replacementEnvs := args.ReplacementEnvs(env)
|
replacementEnvs := args.ReplacementEnvs(env)
|
||||||
// The sort order of `replacementEnvs` is basically undefined, sort it
|
// The sort order of `replacementEnvs` is basically undefined, sort it
|
||||||
|
|
@ -212,15 +212,16 @@ func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string
|
||||||
// avoid conflicts with any RUN command since commands can not
|
// avoid conflicts with any RUN command since commands can not
|
||||||
// start with | (vertical bar). The "#" (number of build envs) is there to
|
// start with | (vertical bar). The "#" (number of build envs) is there to
|
||||||
// help ensure proper cache matches.
|
// help ensure proper cache matches.
|
||||||
if len(replacementEnvs) > 0 {
|
|
||||||
compositeKey.AddKey(fmt.Sprintf("|%d", len(replacementEnvs)))
|
if command.IsArgsEnvsRequiredInCache() {
|
||||||
compositeKey.AddKey(replacementEnvs...)
|
if len(replacementEnvs) > 0 {
|
||||||
|
compositeKey.AddKey(fmt.Sprintf("|%d", len(replacementEnvs)))
|
||||||
|
compositeKey.AddKey(replacementEnvs...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the next command to the cache key.
|
// Add the next command to the cache key.
|
||||||
compositeKey.AddKey(resolvedCmd)
|
compositeKey.AddKey(resolvedCmd)
|
||||||
if copyCmd, ok := commands.CastAbstractCopyCommand(command); ok == true {
|
|
||||||
compositeKey = s.populateCopyCmdCompositeKey(command, copyCmd.From(), compositeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if err := compositeKey.AddPath(f, s.fileContext); err != nil {
|
if err := compositeKey.AddPath(f, s.fileContext); err != nil {
|
||||||
|
|
@ -230,22 +231,6 @@ func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string
|
||||||
return compositeKey, nil
|
return compositeKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) populateCopyCmdCompositeKey(command fmt.Stringer, from string, compositeKey CompositeCache) CompositeCache {
|
|
||||||
if from != "" {
|
|
||||||
digest, ok := s.stageIdxToDigest[from]
|
|
||||||
if ok {
|
|
||||||
ds := digest
|
|
||||||
cacheKey, ok := s.digestToCacheKey[ds]
|
|
||||||
if ok {
|
|
||||||
logrus.Debugf("Adding digest %v from previous stage to composite key for %v", ds, command.String())
|
|
||||||
compositeKey.AddKey(cacheKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return compositeKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) error {
|
func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) error {
|
||||||
if !s.opts.Cache {
|
if !s.opts.Cache {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -654,14 +654,15 @@ func newStageContext(command string, args map[string]string, env []string) stage
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
testCases := []struct {
|
type testcase struct {
|
||||||
description string
|
description string
|
||||||
cmd1 stageContext
|
cmd1 stageContext
|
||||||
cmd2 stageContext
|
cmd2 stageContext
|
||||||
shdEqual bool
|
shdEqual bool
|
||||||
}{
|
}
|
||||||
|
testCases := []testcase{
|
||||||
{
|
{
|
||||||
description: "cache key for same command with same build args",
|
description: "cache key for same command [RUN] with same build args",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo $ARG > test",
|
"RUN echo $ARG > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -675,7 +676,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
shdEqual: true,
|
shdEqual: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command with same env and args",
|
description: "cache key for same command [RUN] with same env and args",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo $ENV > test",
|
"RUN echo $ENV > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -689,7 +690,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
shdEqual: true,
|
shdEqual: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command with same env but different args",
|
description: "cache key for same command [RUN] with same env but different args",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo $ENV > test",
|
"RUN echo $ENV > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -702,7 +703,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command, different buildargs, args not used in command",
|
description: "cache key for same command [RUN], different buildargs, args not used in command",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo const > test",
|
"RUN echo const > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -715,7 +716,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command, different buildargs, args used in script",
|
description: "cache key for same command [RUN], different buildargs, args used in script",
|
||||||
// test.sh
|
// test.sh
|
||||||
// #!/bin/sh
|
// #!/bin/sh
|
||||||
// echo ${ARG}
|
// echo ${ARG}
|
||||||
|
|
@ -731,7 +732,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command with a build arg values",
|
description: "cache key for same command [RUN] with a build arg values",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo $ARG > test",
|
"RUN echo $ARG > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -744,7 +745,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for same command with different env values",
|
description: "cache key for same command [RUN] with different env values",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo $ENV > test",
|
"RUN echo $ENV > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -757,7 +758,7 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cache key for different command same context",
|
description: "cache key for different command [RUN] same context",
|
||||||
cmd1: newStageContext(
|
cmd1: newStageContext(
|
||||||
"RUN echo other > test",
|
"RUN echo other > test",
|
||||||
map[string]string{"ARG": "foo"},
|
map[string]string{"ARG": "foo"},
|
||||||
|
|
@ -769,17 +770,105 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||||
[]string{"ENV=1"},
|
[]string{"ENV=1"},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
func() testcase {
|
||||||
|
dir, files := tempDirAndFile(t)
|
||||||
|
file := files[0]
|
||||||
|
filePath := filepath.Join(dir, file)
|
||||||
|
return testcase{
|
||||||
|
description: "cache key for same command [COPY] with same args",
|
||||||
|
cmd1: newStageContext(
|
||||||
|
fmt.Sprintf("COPY %s /meow", filePath),
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
cmd2: newStageContext(
|
||||||
|
fmt.Sprintf("COPY %s /meow", filePath),
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
shdEqual: true,
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
func() testcase {
|
||||||
|
dir, files := tempDirAndFile(t)
|
||||||
|
file := files[0]
|
||||||
|
filePath := filepath.Join(dir, file)
|
||||||
|
return testcase{
|
||||||
|
description: "cache key for same command [COPY] with different args",
|
||||||
|
cmd1: newStageContext(
|
||||||
|
fmt.Sprintf("COPY %s /meow", filePath),
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
cmd2: newStageContext(
|
||||||
|
fmt.Sprintf("COPY %s /meow", filePath),
|
||||||
|
map[string]string{"ARG": "bar"},
|
||||||
|
[]string{"ENV=2"},
|
||||||
|
),
|
||||||
|
shdEqual: true,
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
{
|
||||||
|
description: "cache key for same command [WORKDIR] with same args",
|
||||||
|
cmd1: newStageContext(
|
||||||
|
"WORKDIR /",
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
cmd2: newStageContext(
|
||||||
|
"WORKDIR /",
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
shdEqual: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "cache key for same command [WORKDIR] with different args",
|
||||||
|
cmd1: newStageContext(
|
||||||
|
"WORKDIR /",
|
||||||
|
map[string]string{"ARG": "foo"},
|
||||||
|
[]string{"ENV=1"},
|
||||||
|
),
|
||||||
|
cmd2: newStageContext(
|
||||||
|
"WORKDIR /",
|
||||||
|
map[string]string{"ARG": "bar"},
|
||||||
|
[]string{"ENV=2"},
|
||||||
|
),
|
||||||
|
shdEqual: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
sb := &stageBuilder{fileContext: util.FileContext{Root: "workspace"}}
|
sb := &stageBuilder{fileContext: util.FileContext{Root: "workspace"}}
|
||||||
ck := CompositeCache{}
|
ck := CompositeCache{}
|
||||||
|
|
||||||
ck1, err := sb.populateCompositeKey(tc.cmd1.command, []string{}, ck, tc.cmd1.args, tc.cmd1.env)
|
instructions1, err := dockerfile.ParseCommands([]string{tc.cmd1.command.String()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fc1 := util.FileContext{Root: "workspace"}
|
||||||
|
dockerCommand1, err := commands.GetCommand(instructions1[0], fc1, false, true, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions, err := dockerfile.ParseCommands([]string{tc.cmd2.command.String()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fc2 := util.FileContext{Root: "workspace"}
|
||||||
|
dockerCommand2, err := commands.GetCommand(instructions[0], fc2, false, true, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ck1, err := sb.populateCompositeKey(dockerCommand1, []string{}, ck, tc.cmd1.args, tc.cmd1.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected error to be nil but was %v", err)
|
t.Errorf("Expected error to be nil but was %v", err)
|
||||||
}
|
}
|
||||||
ck2, err := sb.populateCompositeKey(tc.cmd2.command, []string{}, ck, tc.cmd2.args, tc.cmd2.env)
|
ck2, err := sb.populateCompositeKey(dockerCommand2, []string{}, ck, tc.cmd2.args, tc.cmd2.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected error to be nil but was %v", err)
|
t.Errorf("Expected error to be nil but was %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1206,6 +1295,7 @@ RUN foobar
|
||||||
cacheCommand: MockCachedDockerCommand{
|
cacheCommand: MockCachedDockerCommand{
|
||||||
contextFiles: []string{},
|
contextFiles: []string{},
|
||||||
},
|
},
|
||||||
|
argToCompositeCache: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return testcase{
|
return testcase{
|
||||||
|
|
@ -1243,6 +1333,7 @@ RUN foobar
|
||||||
cacheCommand: MockCachedDockerCommand{
|
cacheCommand: MockCachedDockerCommand{
|
||||||
contextFiles: []string{},
|
contextFiles: []string{},
|
||||||
},
|
},
|
||||||
|
argToCompositeCache: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return testcase{
|
return testcase{
|
||||||
|
|
@ -1286,6 +1377,7 @@ RUN foobar
|
||||||
cacheCommand: MockCachedDockerCommand{
|
cacheCommand: MockCachedDockerCommand{
|
||||||
contextFiles: []string{},
|
contextFiles: []string{},
|
||||||
},
|
},
|
||||||
|
argToCompositeCache: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return testcase{
|
return testcase{
|
||||||
|
|
@ -1500,9 +1592,10 @@ func Test_stageBuild_populateCompositeKeyForCopyCommand(t *testing.T) {
|
||||||
expectedCacheKey string
|
expectedCacheKey string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "multi-stage copy command",
|
description: "multi-stage copy command",
|
||||||
|
// dont use digest from previoust stage for COPY
|
||||||
command: "COPY --from=0 foo.txt bar.txt",
|
command: "COPY --from=0 foo.txt bar.txt",
|
||||||
expectedCacheKey: "COPY --from=0 foo.txt bar.txt-some-cache-key",
|
expectedCacheKey: "COPY --from=0 foo.txt bar.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "copy command",
|
description: "copy command",
|
||||||
|
|
@ -1524,7 +1617,7 @@ func Test_stageBuild_populateCompositeKeyForCopyCommand(t *testing.T) {
|
||||||
|
|
||||||
for _, useCacheCommand := range []bool{false, true} {
|
for _, useCacheCommand := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprintf("CacheCommand=%t", useCacheCommand), func(t *testing.T) {
|
t.Run(fmt.Sprintf("CacheCommand=%t", useCacheCommand), func(t *testing.T) {
|
||||||
var cmd fmt.Stringer = copyCommand
|
var cmd commands.DockerCommand = copyCommand
|
||||||
if useCacheCommand {
|
if useCacheCommand {
|
||||||
cmd = copyCommand.(*commands.CopyCommand).CacheCommand(nil)
|
cmd = copyCommand.(*commands.CopyCommand).CacheCommand(nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,10 @@ func (f fakeSnapShotter) TakeSnapshot(_ []string, _, _ bool) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockDockerCommand struct {
|
type MockDockerCommand struct {
|
||||||
command string
|
command string
|
||||||
contextFiles []string
|
contextFiles []string
|
||||||
cacheCommand commands.DockerCommand
|
cacheCommand commands.DockerCommand
|
||||||
|
argToCompositeCache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MockDockerCommand) ExecuteCommand(c *v1.Config, args *dockerfile.BuildArgs) error { return nil }
|
func (m MockDockerCommand) ExecuteCommand(c *v1.Config, args *dockerfile.BuildArgs) error { return nil }
|
||||||
|
|
@ -76,9 +77,13 @@ func (m MockDockerCommand) ShouldCacheOutput() bool {
|
||||||
func (m MockDockerCommand) ShouldDetectDeletedFiles() bool {
|
func (m MockDockerCommand) ShouldDetectDeletedFiles() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
func (m MockDockerCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return m.argToCompositeCache
|
||||||
|
}
|
||||||
|
|
||||||
type MockCachedDockerCommand struct {
|
type MockCachedDockerCommand struct {
|
||||||
contextFiles []string
|
contextFiles []string
|
||||||
|
argToCompositeCache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MockCachedDockerCommand) ExecuteCommand(c *v1.Config, args *dockerfile.BuildArgs) error {
|
func (m MockCachedDockerCommand) ExecuteCommand(c *v1.Config, args *dockerfile.BuildArgs) error {
|
||||||
|
|
@ -111,6 +116,9 @@ func (m MockCachedDockerCommand) RequiresUnpackedFS() bool {
|
||||||
func (m MockCachedDockerCommand) ShouldCacheOutput() bool {
|
func (m MockCachedDockerCommand) ShouldCacheOutput() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
func (m MockCachedDockerCommand) IsArgsEnvsRequiredInCache() bool {
|
||||||
|
return m.argToCompositeCache
|
||||||
|
}
|
||||||
|
|
||||||
type fakeLayerCache struct {
|
type fakeLayerCache struct {
|
||||||
retrieve bool
|
retrieve bool
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue