distribution_routes.go 4.8 KB

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