2017-05-01 23:17:35 +00:00
|
|
|
package distribution
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
|
|
"github.com/docker/distribution/manifest/schema2"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
|
|
"github.com/docker/docker/api/server/httputils"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
registrytypes "github.com/docker/docker/api/types/registry"
|
2017-05-16 21:22:16 +00:00
|
|
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
2017-05-01 23:17:35 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
if err := httputils.ParseForm(r); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
var (
|
|
|
|
config = &types.AuthConfig{}
|
|
|
|
authEncoded = r.Header.Get("X-Registry-Auth")
|
|
|
|
distributionInspect registrytypes.DistributionInspect
|
|
|
|
)
|
|
|
|
|
|
|
|
if authEncoded != "" {
|
|
|
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
|
|
|
if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
|
|
|
|
// for a search it is not an error if no auth was given
|
|
|
|
// to increase compatibility with the existing api it is defaulting to be empty
|
|
|
|
config = &types.AuthConfig{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
image := vars["name"]
|
|
|
|
|
|
|
|
ref, err := reference.ParseAnyReference(image)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
namedRef, ok := ref.(reference.Named)
|
|
|
|
if !ok {
|
|
|
|
if _, ok := ref.(reference.Digested); ok {
|
|
|
|
// full image ID
|
|
|
|
return errors.Errorf("no manifest found for full image ID")
|
|
|
|
}
|
|
|
|
return errors.Errorf("unknown image reference format: %s", image)
|
|
|
|
}
|
|
|
|
|
|
|
|
distrepo, _, err := s.backend.GetRepository(ctx, namedRef, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-08 23:29:31 +00:00
|
|
|
blobsrvc := distrepo.Blobs(ctx)
|
2017-05-01 23:17:35 +00:00
|
|
|
|
|
|
|
if canonicalRef, ok := namedRef.(reference.Canonical); !ok {
|
|
|
|
namedRef = reference.TagNameOnly(namedRef)
|
|
|
|
|
|
|
|
taggedRef, ok := namedRef.(reference.NamedTagged)
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("image reference not tagged: %s", image)
|
|
|
|
}
|
|
|
|
|
2017-05-16 21:22:16 +00:00
|
|
|
descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag())
|
2017-05-01 23:17:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-16 21:22:16 +00:00
|
|
|
distributionInspect.Descriptor = v1.Descriptor{
|
|
|
|
MediaType: descriptor.MediaType,
|
|
|
|
Digest: descriptor.Digest,
|
|
|
|
Size: descriptor.Size,
|
|
|
|
}
|
2017-05-01 23:17:35 +00:00
|
|
|
} else {
|
2017-05-08 23:29:31 +00:00
|
|
|
// TODO(nishanttotla): Once manifests can be looked up as a blob, the
|
|
|
|
// descriptor should be set using blobsrvc.Stat(ctx, canonicalRef.Digest())
|
|
|
|
// instead of having to manually fill in the fields
|
|
|
|
distributionInspect.Descriptor.Digest = canonicalRef.Digest()
|
2017-05-01 23:17:35 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 23:29:31 +00:00
|
|
|
// we have a digest, so we can retrieve the manifest
|
2017-05-01 23:17:35 +00:00
|
|
|
mnfstsrvc, err := distrepo.Manifests(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-08 23:29:31 +00:00
|
|
|
mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest)
|
2017-05-01 23:17:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-05-08 23:29:31 +00:00
|
|
|
mediaType, payload, err := mnfst.Payload()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// update MediaType because registry might return something incorrect
|
|
|
|
distributionInspect.Descriptor.MediaType = mediaType
|
|
|
|
if distributionInspect.Descriptor.Size == 0 {
|
|
|
|
distributionInspect.Descriptor.Size = int64(len(payload))
|
|
|
|
}
|
|
|
|
|
2017-05-01 23:17:35 +00:00
|
|
|
// retrieve platform information depending on the type of manifest
|
|
|
|
switch mnfstObj := mnfst.(type) {
|
|
|
|
case *manifestlist.DeserializedManifestList:
|
|
|
|
for _, m := range mnfstObj.Manifests {
|
2017-05-16 21:22:16 +00:00
|
|
|
distributionInspect.Platforms = append(distributionInspect.Platforms, v1.Platform{
|
|
|
|
Architecture: m.Platform.Architecture,
|
|
|
|
OS: m.Platform.OS,
|
|
|
|
OSVersion: m.Platform.OSVersion,
|
|
|
|
OSFeatures: m.Platform.OSFeatures,
|
|
|
|
Variant: m.Platform.Variant,
|
|
|
|
})
|
2017-05-01 23:17:35 +00:00
|
|
|
}
|
|
|
|
case *schema2.DeserializedManifest:
|
|
|
|
configJSON, err := blobsrvc.Get(ctx, mnfstObj.Config.Digest)
|
2017-05-16 21:22:16 +00:00
|
|
|
var platform v1.Platform
|
2017-05-01 23:17:35 +00:00
|
|
|
if err == nil {
|
|
|
|
err := json.Unmarshal(configJSON, &platform)
|
2017-05-18 01:42:16 +00:00
|
|
|
if err == nil && (platform.OS != "" || platform.Architecture != "") {
|
2017-05-01 23:17:35 +00:00
|
|
|
distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *schema1.SignedManifest:
|
2017-05-16 21:22:16 +00:00
|
|
|
platform := v1.Platform{
|
2017-05-01 23:17:35 +00:00
|
|
|
Architecture: mnfstObj.Architecture,
|
|
|
|
OS: "linux",
|
|
|
|
}
|
|
|
|
distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
|
|
|
|
}
|
|
|
|
|
|
|
|
return httputils.WriteJSON(w, http.StatusOK, distributionInspect)
|
|
|
|
}
|