Skip to content

Commit

Permalink
feat: allow caching from built artifact on local Docker build (#6904)
Browse files Browse the repository at this point in the history
Similar to the cache-from support in #5903 for the GCB build environment.
  • Loading branch information
artemkoru authored Dec 1, 2021
1 parent 2ae224e commit 527c7c3
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/content/en/docs/pipeline-stages/builders/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ of `skaffold.yaml`. The following options can optionally be configured:

{{< schema root="LocalBuild" >}}

The `docker` builder replaces cache references to the
artifact image with the tagged image to allow caching from the
previously built image.

**Example**

The following `build` section instructs Skaffold to build a
Expand Down
5 changes: 5 additions & 0 deletions docs/content/en/samples/builders/local.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
build:
artifacts:
- image: gcr.io/k8s-skaffold/example
docker:
cacheFrom:
# Local Docker builder replaces cache references to the artifact image with
# the tagged image reference, useful for caching from the previous build.
- gcr.io/k8s-skaffold/example
local: {}
26 changes: 26 additions & 0 deletions pkg/skaffold/build/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ import (
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings"
)

func (b *Builder) Build(ctx context.Context, out io.Writer, a *latestV1.Artifact, tag string) (string, error) {
a = adjustCacheFrom(a, tag)
instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
"BuildType": "docker",
"Context": instrumentation.PII(a.Workspace),
Expand Down Expand Up @@ -132,3 +134,27 @@ func (b *Builder) pullCacheFromImages(ctx context.Context, out io.Writer, a *lat

return nil
}

// adjustCacheFrom returns an artifact where any cache references from the artifactImage is changed to the tagged built image name instead.
func adjustCacheFrom(a *latestV1.Artifact, artifactTag string) *latestV1.Artifact {
if os.Getenv("SKAFFOLD_DISABLE_DOCKER_CACHE_ADJUSTMENT") != "" {
// allow this behaviour to be disabled
return a
}

if !stringslice.Contains(a.DockerArtifact.CacheFrom, a.ImageName) {
return a
}

cf := make([]string, 0, len(a.DockerArtifact.CacheFrom))
for _, image := range a.DockerArtifact.CacheFrom {
if image == a.ImageName {
cf = append(cf, artifactTag)
} else {
cf = append(cf, image)
}
}
copy := *a
copy.DockerArtifact.CacheFrom = cf
return &copy
}
59 changes: 59 additions & 0 deletions pkg/skaffold/build/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"

"github.com/docker/docker/api/types"
Expand Down Expand Up @@ -178,6 +179,64 @@ func TestDockerCLIBuild(t *testing.T) {
}
}

func TestDockerCLICheckCacheFromArgs(t *testing.T) {
tests := []struct {
description string
artifact *latestV1.Artifact
tag string
expectedCacheFrom []string
}{
{
description: "multiple cache-from images",
artifact: &latestV1.Artifact{
ImageName: "gcr.io/k8s-skaffold/test",
ArtifactType: latestV1.ArtifactType{
DockerArtifact: &latestV1.DockerArtifact{
CacheFrom: []string{"from/image1", "from/image2"},
},
},
},
tag: "tag",
expectedCacheFrom: []string{"from/image1", "from/image2"},
},
{
description: "cache-from self uses tagged image",
artifact: &latestV1.Artifact{
ImageName: "gcr.io/k8s-skaffold/test",
ArtifactType: latestV1.ArtifactType{
DockerArtifact: &latestV1.DockerArtifact{
CacheFrom: []string{"gcr.io/k8s-skaffold/test"},
},
},
},
tag: "gcr.io/k8s-skaffold/test:tagged",
expectedCacheFrom: []string{"gcr.io/k8s-skaffold/test:tagged"},
},
}

for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
t.NewTempDir().Touch("Dockerfile").Chdir()
dockerfilePath, _ := filepath.Abs("Dockerfile")
a := *test.artifact
a.Workspace = "."
a.DockerArtifact.DockerfilePath = dockerfilePath
t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
return args, nil
})

mockCmd := testutil.CmdRun(
"docker build . --file " + dockerfilePath + " -t " + test.tag + " --cache-from " + strings.Join(test.expectedCacheFrom, " --cache-from "),
)
t.Override(&util.DefaultExecCommand, mockCmd)

builder := NewArtifactBuilder(fakeLocalDaemonWithExtraEnv([]string{}), mockConfig{}, true, util.BoolPtr(false), false, mockArtifactResolver{make(map[string]string)}, nil)
_, err := builder.Build(context.Background(), ioutil.Discard, &a, test.tag)
t.CheckNoError(err)
})
}
}

func fakeLocalDaemonWithExtraEnv(extraEnv []string) docker.LocalDaemon {
return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, extraEnv, false, nil)
}
Expand Down

0 comments on commit 527c7c3

Please sign in to comment.