Browse code

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 authored on 2017/10/04 08:58:07
Showing 3 changed files
... ...
@@ -708,29 +708,20 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
708 708
 	}
709 709
 
710 710
 	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a os/arch match", ref, len(mfstList.Manifests))
711
-	var manifestDigest digest.Digest
712
-	// TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode.
713
-	lookingForOS := runtime.GOOS
714
-	if system.LCOWSupported() {
715
-		lookingForOS = "linux"
716
-	}
717
-	for _, manifestDescriptor := range mfstList.Manifests {
718
-		// TODO(aaronl): The manifest list spec supports optional
719
-		// "features" and "variant" fields. These are not yet used.
720
-		// Once they are, their values should be interpreted here.
721
-		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
722
-			manifestDigest = manifestDescriptor.Digest
723
-			logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDigest.String())
724
-			break
725
-		}
726
-	}
727 711
 
728
-	if manifestDigest == "" {
712
+	manifestMatches := filterManifests(mfstList.Manifests)
713
+
714
+	if len(manifestMatches) == 0 {
729 715
 		errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", runtime.GOOS, runtime.GOARCH)
730 716
 		logrus.Debugf(errMsg)
731 717
 		return "", "", errors.New(errMsg)
732 718
 	}
733 719
 
720
+	if len(manifestMatches) > 1 {
721
+		logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String())
722
+	}
723
+	manifestDigest := manifestMatches[0].Digest
724
+
734 725
 	manSvc, err := p.repo.Manifests(ctx)
735 726
 	if err != nil {
736 727
 		return "", "", err
... ...
@@ -3,11 +3,27 @@
3 3
 package distribution
4 4
 
5 5
 import (
6
+	"runtime"
7
+
6 8
 	"github.com/docker/distribution"
7 9
 	"github.com/docker/distribution/context"
10
+	"github.com/docker/distribution/manifest/manifestlist"
11
+	"github.com/sirupsen/logrus"
8 12
 )
9 13
 
10 14
 func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
11 15
 	blobs := ld.repo.Blobs(ctx)
12 16
 	return blobs.Open(ctx, ld.digest)
13 17
 }
18
+
19
+func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
20
+	var matches []manifestlist.ManifestDescriptor
21
+	for _, manifestDescriptor := range manifests {
22
+		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
23
+			matches = append(matches, manifestDescriptor)
24
+
25
+			logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
26
+		}
27
+	}
28
+	return matches
29
+}
... ...
@@ -3,13 +3,19 @@
3 3
 package distribution
4 4
 
5 5
 import (
6
+	"fmt"
6 7
 	"net/http"
7 8
 	"os"
9
+	"runtime"
10
+	"sort"
11
+	"strings"
8 12
 
9 13
 	"github.com/docker/distribution"
10 14
 	"github.com/docker/distribution/context"
15
+	"github.com/docker/distribution/manifest/manifestlist"
11 16
 	"github.com/docker/distribution/manifest/schema2"
12 17
 	"github.com/docker/distribution/registry/client/transport"
18
+	"github.com/docker/docker/pkg/system"
13 19
 	"github.com/sirupsen/logrus"
14 20
 )
15 21
 
... ...
@@ -55,3 +61,57 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
55 55
 	}
56 56
 	return rsc, err
57 57
 }
58
+
59
+func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
60
+	version := system.GetOSVersion()
61
+
62
+	// TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode.
63
+	lookingForOS := runtime.GOOS
64
+	osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
65
+	if system.LCOWSupported() {
66
+		lookingForOS = "linux"
67
+		osVersion = ""
68
+	}
69
+
70
+	var matches []manifestlist.ManifestDescriptor
71
+	for _, manifestDescriptor := range manifests {
72
+		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
73
+			if !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) {
74
+				continue
75
+			}
76
+			matches = append(matches, manifestDescriptor)
77
+
78
+			logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
79
+		}
80
+	}
81
+	sort.Stable(manifestsByVersion(matches))
82
+	return matches
83
+}
84
+
85
+func versionMatch(actual, expected string) bool {
86
+	// Check whether actual and expected are equivalent, or whether
87
+	// expected is a version prefix of actual.
88
+	return actual == "" || expected == "" || actual == expected || strings.HasPrefix(actual, expected+".")
89
+}
90
+
91
+type manifestsByVersion []manifestlist.ManifestDescriptor
92
+
93
+func (mbv manifestsByVersion) Less(i, j int) bool {
94
+	if mbv[i].Platform.OSVersion == "" {
95
+		return false
96
+	}
97
+	if mbv[j].Platform.OSVersion == "" {
98
+		return true
99
+	}
100
+	// TODO: Split version by parts and compare
101
+	// TODO: Prefer versions which have a greater version number
102
+	return false
103
+}
104
+
105
+func (mbv manifestsByVersion) Len() int {
106
+	return len(mbv)
107
+}
108
+
109
+func (mbv manifestsByVersion) Swap(i, j int) {
110
+	mbv[i], mbv[j] = mbv[j], mbv[i]
111
+}