image/cache: Check image platform
Make sure the cache candidate platform matches the requested.
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 877ebbe038
)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
05a370f52f
commit
f3f5327b48
7 changed files with 51 additions and 32 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -85,7 +86,7 @@ type ImageCacheBuilder interface {
|
|||
type ImageCache interface {
|
||||
// GetCache returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
GetCache(parentID string, cfg *container.Config) (imageID string, err error)
|
||||
GetCache(parentID string, cfg *container.Config, platform ocispec.Platform) (imageID string, err error)
|
||||
}
|
||||
|
||||
// Image represents a Docker image used by the builder.
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -74,7 +73,7 @@ type copier struct {
|
|||
source builder.Source
|
||||
pathCache pathCache
|
||||
download sourceDownloader
|
||||
platform *ocispec.Platform
|
||||
platform ocispec.Platform
|
||||
// for cleanup. TODO: having copier.cleanup() is error prone and hard to
|
||||
// follow. Code calling performCopy should manage the lifecycle of its params.
|
||||
// Copier should take override source as input, not imageMount.
|
||||
|
@ -83,19 +82,7 @@ type copier struct {
|
|||
}
|
||||
|
||||
func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, imageSource *imageMount) copier {
|
||||
platform := req.builder.platform
|
||||
if platform == nil {
|
||||
// May be nil if not explicitly set in API/dockerfile
|
||||
platform = &ocispec.Platform{}
|
||||
}
|
||||
if platform.OS == "" {
|
||||
// Default to the dispatch requests operating system if not explicit in API/dockerfile
|
||||
platform.OS = req.state.operatingSystem
|
||||
}
|
||||
if platform.OS == "" {
|
||||
// This is a failsafe just in case. Shouldn't be hit.
|
||||
platform.OS = runtime.GOOS
|
||||
}
|
||||
platform := req.builder.getPlatform(req.state)
|
||||
|
||||
return copier{
|
||||
source: req.source,
|
||||
|
|
|
@ -6,13 +6,14 @@ import (
|
|||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ImageProber exposes an Image cache to the Builder. It supports resetting a
|
||||
// cache.
|
||||
type ImageProber interface {
|
||||
Reset(ctx context.Context) error
|
||||
Probe(parentID string, runConfig *container.Config) (string, error)
|
||||
Probe(parentID string, runConfig *container.Config, platform ocispec.Platform) (string, error)
|
||||
}
|
||||
|
||||
type resetFunc func(context.Context) (builder.ImageCache, error)
|
||||
|
@ -51,11 +52,11 @@ func (c *imageProber) Reset(ctx context.Context) error {
|
|||
|
||||
// Probe checks if cache match can be found for current build instruction.
|
||||
// It returns the cachedID if there is a hit, and the empty string on miss
|
||||
func (c *imageProber) Probe(parentID string, runConfig *container.Config) (string, error) {
|
||||
func (c *imageProber) Probe(parentID string, runConfig *container.Config, platform ocispec.Platform) (string, error) {
|
||||
if c.cacheBusted {
|
||||
return "", nil
|
||||
}
|
||||
cacheID, err := c.cache.GetCache(parentID, runConfig)
|
||||
cacheID, err := c.cache.GetCache(parentID, runConfig, platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -74,6 +75,6 @@ func (c *nopProber) Reset(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *nopProber) Probe(_ string, _ *container.Config) (string, error) {
|
||||
func (c *nopProber) Probe(_ string, _ *container.Config, _ ocispec.Platform) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
|
@ -328,7 +329,7 @@ func getShell(c *container.Config, os string) []string {
|
|||
}
|
||||
|
||||
func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.Config) (bool, error) {
|
||||
cachedID, err := b.imageProber.Probe(dispatchState.imageID, runConfig)
|
||||
cachedID, err := b.imageProber.Probe(dispatchState.imageID, runConfig, b.getPlatform(dispatchState))
|
||||
if cachedID == "" || err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -388,3 +389,17 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
|
|||
}
|
||||
return hc
|
||||
}
|
||||
|
||||
func (b *Builder) getPlatform(state *dispatchState) ocispec.Platform {
|
||||
// May be nil if not explicitly set in API/dockerfile
|
||||
out := platforms.DefaultSpec()
|
||||
if b.platform != nil {
|
||||
out = *b.platform
|
||||
}
|
||||
|
||||
if state.operatingSystem != "" {
|
||||
out.OS = state.operatingSystem
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// MockBackend implements the builder.Backend interface for unit testing
|
||||
|
@ -106,7 +107,7 @@ type mockImageCache struct {
|
|||
getCacheFunc func(parentID string, cfg *container.Config) (string, error)
|
||||
}
|
||||
|
||||
func (mic *mockImageCache) GetCache(parentID string, cfg *container.Config) (string, error) {
|
||||
func (mic *mockImageCache) GetCache(parentID string, cfg *container.Config, _ ocispec.Platform) (string, error) {
|
||||
if mic.getCacheFunc != nil {
|
||||
return mic.getCacheFunc(parentID, cfg)
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ type localCache struct {
|
|||
imageService *ImageService
|
||||
}
|
||||
|
||||
func (ic *localCache) GetCache(parentID string, cfg *container.Config) (imageID string, err error) {
|
||||
func (ic *localCache) GetCache(parentID string, cfg *container.Config, platform ocispec.Platform) (imageID string, err error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
var children []image.ID
|
||||
|
@ -100,8 +100,11 @@ func (ic *localCache) GetCache(parentID string, cfg *container.Config) (imageID
|
|||
}
|
||||
|
||||
if cache.CompareConfig(&cc, cfg) {
|
||||
childImage, err := ic.imageService.GetImage(ctx, child.String(), imagetype.GetImageOpts{})
|
||||
childImage, err := ic.imageService.GetImage(ctx, child.String(), imagetype.GetImageOpts{Platform: &platform})
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -124,10 +127,10 @@ type imageCache struct {
|
|||
lc *localCache
|
||||
}
|
||||
|
||||
func (ic *imageCache) GetCache(parentID string, cfg *container.Config) (imageID string, err error) {
|
||||
func (ic *imageCache) GetCache(parentID string, cfg *container.Config, platform ocispec.Platform) (imageID string, err error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
imgID, err := ic.lc.GetCache(parentID, cfg)
|
||||
imgID, err := ic.lc.GetCache(parentID, cfg, platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -143,7 +146,7 @@ func (ic *imageCache) GetCache(parentID string, cfg *container.Config) (imageID
|
|||
lenHistory := 0
|
||||
|
||||
if parentID != "" {
|
||||
parent, err = ic.imageService.GetImage(ctx, parentID, imagetype.GetImageOpts{})
|
||||
parent, err = ic.imageService.GetImage(ctx, parentID, imagetype.GetImageOpts{Platform: &platform})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
21
image/cache/cache.go
vendored
21
image/cache/cache.go
vendored
|
@ -7,11 +7,13 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/log"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -28,8 +30,8 @@ type LocalImageCache struct {
|
|||
}
|
||||
|
||||
// GetCache returns the image id found in the cache
|
||||
func (lic *LocalImageCache) GetCache(imgID string, config *containertypes.Config) (string, error) {
|
||||
return getImageIDAndError(getLocalCachedImage(lic.store, image.ID(imgID), config))
|
||||
func (lic *LocalImageCache) GetCache(imgID string, config *containertypes.Config, platform ocispec.Platform) (string, error) {
|
||||
return getImageIDAndError(getLocalCachedImage(lic.store, image.ID(imgID), config, platform))
|
||||
}
|
||||
|
||||
// New returns an image cache, based on history objects
|
||||
|
@ -53,8 +55,8 @@ func (ic *ImageCache) Populate(image *image.Image) {
|
|||
}
|
||||
|
||||
// GetCache returns the image id found in the cache
|
||||
func (ic *ImageCache) GetCache(parentID string, cfg *containertypes.Config) (string, error) {
|
||||
imgID, err := ic.localImageCache.GetCache(parentID, cfg)
|
||||
func (ic *ImageCache) GetCache(parentID string, cfg *containertypes.Config, platform ocispec.Platform) (string, error) {
|
||||
imgID, err := ic.localImageCache.GetCache(parentID, cfg, platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -217,7 +219,7 @@ func getImageIDAndError(img *image.Image, err error) (string, error) {
|
|||
// of the image with imgID, that had the same config when it was
|
||||
// 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) {
|
||||
func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *containertypes.Config, platform ocispec.Platform) (*image.Image, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -247,6 +249,15 @@ func getLocalCachedImage(imageStore image.Store, imgID image.ID, config *contain
|
|||
continue
|
||||
}
|
||||
|
||||
imgPlatform := img.Platform()
|
||||
// Discard old linux/amd64 images with empty platform.
|
||||
if imgPlatform.OS == "" && imgPlatform.Architecture == "" {
|
||||
continue
|
||||
}
|
||||
if !platforms.OnlyStrict(platform).Match(imgPlatform) {
|
||||
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)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue