LCOW: Auto-select OS
Signed-off-by: John Howard <jhoward@microsoft.com> Addresses https://github.com/moby/moby/pull/35089#issuecomment-367802698. This change enables the daemon to automatically select an image under LCOW that can be used if the API doesn't specify an explicit platform. For example: FROM supertest2014/nyan ADD Dockerfile / And docker build . will download the linux image (not a multi-manifest image) And similarly docker pull ubuntu will match linux/amd64
This commit is contained in:
parent
8e610b2b55
commit
35193c0e7d
9 changed files with 49 additions and 40 deletions
|
@ -228,7 +228,7 @@ func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
|
|||
// multi-arch aware yet, it is guaranteed to only hold the OS part here.
|
||||
return d.builder.options.Platform
|
||||
default:
|
||||
return runtime.GOOS
|
||||
return "" // Auto-select
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,9 +247,9 @@ func (d *dispatchRequest) getImageOrStage(name string, stageOS string) (builder.
|
|||
imageImage.OS = runtime.GOOS
|
||||
if runtime.GOOS == "windows" {
|
||||
switch os {
|
||||
case "windows", "":
|
||||
case "windows":
|
||||
return nil, errors.New("Windows does not support FROM scratch")
|
||||
case "linux":
|
||||
case "linux", "":
|
||||
if !system.LCOWSupported() {
|
||||
return nil, errors.New("Linux containers are not supported on this system")
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
|
|||
// Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
|
||||
// leaking of layers.
|
||||
func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
|
||||
if refOrID == "" {
|
||||
if refOrID == "" { // ie FROM scratch
|
||||
if !system.IsOSSupported(opts.OS) {
|
||||
return nil, nil, system.ErrNotSupportedOperatingSystem
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package images // import "github.com/docker/docker/daemon/images"
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -65,11 +64,6 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
|
|||
close(writesDone)
|
||||
}()
|
||||
|
||||
// Default to the host OS platform in case it hasn't been populated with an explicit value.
|
||||
if os == "" {
|
||||
os = runtime.GOOS
|
||||
}
|
||||
|
||||
imagePullConfig := &distribution.ImagePullConfig{
|
||||
Config: distribution.Config{
|
||||
MetaHeaders: metaHeaders,
|
||||
|
|
|
@ -3,7 +3,6 @@ package distribution // import "github.com/docker/docker/distribution"
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api"
|
||||
|
@ -115,11 +114,6 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
|
|||
continue
|
||||
}
|
||||
|
||||
// Make sure we default the OS if it hasn't been supplied
|
||||
if imagePullConfig.OS == "" {
|
||||
imagePullConfig.OS = runtime.GOOS
|
||||
}
|
||||
|
||||
if err := puller.Pull(ctx, ref, imagePullConfig.OS); err != nil {
|
||||
// Was this pull cancelled? If so, don't try to fall
|
||||
// back.
|
||||
|
|
|
@ -509,6 +509,14 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
|
|||
}
|
||||
}
|
||||
|
||||
// In the situation that the API call didn't specify an OS explicitly, but
|
||||
// we support the operating system, switch to that operating system.
|
||||
// eg FROM supertest2014/nyan with no platform specifier, and docker build
|
||||
// with no --platform= flag under LCOW.
|
||||
if requestedOS == "" && system.IsOSSupported(configOS) {
|
||||
requestedOS = configOS
|
||||
}
|
||||
|
||||
// Early bath if the requested OS doesn't match that of the configuration.
|
||||
// This avoids doing the download, only to potentially fail later.
|
||||
if !strings.EqualFold(configOS, requestedOS) {
|
||||
|
@ -618,9 +626,10 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|||
|
||||
// Early bath if the requested OS doesn't match that of the configuration.
|
||||
// This avoids doing the download, only to potentially fail later.
|
||||
if !strings.EqualFold(configPlatform.OS, requestedOS) {
|
||||
if !system.IsOSSupported(configPlatform.OS) {
|
||||
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, requestedOS)
|
||||
}
|
||||
requestedOS = configPlatform.OS
|
||||
|
||||
// Populate diff ids in descriptors to avoid downloading foreign layers
|
||||
// which have been side loaded
|
||||
|
@ -629,6 +638,10 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|||
}
|
||||
}
|
||||
|
||||
if requestedOS == "" {
|
||||
requestedOS = runtime.GOOS
|
||||
}
|
||||
|
||||
if p.config.DownloadManager != nil {
|
||||
go func() {
|
||||
var (
|
||||
|
@ -722,18 +735,22 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan
|
|||
|
||||
// pullManifestList handles "manifest lists" which point to various
|
||||
// platform-specific manifests.
|
||||
func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, os string) (id digest.Digest, manifestListDigest digest.Digest, err error) {
|
||||
func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, requestedOS string) (id digest.Digest, manifestListDigest digest.Digest, err error) {
|
||||
manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), os, runtime.GOARCH)
|
||||
logOS := requestedOS // May be "" indicating any OS
|
||||
if logOS == "" {
|
||||
logOS = "*"
|
||||
}
|
||||
logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), logOS, runtime.GOARCH)
|
||||
|
||||
manifestMatches := filterManifests(mfstList.Manifests, os)
|
||||
manifestMatches := filterManifests(mfstList.Manifests, requestedOS)
|
||||
|
||||
if len(manifestMatches) == 0 {
|
||||
errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", os, runtime.GOARCH)
|
||||
errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", logOS, runtime.GOARCH)
|
||||
logrus.Debugf(errMsg)
|
||||
return "", "", errors.New(errMsg)
|
||||
}
|
||||
|
@ -764,12 +781,12 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
|
|||
|
||||
switch v := manifest.(type) {
|
||||
case *schema1.SignedManifest:
|
||||
id, _, err = p.pullSchema1(ctx, manifestRef, v, os)
|
||||
id, _, err = p.pullSchema1(ctx, manifestRef, v, manifestMatches[0].Platform.OS)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
case *schema2.DeserializedManifest:
|
||||
id, _, err = p.pullSchema2(ctx, manifestRef, v, os)
|
||||
id, _, err = p.pullSchema2(ctx, manifestRef, v, manifestMatches[0].Platform.OS)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
|
|||
return blobs.Open(ctx, ld.digest)
|
||||
}
|
||||
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, _ string) []manifestlist.ManifestDescriptor {
|
||||
var matches []manifestlist.ManifestDescriptor
|
||||
for _, manifestDescriptor := range manifests {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
|
||||
matches = append(matches, manifestDescriptor)
|
||||
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
}
|
||||
}
|
||||
return matches
|
||||
|
|
|
@ -62,24 +62,27 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
|
|||
return rsc, err
|
||||
}
|
||||
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
|
||||
osVersion := ""
|
||||
if os == "windows" {
|
||||
version := system.GetOSVersion()
|
||||
osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
|
||||
logrus.Debugf("will prefer entries with version %s", osVersion)
|
||||
}
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, requestedOS string) []manifestlist.ManifestDescriptor {
|
||||
version := system.GetOSVersion()
|
||||
osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
|
||||
logrus.Debugf("will prefer Windows entries with version %s", osVersion)
|
||||
|
||||
var matches []manifestlist.ManifestDescriptor
|
||||
foundWindowsMatch := false
|
||||
for _, manifestDescriptor := range manifests {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
|
||||
if (manifestDescriptor.Platform.Architecture == runtime.GOARCH) &&
|
||||
((requestedOS != "" && manifestDescriptor.Platform.OS == requestedOS) || // Explicit user request for an OS we know we support
|
||||
(requestedOS == "" && system.IsOSSupported(manifestDescriptor.Platform.OS))) { // No user requested OS, but one we can support
|
||||
matches = append(matches, manifestDescriptor)
|
||||
logrus.Debugf("found match for %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("found match %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
if strings.EqualFold("windows", manifestDescriptor.Platform.OS) {
|
||||
foundWindowsMatch = true
|
||||
}
|
||||
} else {
|
||||
logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, manifestDescriptor.Platform.Architecture, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
}
|
||||
}
|
||||
if os == "windows" {
|
||||
if foundWindowsMatch {
|
||||
sort.Stable(manifestsByVersion{osVersion, matches})
|
||||
}
|
||||
return matches
|
||||
|
|
|
@ -76,7 +76,8 @@ func (is *store) restore() error {
|
|||
var l layer.Layer
|
||||
if chainID := img.RootFS.ChainID(); chainID != "" {
|
||||
if !system.IsOSSupported(img.OperatingSystem()) {
|
||||
return system.ErrNotSupportedOperatingSystem
|
||||
logrus.Errorf("not restoring image with unsupported operating system %v, %v, %s", dgst, chainID, img.OperatingSystem())
|
||||
return nil
|
||||
}
|
||||
l, err = is.lss[img.OperatingSystem()].Get(chainID)
|
||||
if err != nil {
|
||||
|
|
|
@ -59,10 +59,10 @@ func ParsePlatform(in string) *specs.Platform {
|
|||
|
||||
// IsOSSupported determines if an operating system is supported by the host
|
||||
func IsOSSupported(os string) bool {
|
||||
if runtime.GOOS == os {
|
||||
if strings.EqualFold(runtime.GOOS, os) {
|
||||
return true
|
||||
}
|
||||
if LCOWSupported() && os == "linux" {
|
||||
if LCOWSupported() && strings.EqualFold(os, "linux") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
Loading…
Add table
Reference in a new issue