|
@@ -11,6 +11,7 @@ import (
|
|
"runtime"
|
|
"runtime"
|
|
"strings"
|
|
"strings"
|
|
|
|
|
|
|
|
+ "github.com/containerd/containerd/platforms"
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
@@ -63,7 +64,7 @@ type v2Puller struct {
|
|
confirmedV2 bool
|
|
confirmedV2 bool
|
|
}
|
|
}
|
|
|
|
|
|
-func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (err error) {
|
|
|
|
|
|
+func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
|
|
// TODO(tiborvass): was ReceiveTimeout
|
|
// TODO(tiborvass): was ReceiveTimeout
|
|
p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
|
p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -71,7 +72,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
- if err = p.pullV2Repository(ctx, ref, os); err != nil {
|
|
|
|
|
|
+ if err = p.pullV2Repository(ctx, ref, platform); err != nil {
|
|
if _, ok := err.(fallbackError); ok {
|
|
if _, ok := err.(fallbackError); ok {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -86,10 +87,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
-func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os string) (err error) {
|
|
|
|
|
|
+func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
|
|
var layersDownloaded bool
|
|
var layersDownloaded bool
|
|
if !reference.IsNameOnly(ref) {
|
|
if !reference.IsNameOnly(ref) {
|
|
- layersDownloaded, err = p.pullV2Tag(ctx, ref, os)
|
|
|
|
|
|
+ layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -111,7 +112,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
- pulledNew, err := p.pullV2Tag(ctx, tagRef, os)
|
|
|
|
|
|
+ pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
// Since this is the pull-all-tags case, don't
|
|
// Since this is the pull-all-tags case, don't
|
|
// allow an error pulling a particular tag to
|
|
// allow an error pulling a particular tag to
|
|
@@ -327,7 +328,7 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
|
|
ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
|
|
ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
|
|
}
|
|
}
|
|
|
|
|
|
-func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string) (tagUpdated bool, err error) {
|
|
|
|
|
|
+func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) {
|
|
manSvc, err := p.repo.Manifests(ctx)
|
|
manSvc, err := p.repo.Manifests(ctx)
|
|
if err != nil {
|
|
if err != nil {
|
|
return false, err
|
|
return false, err
|
|
@@ -391,17 +392,17 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string
|
|
if p.config.RequireSchema2 {
|
|
if p.config.RequireSchema2 {
|
|
return false, fmt.Errorf("invalid manifest: not schema2")
|
|
return false, fmt.Errorf("invalid manifest: not schema2")
|
|
}
|
|
}
|
|
- id, manifestDigest, err = p.pullSchema1(ctx, ref, v, os)
|
|
|
|
|
|
+ id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return false, err
|
|
return false, err
|
|
}
|
|
}
|
|
case *schema2.DeserializedManifest:
|
|
case *schema2.DeserializedManifest:
|
|
- id, manifestDigest, err = p.pullSchema2(ctx, ref, v, os)
|
|
|
|
|
|
+ id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return false, err
|
|
return false, err
|
|
}
|
|
}
|
|
case *manifestlist.DeserializedManifestList:
|
|
case *manifestlist.DeserializedManifestList:
|
|
- id, manifestDigest, err = p.pullManifestList(ctx, ref, v, os)
|
|
|
|
|
|
+ id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return false, err
|
|
return false, err
|
|
}
|
|
}
|
|
@@ -437,7 +438,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string
|
|
return true, nil
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
|
|
|
|
|
+func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
|
var verifiedManifest *schema1.Manifest
|
|
var verifiedManifest *schema1.Manifest
|
|
verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
|
|
verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -513,7 +514,10 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
|
|
// we support the operating system, switch to that operating system.
|
|
// we support the operating system, switch to that operating system.
|
|
// eg FROM supertest2014/nyan with no platform specifier, and docker build
|
|
// eg FROM supertest2014/nyan with no platform specifier, and docker build
|
|
// with no --platform= flag under LCOW.
|
|
// with no --platform= flag under LCOW.
|
|
- if requestedOS == "" && system.IsOSSupported(configOS) {
|
|
|
|
|
|
+ requestedOS := ""
|
|
|
|
+ if platform != nil {
|
|
|
|
+ requestedOS = platform.OS
|
|
|
|
+ } else if system.IsOSSupported(configOS) {
|
|
requestedOS = configOS
|
|
requestedOS = configOS
|
|
}
|
|
}
|
|
|
|
|
|
@@ -544,7 +548,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
|
|
return imageID, manifestDigest, nil
|
|
return imageID, manifestDigest, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
|
|
|
|
|
+func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
|
manifestDigest, err = schema2ManifestDigest(ref, mfst)
|
|
manifestDigest, err = schema2ManifestDigest(ref, mfst)
|
|
if err != nil {
|
|
if err != nil {
|
|
return "", "", err
|
|
return "", "", err
|
|
@@ -600,6 +604,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
configPlatform *specs.Platform // for LCOW when registering downloaded layers
|
|
configPlatform *specs.Platform // for LCOW when registering downloaded layers
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+ layerStoreOS := runtime.GOOS
|
|
|
|
+ if platform != nil {
|
|
|
|
+ layerStoreOS = platform.OS
|
|
|
|
+ }
|
|
|
|
+
|
|
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
|
|
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
|
|
// explicitly blocking images intended for linux from the Windows daemon. On
|
|
// explicitly blocking images intended for linux from the Windows daemon. On
|
|
// Windows, we do this before the attempt to download, effectively serialising
|
|
// Windows, we do this before the attempt to download, effectively serialising
|
|
@@ -623,13 +632,14 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
if len(descriptors) != len(configRootFS.DiffIDs) {
|
|
if len(descriptors) != len(configRootFS.DiffIDs) {
|
|
return "", "", errRootFSMismatch
|
|
return "", "", errRootFSMismatch
|
|
}
|
|
}
|
|
-
|
|
|
|
- // Early bath if the requested OS doesn't match that of the configuration.
|
|
|
|
- // This avoids doing the download, only to potentially fail later.
|
|
|
|
- if !system.IsOSSupported(configPlatform.OS) {
|
|
|
|
- return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, requestedOS)
|
|
|
|
|
|
+ if platform == nil {
|
|
|
|
+ // Early bath if the requested OS doesn't match that of the configuration.
|
|
|
|
+ // This avoids doing the download, only to potentially fail later.
|
|
|
|
+ if !system.IsOSSupported(configPlatform.OS) {
|
|
|
|
+ return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
|
|
|
|
+ }
|
|
|
|
+ layerStoreOS = configPlatform.OS
|
|
}
|
|
}
|
|
- requestedOS = configPlatform.OS
|
|
|
|
|
|
|
|
// Populate diff ids in descriptors to avoid downloading foreign layers
|
|
// Populate diff ids in descriptors to avoid downloading foreign layers
|
|
// which have been side loaded
|
|
// which have been side loaded
|
|
@@ -638,10 +648,6 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if requestedOS == "" {
|
|
|
|
- requestedOS = runtime.GOOS
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if p.config.DownloadManager != nil {
|
|
if p.config.DownloadManager != nil {
|
|
go func() {
|
|
go func() {
|
|
var (
|
|
var (
|
|
@@ -649,7 +655,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
rootFS image.RootFS
|
|
rootFS image.RootFS
|
|
)
|
|
)
|
|
downloadRootFS := *image.NewRootFS()
|
|
downloadRootFS := *image.NewRootFS()
|
|
- rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, requestedOS, descriptors, p.config.ProgressOutput)
|
|
|
|
|
|
+ rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layerStoreOS, descriptors, p.config.ProgressOutput)
|
|
if err != nil {
|
|
if err != nil {
|
|
// Intentionally do not cancel the config download here
|
|
// Intentionally do not cancel the config download here
|
|
// as the error from config download (if there is one)
|
|
// as the error from config download (if there is one)
|
|
@@ -735,22 +741,22 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan
|
|
|
|
|
|
// pullManifestList handles "manifest lists" which point to various
|
|
// pullManifestList handles "manifest lists" which point to various
|
|
// platform-specific manifests.
|
|
// platform-specific manifests.
|
|
-func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, requestedOS string) (id digest.Digest, manifestListDigest digest.Digest, err error) {
|
|
|
|
|
|
+func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) {
|
|
manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
|
|
manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
|
|
if err != nil {
|
|
if err != nil {
|
|
return "", "", err
|
|
return "", "", err
|
|
}
|
|
}
|
|
|
|
|
|
- logOS := requestedOS // May be "" indicating any OS
|
|
|
|
- if logOS == "" {
|
|
|
|
- logOS = "*"
|
|
|
|
|
|
+ var platform specs.Platform
|
|
|
|
+ if pp != nil {
|
|
|
|
+ platform = *pp
|
|
}
|
|
}
|
|
- logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), logOS, runtime.GOARCH)
|
|
|
|
|
|
+ logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), platforms.Format(platform), runtime.GOARCH)
|
|
|
|
|
|
- manifestMatches := filterManifests(mfstList.Manifests, requestedOS)
|
|
|
|
|
|
+ manifestMatches := filterManifests(mfstList.Manifests, platform)
|
|
|
|
|
|
if len(manifestMatches) == 0 {
|
|
if len(manifestMatches) == 0 {
|
|
- errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", logOS, runtime.GOARCH)
|
|
|
|
|
|
+ errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", platforms.Format(platform))
|
|
logrus.Debugf(errMsg)
|
|
logrus.Debugf(errMsg)
|
|
return "", "", errors.New(errMsg)
|
|
return "", "", errors.New(errMsg)
|
|
}
|
|
}
|
|
@@ -781,12 +787,14 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
|
|
|
|
|
|
switch v := manifest.(type) {
|
|
switch v := manifest.(type) {
|
|
case *schema1.SignedManifest:
|
|
case *schema1.SignedManifest:
|
|
- id, _, err = p.pullSchema1(ctx, manifestRef, v, manifestMatches[0].Platform.OS)
|
|
|
|
|
|
+ platform := toOCIPlatform(manifestMatches[0].Platform)
|
|
|
|
+ id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return "", "", err
|
|
return "", "", err
|
|
}
|
|
}
|
|
case *schema2.DeserializedManifest:
|
|
case *schema2.DeserializedManifest:
|
|
- id, _, err = p.pullSchema2(ctx, manifestRef, v, manifestMatches[0].Platform.OS)
|
|
|
|
|
|
+ platform := toOCIPlatform(manifestMatches[0].Platform)
|
|
|
|
+ id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform)
|
|
if err != nil {
|
|
if err != nil {
|
|
return "", "", err
|
|
return "", "", err
|
|
}
|
|
}
|
|
@@ -956,3 +964,13 @@ func fixManifestLayers(m *schema1.Manifest) error {
|
|
func createDownloadFile() (*os.File, error) {
|
|
func createDownloadFile() (*os.File, error) {
|
|
return ioutil.TempFile("", "GetImageBlob")
|
|
return ioutil.TempFile("", "GetImageBlob")
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform {
|
|
|
|
+ return specs.Platform{
|
|
|
|
+ OS: p.OS,
|
|
|
|
+ Architecture: p.Architecture,
|
|
|
|
+ Variant: p.Variant,
|
|
|
|
+ OSFeatures: p.OSFeatures,
|
|
|
|
+ OSVersion: p.OSVersion,
|
|
|
|
+ }
|
|
|
|
+}
|