github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/api/server/router/distribution/distribution_routes.go (about)

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