github.com/rish1988/moby@v25.0.2+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/json" 6 "net/http" 7 8 "github.com/distribution/reference" 9 "github.com/docker/distribution" 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/docker/api/server/httputils" 14 "github.com/docker/docker/api/types/registry" 15 "github.com/docker/docker/errdefs" 16 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 17 "github.com/pkg/errors" 18 ) 19 20 func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 21 if err := httputils.ParseForm(r); err != nil { 22 return err 23 } 24 25 w.Header().Set("Content-Type", "application/json") 26 27 imgName := vars["name"] 28 29 // TODO why is reference.ParseAnyReference() / reference.ParseNormalizedNamed() not using the reference.ErrTagInvalidFormat (and so on) errors? 30 ref, err := reference.ParseAnyReference(imgName) 31 if err != nil { 32 return errdefs.InvalidParameter(err) 33 } 34 namedRef, ok := ref.(reference.Named) 35 if !ok { 36 if _, ok := ref.(reference.Digested); ok { 37 // full image ID 38 return errors.Errorf("no manifest found for full image ID") 39 } 40 return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", imgName)) 41 } 42 43 // For a search it is not an error if no auth was given. Ignore invalid 44 // AuthConfig to increase compatibility with the existing API. 45 authConfig, _ := registry.DecodeAuthConfig(r.Header.Get(registry.AuthHeader)) 46 repos, err := s.backend.GetRepositories(ctx, namedRef, authConfig) 47 if err != nil { 48 return err 49 } 50 51 // Fetch the manifest; if a mirror is configured, try the mirror first, 52 // but continue with upstream on failure. 53 // 54 // FIXME(thaJeztah): construct "repositories" on-demand; 55 // GetRepositories() will attempt to connect to all endpoints (registries), 56 // but we may only need the first one if it contains the manifest we're 57 // looking for, or if the configured mirror is a pull-through mirror. 58 // 59 // Logic for this could be implemented similar to "distribution.Pull()", 60 // which uses the "pullEndpoints" utility to iterate over the list 61 // of endpoints; 62 // 63 // - https://github.com/moby/moby/blob/12c7411b6b7314bef130cd59f1c7384a7db06d0b/distribution/pull.go#L17-L31 64 // - https://github.com/moby/moby/blob/12c7411b6b7314bef130cd59f1c7384a7db06d0b/distribution/pull.go#L76-L152 65 var lastErr error 66 for _, repo := range repos { 67 distributionInspect, err := s.fetchManifest(ctx, repo, namedRef) 68 if err != nil { 69 lastErr = err 70 continue 71 } 72 return httputils.WriteJSON(w, http.StatusOK, distributionInspect) 73 } 74 return lastErr 75 } 76 77 func (s *distributionRouter) fetchManifest(ctx context.Context, distrepo distribution.Repository, namedRef reference.Named) (registry.DistributionInspect, error) { 78 var distributionInspect registry.DistributionInspect 79 if canonicalRef, ok := namedRef.(reference.Canonical); !ok { 80 namedRef = reference.TagNameOnly(namedRef) 81 82 taggedRef, ok := namedRef.(reference.NamedTagged) 83 if !ok { 84 return registry.DistributionInspect{}, errdefs.InvalidParameter(errors.Errorf("image reference not tagged: %s", namedRef)) 85 } 86 87 descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag()) 88 if err != nil { 89 return registry.DistributionInspect{}, err 90 } 91 distributionInspect.Descriptor = ocispec.Descriptor{ 92 MediaType: descriptor.MediaType, 93 Digest: descriptor.Digest, 94 Size: descriptor.Size, 95 } 96 } else { 97 // TODO(nishanttotla): Once manifests can be looked up as a blob, the 98 // descriptor should be set using blobsrvc.Stat(ctx, canonicalRef.Digest()) 99 // instead of having to manually fill in the fields 100 distributionInspect.Descriptor.Digest = canonicalRef.Digest() 101 } 102 103 // we have a digest, so we can retrieve the manifest 104 mnfstsrvc, err := distrepo.Manifests(ctx) 105 if err != nil { 106 return registry.DistributionInspect{}, err 107 } 108 mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest) 109 if err != nil { 110 switch err { 111 case reference.ErrReferenceInvalidFormat, 112 reference.ErrTagInvalidFormat, 113 reference.ErrDigestInvalidFormat, 114 reference.ErrNameContainsUppercase, 115 reference.ErrNameEmpty, 116 reference.ErrNameTooLong, 117 reference.ErrNameNotCanonical: 118 return registry.DistributionInspect{}, errdefs.InvalidParameter(err) 119 } 120 return registry.DistributionInspect{}, err 121 } 122 123 mediaType, payload, err := mnfst.Payload() 124 if err != nil { 125 return registry.DistributionInspect{}, err 126 } 127 // update MediaType because registry might return something incorrect 128 distributionInspect.Descriptor.MediaType = mediaType 129 if distributionInspect.Descriptor.Size == 0 { 130 distributionInspect.Descriptor.Size = int64(len(payload)) 131 } 132 133 // retrieve platform information depending on the type of manifest 134 switch mnfstObj := mnfst.(type) { 135 case *manifestlist.DeserializedManifestList: 136 for _, m := range mnfstObj.Manifests { 137 distributionInspect.Platforms = append(distributionInspect.Platforms, ocispec.Platform{ 138 Architecture: m.Platform.Architecture, 139 OS: m.Platform.OS, 140 OSVersion: m.Platform.OSVersion, 141 OSFeatures: m.Platform.OSFeatures, 142 Variant: m.Platform.Variant, 143 }) 144 } 145 case *schema2.DeserializedManifest: 146 blobStore := distrepo.Blobs(ctx) 147 configJSON, err := blobStore.Get(ctx, mnfstObj.Config.Digest) 148 var platform ocispec.Platform 149 if err == nil { 150 err := json.Unmarshal(configJSON, &platform) 151 if err == nil && (platform.OS != "" || platform.Architecture != "") { 152 distributionInspect.Platforms = append(distributionInspect.Platforms, platform) 153 } 154 } 155 case *schema1.SignedManifest: 156 platform := ocispec.Platform{ 157 Architecture: mnfstObj.Architecture, 158 OS: "linux", 159 } 160 distributionInspect.Platforms = append(distributionInspect.Platforms, platform) 161 } 162 return distributionInspect, nil 163 }