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