github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/cache/retrieve.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "time" 24 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 28 sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors" 29 eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output" 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 34 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" 35 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 36 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/tag" 37 timeutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/time" 38 ) 39 40 func (c *cache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, platforms platform.Resolver, buildAndTest BuildAndTestFn) ([]graph.Artifact, error) { 41 ctx, cancel := context.WithCancel(ctx) 42 defer cancel() 43 44 start := time.Now() 45 46 output.Default.Fprintln(out, "Checking cache...") 47 ctx, endTrace := instrumentation.StartTrace(ctx, "Build_CheckBuildCache") 48 defer endTrace() 49 50 lookup := make(chan []cacheDetails) 51 go func() { lookup <- c.lookupArtifacts(ctx, tags, platforms, artifacts) }() 52 53 var results []cacheDetails 54 select { 55 case <-ctx.Done(): 56 return nil, context.Canceled 57 case results = <-lookup: 58 } 59 60 hashByName := make(map[string]string) 61 var needToBuild []*latest.Artifact 62 var alreadyBuilt []graph.Artifact 63 for i, artifact := range artifacts { 64 eventV2.CacheCheckInProgress(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String()) 65 out, ctx := output.WithEventContext(ctx, out, constants.Build, artifact.ImageName) 66 output.Default.Fprintf(out, " - %s: ", artifact.ImageName) 67 68 result := results[i] 69 switch result := result.(type) { 70 case failed: 71 output.Red.Fprintln(out, "Error checking cache.") 72 endTrace(instrumentation.TraceEndError(result.err)) 73 return nil, result.err 74 75 case needsBuilding: 76 eventV2.CacheCheckMiss(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String()) 77 output.Yellow.Fprintln(out, "Not found. Building") 78 hashByName[artifact.ImageName] = result.Hash() 79 needToBuild = append(needToBuild, artifact) 80 continue 81 82 case needsTagging: 83 eventV2.CacheCheckHit(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String()) 84 output.Green.Fprintln(out, "Found. Tagging") 85 if err := result.Tag(ctx, c, platforms); err != nil { 86 endTrace(instrumentation.TraceEndError(err)) 87 return nil, fmt.Errorf("tagging image: %w", err) 88 } 89 90 case needsPushing: 91 eventV2.CacheCheckHit(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String()) 92 output.Green.Fprintln(out, "Found. Pushing") 93 if err := result.Push(ctx, out, c); err != nil { 94 endTrace(instrumentation.TraceEndError(err)) 95 96 return nil, fmt.Errorf("%s: %w", sErrors.PushImageErr, err) 97 } 98 99 default: 100 eventV2.CacheCheckHit(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String()) 101 isLocal, err := c.isLocalImage(artifact.ImageName) 102 if err != nil { 103 endTrace(instrumentation.TraceEndError(err)) 104 return nil, err 105 } 106 if isLocal { 107 output.Green.Fprintln(out, "Found Locally") 108 } else { 109 output.Green.Fprintln(out, "Found Remotely") 110 } 111 } 112 113 // Image is already built 114 c.cacheMutex.RLock() 115 entry := c.artifactCache[result.Hash()] 116 c.cacheMutex.RUnlock() 117 tag := tags[artifact.ImageName] 118 119 var uniqueTag string 120 isLocal, err := c.isLocalImage(artifact.ImageName) 121 if err != nil { 122 endTrace(instrumentation.TraceEndError(err)) 123 return nil, err 124 } 125 if isLocal { 126 var err error 127 uniqueTag, err = build.TagWithImageID(ctx, tag, entry.ID, c.client) 128 if err != nil { 129 endTrace(instrumentation.TraceEndError(err)) 130 return nil, err 131 } 132 } else { 133 uniqueTag = build.TagWithDigest(tag, entry.Digest) 134 } 135 c.artifactStore.Record(artifact, uniqueTag) 136 alreadyBuilt = append(alreadyBuilt, graph.Artifact{ 137 ImageName: artifact.ImageName, 138 Tag: uniqueTag, 139 }) 140 } 141 142 log.Entry(ctx).Infoln("Cache check completed in", timeutil.Humanize(time.Since(start))) 143 144 bRes, err := buildAndTest(ctx, out, tags, needToBuild, platforms) 145 if err != nil { 146 endTrace(instrumentation.TraceEndError(err)) 147 return nil, err 148 } 149 150 if err := c.addArtifacts(ctx, bRes, hashByName); err != nil { 151 log.Entry(ctx).Warnf("error adding artifacts to cache; caching may not work as expected: %v", err) 152 return append(bRes, alreadyBuilt...), nil 153 } 154 155 if err := saveArtifactCache(c.cacheFile, c.artifactCache); err != nil { 156 log.Entry(ctx).Warnf("error saving cache file; caching may not work as expected: %v", err) 157 return append(bRes, alreadyBuilt...), nil 158 } 159 160 return maintainArtifactOrder(append(bRes, alreadyBuilt...), artifacts), err 161 } 162 163 func maintainArtifactOrder(built []graph.Artifact, artifacts []*latest.Artifact) []graph.Artifact { 164 byName := make(map[string]graph.Artifact) 165 for _, build := range built { 166 byName[build.ImageName] = build 167 } 168 169 var ordered []graph.Artifact 170 171 for _, artifact := range artifacts { 172 ordered = append(ordered, byName[artifact.ImageName]) 173 } 174 175 return ordered 176 } 177 178 func (c *cache) addArtifacts(ctx context.Context, bRes []graph.Artifact, hashByName map[string]string) error { 179 for _, a := range bRes { 180 entry := ImageDetails{} 181 isLocal, err := c.isLocalImage(a.ImageName) 182 if err != nil { 183 return err 184 } 185 if isLocal { 186 imageID, err := c.client.ImageID(ctx, a.Tag) 187 if err != nil { 188 return err 189 } 190 191 if imageID != "" { 192 entry.ID = imageID 193 } 194 } else { 195 ref, err := docker.ParseReference(a.Tag) 196 if err != nil { 197 return fmt.Errorf("parsing reference %q: %w", a.Tag, err) 198 } 199 entry.Digest = ref.Digest 200 } 201 c.cacheMutex.Lock() 202 c.artifactCache[hashByName[a.ImageName]] = entry 203 c.cacheMutex.Unlock() 204 } 205 return nil 206 }