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  }