@@ -6,11 +6,14 @@ import (
66 "reflect"
77 "strings"
88
9+ "github.com/containerd/containerd/platforms"
910 containertypes "github.com/docker/docker/api/types/container"
1011 "github.com/docker/docker/dockerversion"
1112 "github.com/docker/docker/image"
1213 "github.com/docker/docker/layer"
14+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1315 "github.com/pkg/errors"
16+ "github.com/sirupsen/logrus"
1417)
1518
1619// NewLocal returns a local image cache, based on parent chain
@@ -26,8 +29,8 @@ type LocalImageCache struct {
2629}
2730
2831// GetCache returns the image id found in the cache
29- func (lic * LocalImageCache ) GetCache (imgID string , config * containertypes.Config ) (string , error ) {
30- return getImageIDAndError (getLocalCachedImage (lic .store , image .ID (imgID ), config ))
32+ func (lic * LocalImageCache ) GetCache (imgID string , config * containertypes.Config , platform ocispec. Platform ) (string , error ) {
33+ return getImageIDAndError (getLocalCachedImage (lic .store , image .ID (imgID ), config , platform ))
3134}
3235
3336// New returns an image cache, based on history objects
@@ -51,8 +54,8 @@ func (ic *ImageCache) Populate(image *image.Image) {
5154}
5255
5356// GetCache returns the image id found in the cache
54- func (ic * ImageCache ) GetCache (parentID string , cfg * containertypes.Config ) (string , error ) {
55- imgID , err := ic .localImageCache .GetCache (parentID , cfg )
57+ func (ic * ImageCache ) GetCache (parentID string , cfg * containertypes.Config , platform ocispec. Platform ) (string , error ) {
58+ imgID , err := ic .localImageCache .GetCache (parentID , cfg , platform )
5659 if err != nil {
5760 return "" , err
5861 }
@@ -215,7 +218,23 @@ func getImageIDAndError(img *image.Image, err error) (string, error) {
215218// of the image with imgID, that had the same config when it was
216219// created. nil is returned if a child cannot be found. An error is
217220// returned if the parent image cannot be found.
218- func getLocalCachedImage (imageStore image.Store , imgID image.ID , config * containertypes.Config ) (* image.Image , error ) {
221+ func getLocalCachedImage (imageStore image.Store , imgID image.ID , config * containertypes.Config , platform ocispec.Platform ) (* image.Image , error ) {
222+ if config == nil {
223+ return nil , nil
224+ }
225+
226+ isBuiltLocally := func (id image.ID ) bool {
227+ builtLocally , err := imageStore .IsBuiltLocally (id )
228+ if err != nil {
229+ logrus .WithFields (logrus.Fields {
230+ "error" : err ,
231+ "id" : id ,
232+ }).Warn ("failed to check if image was built locally" )
233+ return false
234+ }
235+ return builtLocally
236+ }
237+
219238 // Loop on the children of the given image and check the config
220239 getMatch := func (siblings []image.ID ) (* image.Image , error ) {
221240 var match * image.Image
@@ -225,6 +244,25 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
225244 return nil , fmt .Errorf ("unable to find image %q" , id )
226245 }
227246
247+ if ! isBuiltLocally (id ) {
248+ continue
249+ }
250+
251+ imgPlatform := ocispec.Platform {
252+ Architecture : img .Architecture ,
253+ OS : img .OS ,
254+ OSVersion : img .OSVersion ,
255+ OSFeatures : img .OSFeatures ,
256+ Variant : img .Variant ,
257+ }
258+ // Discard old linux/amd64 images with empty platform.
259+ if imgPlatform .OS == "" && imgPlatform .Architecture == "" {
260+ continue
261+ }
262+ if ! platforms .OnlyStrict (platform ).Match (imgPlatform ) {
263+ continue
264+ }
265+
228266 if compare (& img .ContainerConfig , config ) {
229267 // check for the most up to date match
230268 if match == nil || match .Created .Before (img .Created ) {
@@ -238,11 +276,29 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
238276 // In this case, this is `FROM scratch`, which isn't an actual image.
239277 if imgID == "" {
240278 images := imageStore .Map ()
279+
241280 var siblings []image.ID
242281 for id , img := range images {
243- if img .Parent == imgID {
244- siblings = append ( siblings , id )
282+ if img .Parent != "" {
283+ continue
245284 }
285+
286+ if ! isBuiltLocally (id ) {
287+ continue
288+ }
289+
290+ // Do a quick initial filter on the Cmd to avoid adding all
291+ // non-local images with empty parent to the siblings slice and
292+ // performing a full config compare.
293+ //
294+ // config.Cmd is set to the current Dockerfile instruction so we
295+ // check it against the img.ContainerConfig.Cmd which is the
296+ // command of the last layer.
297+ if ! strSliceEqual (img .ContainerConfig .Cmd , config .Cmd ) {
298+ continue
299+ }
300+
301+ siblings = append (siblings , id )
246302 }
247303 return getMatch (siblings )
248304 }
@@ -251,3 +307,15 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
251307 siblings := imageStore .Children (imgID )
252308 return getMatch (siblings )
253309}
310+
311+ func strSliceEqual (a , b []string ) bool {
312+ if len (a ) != len (b ) {
313+ return false
314+ }
315+ for i := 0 ; i < len (a ); i ++ {
316+ if a [i ] != b [i ] {
317+ return false
318+ }
319+ }
320+ return true
321+ }
0 commit comments