image/cache: Restrict cache candidates to locally built images

Restrict cache candidates only to images that were built locally.
This doesn't affect builds using `--cache-from`.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 96ac22768a)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2023-12-01 10:24:27 +01:00
parent be7b60ef05
commit 05a370f52f
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A

56
image/cache/cache.go vendored
View file

@ -1,11 +1,13 @@
package cache // import "github.com/docker/docker/image/cache"
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"github.com/containerd/log"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/image"
@ -216,6 +218,22 @@ func getImageIDAndError(img *image.Image, err error) (string, error) {
// created. nil is returned if a child cannot be found. An error is
// returned if the parent image cannot be found.
func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *containertypes.Config) (*image.Image, error) {
if config == nil {
return nil, nil
}
isBuiltLocally := func(id image.ID) bool {
builtLocally, err := imageStore.IsBuiltLocally(id)
if err != nil {
log.G(context.TODO()).WithFields(log.Fields{
"error": err,
"id": id,
}).Warn("failed to check if image was built locally")
return false
}
return builtLocally
}
// Loop on the children of the given image and check the config
getMatch := func(siblings []image.ID) (*image.Image, error) {
var match *image.Image
@ -225,6 +243,10 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
return nil, fmt.Errorf("unable to find image %q", id)
}
if !isBuiltLocally(id) {
continue
}
if compare(&img.ContainerConfig, config) {
// check for the most up to date match
if img.Created != nil && (match == nil || match.Created.Before(*img.Created)) {
@ -238,11 +260,29 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
// In this case, this is `FROM scratch`, which isn't an actual image.
if imgID == "" {
images := imageStore.Map()
var siblings []image.ID
for id, img := range images {
if img.Parent == imgID {
siblings = append(siblings, id)
if img.Parent != "" {
continue
}
if !isBuiltLocally(id) {
continue
}
// Do a quick initial filter on the Cmd to avoid adding all
// non-local images with empty parent to the siblings slice and
// performing a full config compare.
//
// config.Cmd is set to the current Dockerfile instruction so we
// check it against the img.ContainerConfig.Cmd which is the
// command of the last layer.
if !strSliceEqual(img.ContainerConfig.Cmd, config.Cmd) {
continue
}
siblings = append(siblings, id)
}
return getMatch(siblings)
}
@ -251,3 +291,15 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
siblings := imageStore.Children(imgID)
return getMatch(siblings)
}
func strSliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}