Skip to content

Commit b3e079d

Browse files
priyawadhwadgageot
priyawadhwa
authored andcommitted
Simplify caching (#2452)
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 9981c37 commit b3e079d

File tree

3 files changed

+242
-361
lines changed

3 files changed

+242
-361
lines changed

pkg/skaffold/build/cache/retrieve.go

+101-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,155 @@ 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

56-
start := time.Now()
5752
color.Default.Fprintln(out, "Checking cache...")
53+
start := time.Now()
5854

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

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

78+
wg.Wait()
79+
7180
var (
7281
needToBuild []*latest.Artifact
7382
built []build.Artifact
7483
)
7584

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-
85+
for i, imageBuilt := range builtImages {
86+
if imageBuilt {
11287
built = append(built, build.Artifact{
113-
ImageName: artifact.ImageName,
114-
Tag: details.hashTag,
88+
ImageName: artifacts[i].ImageName,
89+
Tag: HashTag(artifacts[i]),
11590
})
11691
}
11792
}
11893

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

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) {
104+
// retrieveCachedArtifact tries to retrieve a cached artifact.
105+
// If it cannot, it returns false.
106+
// If it can, it returns true.
107+
func (c *Cache) retrieveCachedArtifact(ctx context.Context, out io.Writer, a *latest.Artifact) (bool, error) {
132108
hash, err := hashForArtifact(ctx, c.builder, a)
133109
if err != nil {
134-
return nil, errors.Wrapf(err, "getting hash for artifact %s", a.ImageName)
110+
return false, errors.Wrapf(err, "getting hash for artifact %s", a.ImageName)
135111
}
136112
a.WorkspaceHash = hash
137113
imageDetails, cacheHit := c.artifactCache[hash]
138114
if !cacheHit {
139-
return &cachedArtifactDetails{
140-
needsRebuild: true,
141-
}, nil
115+
return false, nil
142116
}
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)
117+
if c.localCluster && !c.pushImages {
118+
return c.artifactExistsLocally(ctx, out, a, imageDetails)
147119
}
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
155-
}
156-
157-
// imageLocation holds information about where the image currently is
158-
type imageLocation struct {
159-
existsRemotely bool
160-
existsLocally bool
161-
prebuiltImage string
120+
return c.artifactExistsRemotely(ctx, out, a, imageDetails)
162121
}
163122

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
123+
// artifactExistsLocally assumes the artifact must exist locally.
124+
// This is called when using a local cluster and push:false.
125+
// 1. Check if image exists locally
126+
// 2. If not, check if same digest exists locally. If it does, retag.
127+
// 3. If not, rebuild
128+
// It returns true if the artifact exists locally, and false if not.
129+
func (c *Cache) artifactExistsLocally(ctx context.Context, out io.Writer, a *latest.Artifact, imageDetails ImageDetails) (bool, error) {
130+
hashTag := HashTag(a)
131+
if c.client.ImageExists(ctx, hashTag) {
132+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
133+
color.Green.Fprintf(out, "Found\n")
134+
return true, nil
180135
}
181136
// Check for a local image with the same digest as the image we want to build
182137
prebuiltImage, err := c.retrievePrebuiltImage(imageDetails)
183138
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 == ""
139+
return false, errors.Wrapf(err, "getting prebuilt image")
140+
}
141+
// If prebuilt image exists, tag it. Otherwise, return false, as artifact doesn't exist locally.
142+
if prebuiltImage != "" {
143+
color.Default.Fprintf(out, " - %s: ", a.ImageName)
144+
color.Green.Fprintf(out, "Found. Retagging.\n")
145+
if err := c.client.Tag(ctx, prebuiltImage, hashTag); err != nil {
146+
return false, errors.Wrap(err, "retagging image")
147+
}
148+
return true, nil
199149
}
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 == ""
150+
return false, nil
205151
}
206152

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

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

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

0 commit comments

Comments
 (0)