github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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 }