Browse Source

Add support for Windows version filtering on pull

Update logic to choose manifest from manifest list to check
for os version on Windows. Separate the logic for windows
and unix to keep unix logic the same.


Signed-off-by: Derek McGowan <derek@mcgstyle.net>
Derek McGowan 7 years ago
parent
commit
38aef56e1f
3 changed files with 84 additions and 17 deletions
  1. 8 17
      distribution/pull_v2.go
  2. 16 0
      distribution/pull_v2_unix.go
  3. 60 0
      distribution/pull_v2_windows.go

+ 8 - 17
distribution/pull_v2.go

@@ -708,29 +708,20 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
 	}
 	}
 
 
 	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a os/arch match", ref, len(mfstList.Manifests))
 	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a os/arch match", ref, len(mfstList.Manifests))
-	var manifestDigest digest.Digest
-	// TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode.
-	lookingForOS := runtime.GOOS
-	if system.LCOWSupported() {
-		lookingForOS = "linux"
-	}
-	for _, manifestDescriptor := range mfstList.Manifests {
-		// TODO(aaronl): The manifest list spec supports optional
-		// "features" and "variant" fields. These are not yet used.
-		// Once they are, their values should be interpreted here.
-		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
-			manifestDigest = manifestDescriptor.Digest
-			logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDigest.String())
-			break
-		}
-	}
 
 
-	if manifestDigest == "" {
+	manifestMatches := filterManifests(mfstList.Manifests)
+
+	if len(manifestMatches) == 0 {
 		errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", runtime.GOOS, runtime.GOARCH)
 		errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", runtime.GOOS, runtime.GOARCH)
 		logrus.Debugf(errMsg)
 		logrus.Debugf(errMsg)
 		return "", "", errors.New(errMsg)
 		return "", "", errors.New(errMsg)
 	}
 	}
 
 
+	if len(manifestMatches) > 1 {
+		logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String())
+	}
+	manifestDigest := manifestMatches[0].Digest
+
 	manSvc, err := p.repo.Manifests(ctx)
 	manSvc, err := p.repo.Manifests(ctx)
 	if err != nil {
 	if err != nil {
 		return "", "", err
 		return "", "", err

+ 16 - 0
distribution/pull_v2_unix.go

@@ -3,11 +3,27 @@
 package distribution
 package distribution
 
 
 import (
 import (
+	"runtime"
+
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
 	"github.com/docker/distribution/context"
+	"github.com/docker/distribution/manifest/manifestlist"
+	"github.com/sirupsen/logrus"
 )
 )
 
 
 func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
 func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
 	blobs := ld.repo.Blobs(ctx)
 	blobs := ld.repo.Blobs(ctx)
 	return blobs.Open(ctx, ld.digest)
 	return blobs.Open(ctx, ld.digest)
 }
 }
+
+func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
+	var matches []manifestlist.ManifestDescriptor
+	for _, manifestDescriptor := range manifests {
+		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", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
+		}
+	}
+	return matches
+}

+ 60 - 0
distribution/pull_v2_windows.go

@@ -3,13 +3,19 @@
 package distribution
 package distribution
 
 
 import (
 import (
+	"fmt"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
+	"runtime"
+	"sort"
+	"strings"
 
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
 	"github.com/docker/distribution/context"
+	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
+	"github.com/docker/docker/pkg/system"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
 
 
@@ -55,3 +61,57 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
 	}
 	}
 	return rsc, err
 	return rsc, err
 }
 }
+
+func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
+	version := system.GetOSVersion()
+
+	// TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode.
+	lookingForOS := runtime.GOOS
+	osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
+	if system.LCOWSupported() {
+		lookingForOS = "linux"
+		osVersion = ""
+	}
+
+	var matches []manifestlist.ManifestDescriptor
+	for _, manifestDescriptor := range manifests {
+		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
+			if !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) {
+				continue
+			}
+			matches = append(matches, manifestDescriptor)
+
+			logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
+		}
+	}
+	sort.Stable(manifestsByVersion(matches))
+	return matches
+}
+
+func versionMatch(actual, expected string) bool {
+	// Check whether actual and expected are equivalent, or whether
+	// expected is a version prefix of actual.
+	return actual == "" || expected == "" || actual == expected || strings.HasPrefix(actual, expected+".")
+}
+
+type manifestsByVersion []manifestlist.ManifestDescriptor
+
+func (mbv manifestsByVersion) Less(i, j int) bool {
+	if mbv[i].Platform.OSVersion == "" {
+		return false
+	}
+	if mbv[j].Platform.OSVersion == "" {
+		return true
+	}
+	// TODO: Split version by parts and compare
+	// TODO: Prefer versions which have a greater version number
+	return false
+}
+
+func (mbv manifestsByVersion) Len() int {
+	return len(mbv)
+}
+
+func (mbv manifestsByVersion) Swap(i, j int) {
+	mbv[i], mbv[j] = mbv[j], mbv[i]
+}