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