github.com/cookieai-jar/moby@v17.12.1-ce-rc2+incompatible/api/server/router/distribution/distribution_routes.go (about)

     1  package distribution
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/docker/distribution/manifest/manifestlist"
    10  	"github.com/docker/distribution/manifest/schema1"
    11  	"github.com/docker/distribution/manifest/schema2"
    12  	"github.com/docker/distribution/reference"
    13  	"github.com/docker/docker/api/server/httputils"
    14  	"github.com/docker/docker/api/types"
    15  	registrytypes "github.com/docker/docker/api/types/registry"
    16  	"github.com/opencontainers/image-spec/specs-go/v1"
    17  	"github.com/pkg/errors"
    18  	"golang.org/x/net/context"
    19  )
    20  
    21  func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    22  	if err := httputils.ParseForm(r); err != nil {
    23  		return err
    24  	}
    25  
    26  	w.Header().Set("Content-Type", "application/json")
    27  
    28  	var (
    29  		config              = &types.AuthConfig{}
    30  		authEncoded         = r.Header.Get("X-Registry-Auth")
    31  		distributionInspect registrytypes.DistributionInspect
    32  	)
    33  
    34  	if authEncoded != "" {
    35  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
    36  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
    37  			// for a search it is not an error if no auth was given
    38  			// to increase compatibility with the existing api it is defaulting to be empty
    39  			config = &types.AuthConfig{}
    40  		}
    41  	}
    42  
    43  	image := vars["name"]
    44  
    45  	ref, err := reference.ParseAnyReference(image)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	namedRef, ok := ref.(reference.Named)
    50  	if !ok {
    51  		if _, ok := ref.(reference.Digested); ok {
    52  			// full image ID
    53  			return errors.Errorf("no manifest found for full image ID")
    54  		}
    55  		return errors.Errorf("unknown image reference format: %s", image)
    56  	}
    57  
    58  	distrepo, _, err := s.backend.GetRepository(ctx, namedRef, config)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	blobsrvc := distrepo.Blobs(ctx)
    63  
    64  	if canonicalRef, ok := namedRef.(reference.Canonical); !ok {
    65  		namedRef = reference.TagNameOnly(namedRef)
    66  
    67  		taggedRef, ok := namedRef.(reference.NamedTagged)
    68  		if !ok {
    69  			return errors.Errorf("image reference not tagged: %s", image)
    70  		}
    71  
    72  		descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag())
    73  		if err != nil {
    74  			return err
    75  		}
    76  		distributionInspect.Descriptor = v1.Descriptor{
    77  			MediaType: descriptor.MediaType,
    78  			Digest:    descriptor.Digest,
    79  			Size:      descriptor.Size,
    80  		}
    81  	} else {
    82  		// TODO(nishanttotla): Once manifests can be looked up as a blob, the
    83  		// descriptor should be set using blobsrvc.Stat(ctx, canonicalRef.Digest())
    84  		// instead of having to manually fill in the fields
    85  		distributionInspect.Descriptor.Digest = canonicalRef.Digest()
    86  	}
    87  
    88  	// we have a digest, so we can retrieve the manifest
    89  	mnfstsrvc, err := distrepo.Manifests(ctx)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	mediaType, payload, err := mnfst.Payload()
    99  	if err != nil {
   100  		return err
   101  	}
   102  	// update MediaType because registry might return something incorrect
   103  	distributionInspect.Descriptor.MediaType = mediaType
   104  	if distributionInspect.Descriptor.Size == 0 {
   105  		distributionInspect.Descriptor.Size = int64(len(payload))
   106  	}
   107  
   108  	// retrieve platform information depending on the type of manifest
   109  	switch mnfstObj := mnfst.(type) {
   110  	case *manifestlist.DeserializedManifestList:
   111  		for _, m := range mnfstObj.Manifests {
   112  			distributionInspect.Platforms = append(distributionInspect.Platforms, v1.Platform{
   113  				Architecture: m.Platform.Architecture,
   114  				OS:           m.Platform.OS,
   115  				OSVersion:    m.Platform.OSVersion,
   116  				OSFeatures:   m.Platform.OSFeatures,
   117  				Variant:      m.Platform.Variant,
   118  			})
   119  		}
   120  	case *schema2.DeserializedManifest:
   121  		configJSON, err := blobsrvc.Get(ctx, mnfstObj.Config.Digest)
   122  		var platform v1.Platform
   123  		if err == nil {
   124  			err := json.Unmarshal(configJSON, &platform)
   125  			if err == nil && (platform.OS != "" || platform.Architecture != "") {
   126  				distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
   127  			}
   128  		}
   129  	case *schema1.SignedManifest:
   130  		platform := v1.Platform{
   131  			Architecture: mnfstObj.Architecture,
   132  			OS:           "linux",
   133  		}
   134  		distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
   135  	}
   136  
   137  	return httputils.WriteJSON(w, http.StatusOK, distributionInspect)
   138  }