distribution_routes.go 4.5 KB

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