Skip to content

Commit 6b26b8b

Browse files
author
Priya Wadhwa
committed
Simplify caching
This PR simplifies caching to the following rules, described in my second design doc for artifact caching: If remote cluster or push:true : 1. Check if image exists remotely. 2. If not, check if same digest exists locally. If it does, retag and repush. 3. If not, rebuild and repush. If local cluster, and push:false: 1. Check if image exists locally 2. If not, check if same digest exists locally. If it does, retag. 3. If not, rebuild
1 parent 2c9ed37 commit 6b26b8b

File tree

3 files changed

+242
-362
lines changed

3 files changed

+242
-362
lines changed

pkg/skaffold/build/cache/retrieve.go

+100-130
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"io"
23+
"sync"
2324
"time"
2425

2526
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
@@ -42,185 +43,154 @@ type ImageDetails struct {
4243
ID string `yaml:"id,omitempty"`
4344
}
4445

45-
type detailsErr struct {
46-
details *cachedArtifactDetails
47-
err error
48-
}
49-
5046
// RetrieveCachedArtifacts checks to see if artifacts are cached, and returns tags for cached images, otherwise a list of images to be built
5147
func (c *Cache) RetrieveCachedArtifacts(ctx context.Context, out io.Writer, artifacts []*latest.Artifact) ([]*latest.Artifact, []build.Artifact, error) {
5248
if !c.useCache {
5349
return artifacts, nil, nil
5450
}
5551

5652
start := time.Now()
57-
color.Default.Fprintln(out, "Checking cache...")
5853

59-
detailsErrs := make([]chan detailsErr, len(artifacts))
54+
wg := sync.WaitGroup{}
55+
builtImages := make([]bool, len(artifacts))
56+
needToBuildImages := make([]bool, len(artifacts))
6057

6158
for i := range artifacts {
62-
detailsErrs[i] = make(chan detailsErr, 1)
63-
59+
wg.Add(1)
6460
i := i
6561
go func() {
66-
details, err := c.retrieveCachedArtifactDetails(ctx, artifacts[i])
67-
detailsErrs[i] <- detailsErr{details: details, err: err}
62+
defer wg.Done()
63+
ok, err := c.retrieveCachedArtifact(ctx, out, artifacts[i])
64+
if ok {
65+
builtImages[i] = true
66+
return
67+
}
68+
if err != nil {
69+
logrus.Debugf("error finding cached artifact for %s: %v", artifacts[i].ImageName, err)
70+
}
71+
color.Default.Fprintf(out, " - %s: ", artifacts[i].ImageName)
72+
color.Red.Fprintf(out, "Not Found. Rebuilding.\n")
73+
needToBuildImages[i] = true
6874
}()
6975
}
7076

77+
wg.Wait()
78+
7179
var (
7280
needToBuild []*latest.Artifact
7381
built []build.Artifact
7482
)
7583

76-
for i, artifact := range artifacts {
77-
color.Default.Fprintf(out, " - %s: ", artifact.ImageName)
78-
79-
select {
80-
case <-ctx.Done():
81-
return nil, nil, context.Canceled
82-
83-
case d := <-detailsErrs[i]:
84-
details := d.details
85-
err := d.err
86-
if err != nil || details.needsRebuild {
87-
color.Red.Fprintln(out, "Not found. Rebuilding.")
88-
needToBuild = append(needToBuild, artifact)
89-
continue
90-
}
91-
92-
color.Green.Fprint(out, "Found")
93-
if details.needsRetag {
94-
color.Green.Fprint(out, ". Retagging")
95-
}
96-
if details.needsPush {
97-
color.Green.Fprint(out, ". Pushing.")
98-
}
99-
color.Default.Fprintln(out)
100-
101-
if details.needsRetag {
102-
if err := c.client.Tag(ctx, details.prebuiltImage, details.hashTag); err != nil {
103-
return nil, nil, errors.Wrap(err, "retagging image")
104-
}
105-
}
106-
if details.needsPush {
107-
if _, err := c.client.Push(ctx, out, details.hashTag); err != nil {
108-
return nil, nil, errors.Wrap(err, "pushing image")
109-
}
110-
}
111-
84+
for i, imageBuilt := range builtImages {
85+
if imageBuilt {
11286
built = append(built, build.Artifact{
113-
ImageName: artifact.ImageName,
114-
Tag: details.hashTag,
87+
ImageName: artifacts[i].ImageName,
88+
Tag: HashTag(artifacts[i]),
11589
})
11690
}
11791
}
11892

93+
for i, imageBuilt := range needToBuildImages {
94+
if imageBuilt {
95+
needToBuild = append(needToBuild, artifacts[i])
96+
}
97+
}
98+
11999
color.Default.Fprintln(out, "Cache check complete in", time.Since(start))
120100
return needToBuild, built, nil
121101
}
122102

123-
type cachedArtifactDetails struct {
124-
needsRebuild bool
125-
needsRetag bool
126-
needsPush bool
127-
prebuiltImage string
128-
hashTag string
129-
}
130-
131-
func (c *Cache) retrieveCachedArtifactDetails(ctx context.Context, a *latest.Artifact) (*cachedArtifactDetails, error) {
103+
// retrieveCachedArtifact tries to retrieve a cached artifact.
104+
// If it cannot, it returns false.
105+
// If it can, it returns true.
106+
func (c *Cache) retrieveCachedArtifact(ctx context.Context, out io.Writer, a *latest.Artifact) (bool, error) {
132107
hash, err := hashForArtifact(ctx, c.builder, a)
133108
if err != nil {
134-
return nil, errors.Wrapf(err, "getting hash for artifact %s", a.ImageName)
109+
return false, errors.Wrapf(err, "getting hash for artifact %s", a.ImageName)
135110
}
136111
a.WorkspaceHash = hash
137112
imageDetails, cacheHit := c.artifactCache[hash]
138113
if !cacheHit {
139-
return &cachedArtifactDetails{
140-
needsRebuild: true,
141-
}, nil
114+
return false, nil
142115
}
143-
hashTag := HashTag(a)
144-
il, err := c.imageLocation(ctx, imageDetails, hashTag)
145-
if err != nil {
146-
return nil, errors.Wrapf(err, "getting artifact details for %s", a.ImageName)
116+
if c.localCluster && !c.pushImages {
117+
return c.artifactExistsLocally(ctx, out, a, imageDetails)
147118
}
148-
return &cachedArtifactDetails{
149-
needsRebuild: needsRebuild(il, c.localCluster),
150-
needsRetag: needsRetag(il),
151-
needsPush: needsPush(il, c.localCluster, c.pushImages),
152-
prebuiltImage: il.prebuiltImage,
153-
hashTag: hashTag,
154-
}, nil
119+
return c.artifactExistsRemotely(ctx, out, a, imageDetails)
155120
}
156121

157-
// imageLocation holds information about where the image currently is
158-
type imageLocation struct {
159-
existsRemotely bool
160-
existsLocally bool
161-
prebuiltImage string
162-
}
163-
164-
func (c *Cache) imageLocation(ctx context.Context, imageDetails ImageDetails, tag string) (*imageLocation, error) {
165-
// Check if tagged image exists remotely with the same digest
166-
existsRemotely := imgExistsRemotely(tag, imageDetails.Digest, c.insecureRegistries)
167-
existsLocally := false
168-
if c.client != nil {
169-
// See if this image exists in the local daemon
170-
if c.client.ImageExists(ctx, tag) {
171-
existsLocally = true
172-
}
173-
}
174-
if existsLocally {
175-
return &imageLocation{
176-
existsLocally: existsLocally,
177-
existsRemotely: existsRemotely,
178-
prebuiltImage: tag,
179-
}, nil
122+
// artifactExistsLocally assumes the artifact must exist locally.
123+
// This is called when using a local cluster and push:false.
124+
// 1. Check if image exists locally
125+
// 2. If not, check if same digest exists locally. If it does, retag.
126+
// 3. If not, rebuild
127+
// It returns true if the artifact exists locally, and false if not.
128+
func (c *Cache) artifactExistsLocally(ctx context.Context, out io.Writer, a *latest.Artifact, imageDetails ImageDetails) (bool, error) {
129+
hashTag := HashTag(a)
130+
if c.client.ImageExists(ctx, hashTag) {
131+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
132+
color.Green.Fprintf(out, "Found\n")
133+
return true, nil
180134
}
181135
// Check for a local image with the same digest as the image we want to build
182136
prebuiltImage, err := c.retrievePrebuiltImage(imageDetails)
183137
if err != nil {
184-
return nil, errors.Wrapf(err, "getting prebuilt image")
185-
}
186-
return &imageLocation{
187-
existsRemotely: existsRemotely,
188-
existsLocally: existsLocally,
189-
prebuiltImage: prebuiltImage,
190-
}, nil
191-
}
192-
193-
func needsRebuild(d *imageLocation, localCluster bool) bool {
194-
// If using local cluster, rebuild if all of the following are true:
195-
// 1. does not exist locally
196-
// 2. can't retag a prebuilt image
197-
if localCluster {
198-
return !d.existsLocally && d.prebuiltImage == ""
138+
return false, errors.Wrapf(err, "getting prebuilt image")
139+
}
140+
// If prebuilt image exists, tag it. Otherwise, return false, as artifact doesn't exist locally.
141+
if prebuiltImage != "" {
142+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
143+
color.Green.Fprintf(out, "Found. Retagging.\n")
144+
if err := c.client.Tag(ctx, prebuiltImage, hashTag); err != nil {
145+
return false, errors.Wrap(err, "retagging image")
146+
}
147+
return true, nil
199148
}
200-
// If using remote cluster, only rebuild image if all of the following are true:
201-
// 1. does not exist locally
202-
// 2. does not exist remotely
203-
// 3. can't retag a prebuilt image
204-
return !d.existsLocally && !d.existsRemotely && d.prebuiltImage == ""
149+
return false, nil
205150
}
206151

207-
func needsPush(d *imageLocation, localCluster, push bool) bool {
208-
// If using local cluster...
209-
if localCluster {
210-
// ... only push if specified and image does not exist remotely
211-
return push && !d.existsRemotely
152+
// artifactExistsRemotely assumes the artifact must exist locally.
153+
// this is used when running a remote cluster, or when push:true
154+
// 1. Check if image exists remotely.
155+
// 2. If not, check if same digest exists locally. If it does, retag and repush.
156+
// 3. If not, rebuild.
157+
// It returns true if the artifact exists remotely, and false if not.
158+
func (c *Cache) artifactExistsRemotely(ctx context.Context, out io.Writer, a *latest.Artifact, imageDetails ImageDetails) (bool, error) {
159+
hashTag := HashTag(a)
160+
if imgExistsRemotely(hashTag, imageDetails.Digest, c.insecureRegistries) {
161+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
162+
color.Green.Fprintf(out, "Found\n")
163+
return true, nil
164+
}
165+
166+
// Check if image exists locally.
167+
if c.client.ImageExists(ctx, hashTag) {
168+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
169+
color.Green.Fprintf(out, "Found Locally. Pushing.\n")
170+
if _, err := c.client.Push(ctx, out, hashTag); err != nil {
171+
return false, errors.Wrap(err, "retagging image")
172+
}
173+
return true, nil
212174
}
213-
// If using remote cluster, push if image does not exist remotely
214-
return !d.existsRemotely
215-
}
216175

217-
func needsRetag(d *imageLocation) bool {
218-
// Don't need a retag if image already exists locally
219-
if d.existsLocally {
220-
return false
176+
// Check for a local image with the same digest as the image we want to build
177+
prebuiltImage, err := c.retrievePrebuiltImage(imageDetails)
178+
if err != nil {
179+
return false, errors.Wrapf(err, "getting prebuilt image")
180+
}
181+
// If prebuilt image exists, tag it and push it. Otherwise, return false, as artifact doesn't exist locally.
182+
if prebuiltImage != "" {
183+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
184+
color.Green.Fprintf(out, "Found Locally. Retagging and Pushing.\n")
185+
if err := c.client.Tag(ctx, prebuiltImage, hashTag); err != nil {
186+
return false, errors.Wrap(err, "retagging image")
187+
}
188+
if _, err := c.client.Push(ctx, out, hashTag); err != nil {
189+
return false, errors.Wrap(err, "retagging image")
190+
}
191+
return true, nil
221192
}
222-
// If a prebuilt image is found locally, retag the image
223-
return d.prebuiltImage != ""
193+
return false, nil
224194
}
225195

226196
func (c *Cache) retrievePrebuiltImage(details ImageDetails) (string, error) {

0 commit comments

Comments
 (0)