github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/distribution/pull_v2_windows.go (about) 1 package distribution // import "github.com/docker/docker/distribution" 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "os" 9 "runtime" 10 "sort" 11 "strconv" 12 "strings" 13 14 "github.com/containerd/containerd/platforms" 15 "github.com/docker/distribution" 16 "github.com/docker/distribution/manifest/manifestlist" 17 "github.com/docker/distribution/manifest/schema2" 18 "github.com/docker/distribution/registry/client/transport" 19 "github.com/docker/docker/pkg/system" 20 specs "github.com/opencontainers/image-spec/specs-go/v1" 21 "github.com/sirupsen/logrus" 22 ) 23 24 var _ distribution.Describable = &v2LayerDescriptor{} 25 26 func (ld *v2LayerDescriptor) Descriptor() distribution.Descriptor { 27 if ld.src.MediaType == schema2.MediaTypeForeignLayer && len(ld.src.URLs) > 0 { 28 return ld.src 29 } 30 return distribution.Descriptor{} 31 } 32 33 func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) { 34 blobs := ld.repo.Blobs(ctx) 35 rsc, err := blobs.Open(ctx, ld.digest) 36 37 if len(ld.src.URLs) == 0 { 38 return rsc, err 39 } 40 41 // We're done if the registry has this blob. 42 if err == nil { 43 // Seek does an HTTP GET. If it succeeds, the blob really is accessible. 44 if _, err = rsc.Seek(0, os.SEEK_SET); err == nil { 45 return rsc, nil 46 } 47 rsc.Close() 48 } 49 50 // Find the first URL that results in a 200 result code. 51 for _, url := range ld.src.URLs { 52 logrus.Debugf("Pulling %v from foreign URL %v", ld.digest, url) 53 rsc = transport.NewHTTPReadSeeker(http.DefaultClient, url, nil) 54 55 // Seek does an HTTP GET. If it succeeds, the blob really is accessible. 56 _, err = rsc.Seek(0, os.SEEK_SET) 57 if err == nil { 58 break 59 } 60 logrus.Debugf("Download for %v failed: %v", ld.digest, err) 61 rsc.Close() 62 rsc = nil 63 } 64 return rsc, err 65 } 66 67 func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor { 68 version := system.GetOSVersion() 69 osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) 70 logrus.Debugf("will prefer Windows entries with version %s", osVersion) 71 72 var matches []manifestlist.ManifestDescriptor 73 foundWindowsMatch := false 74 for _, manifestDescriptor := range manifests { 75 if (manifestDescriptor.Platform.Architecture == runtime.GOARCH) && 76 ((p.OS != "" && manifestDescriptor.Platform.OS == p.OS) || // Explicit user request for an OS we know we support 77 (p.OS == "" && system.IsOSSupported(manifestDescriptor.Platform.OS))) { // No user requested OS, but one we can support 78 if strings.EqualFold("windows", manifestDescriptor.Platform.OS) { 79 if err := checkImageCompatibility("windows", manifestDescriptor.Platform.OSVersion); err != nil { 80 continue 81 } 82 foundWindowsMatch = true 83 } 84 matches = append(matches, manifestDescriptor) 85 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()) 86 } else { 87 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()) 88 } 89 } 90 if foundWindowsMatch { 91 sort.Stable(manifestsByVersion{osVersion, matches}) 92 } 93 return matches 94 } 95 96 func versionMatch(actual, expected string) bool { 97 // Check whether the version matches up to the build, ignoring UBR 98 return strings.HasPrefix(actual, expected+".") 99 } 100 101 type manifestsByVersion struct { 102 version string 103 list []manifestlist.ManifestDescriptor 104 } 105 106 func (mbv manifestsByVersion) Less(i, j int) bool { 107 // TODO: Split version by parts and compare 108 // TODO: Prefer versions which have a greater version number 109 // Move compatible versions to the top, with no other ordering changes 110 return (strings.EqualFold("windows", mbv.list[i].Platform.OS) && !strings.EqualFold("windows", mbv.list[j].Platform.OS)) || 111 (versionMatch(mbv.list[i].Platform.OSVersion, mbv.version) && !versionMatch(mbv.list[j].Platform.OSVersion, mbv.version)) 112 } 113 114 func (mbv manifestsByVersion) Len() int { 115 return len(mbv.list) 116 } 117 118 func (mbv manifestsByVersion) Swap(i, j int) { 119 mbv.list[i], mbv.list[j] = mbv.list[j], mbv.list[i] 120 } 121 122 // checkImageCompatibility blocks pulling incompatible images based on a later OS build 123 // Fixes https://github.com/moby/moby/issues/36184. 124 func checkImageCompatibility(imageOS, imageOSVersion string) error { 125 if imageOS == "windows" { 126 hostOSV := system.GetOSVersion() 127 splitImageOSVersion := strings.Split(imageOSVersion, ".") // eg 10.0.16299.nnnn 128 if len(splitImageOSVersion) >= 3 { 129 if imageOSBuild, err := strconv.Atoi(splitImageOSVersion[2]); err == nil { 130 if imageOSBuild > int(hostOSV.Build) { 131 errMsg := fmt.Sprintf("a Windows version %s.%s.%s-based image is incompatible with a %s host", splitImageOSVersion[0], splitImageOSVersion[1], splitImageOSVersion[2], hostOSV.ToString()) 132 logrus.Debugf(errMsg) 133 return errors.New(errMsg) 134 } 135 } 136 } 137 } 138 return nil 139 } 140 141 func formatPlatform(platform specs.Platform) string { 142 if platform.OS == "" { 143 platform = platforms.DefaultSpec() 144 } 145 return fmt.Sprintf("%s %s", platforms.Format(platform), system.GetOSVersion().ToString()) 146 }