Skip to content

Commit 4d67c5f

Browse files
author
Michal Minář
committed
Registry: moved manifest schema operations to new files
Signed-off-by: Michal Minář <[email protected]>
1 parent 5ffd95f commit 4d67c5f

File tree

4 files changed

+234
-171
lines changed

4 files changed

+234
-171
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package server
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/docker/distribution"
7+
"github.com/docker/distribution/context"
8+
"github.com/docker/distribution/manifest/schema1"
9+
"github.com/docker/distribution/manifest/schema2"
10+
11+
imageapi "github.com/openshift/origin/pkg/image/api"
12+
)
13+
14+
// A ManifestHandler defines a common set of operations on all versions of manifest schema.
15+
type ManifestHandler interface {
16+
// FillImageMetadata fills a given image with metadata parsed from manifest. It also corrects layer sizes
17+
// with blob sizes. Newer Docker client versions don't set layer sizes in the manifest schema 1 at all.
18+
// Origin master needs correct layer sizes for proper image quota support. That's why we need to fill the
19+
// metadata in the registry.
20+
FillImageMetadata(ctx context.Context, image *imageapi.Image) error
21+
22+
// Manifest returns a deserialized manifest object.
23+
Manifest() distribution.Manifest
24+
25+
// Payload returns manifest's media type, complete payload with signatures and canonical payload without
26+
// signatures or an error if the information could not be fetched.
27+
Payload() (mediaType string, payload []byte, canonical []byte, err error)
28+
}
29+
30+
// NewManifestHandler creates a manifest handler for the given manifest.
31+
func NewManifestHandler(repo *repository, manifest distribution.Manifest) (ManifestHandler, error) {
32+
switch t := manifest.(type) {
33+
case *schema1.SignedManifest:
34+
return &manifestSchema1Handler{repo: repo, manifest: t}, nil
35+
case *schema2.DeserializedManifest:
36+
return &manifestSchema2Handler{repo: repo, manifest: t}, nil
37+
default:
38+
return nil, fmt.Errorf("unsupported manifest type %T", manifest)
39+
}
40+
}
41+
42+
// NewManifestHandlerFromImage creates a new manifest handler for a manifest stored in the given image.
43+
func NewManifestHandlerFromImage(repo *repository, image *imageapi.Image) (ManifestHandler, error) {
44+
var (
45+
manifest distribution.Manifest
46+
err error
47+
)
48+
49+
switch image.DockerImageManifestMediaType {
50+
case "", schema1.MediaTypeManifest:
51+
manifest, err = unmarshalManifestSchema1([]byte(image.DockerImageManifest), image.DockerImageSignatures)
52+
case schema2.MediaTypeManifest:
53+
manifest, err = unmarshalManifestSchema2([]byte(image.DockerImageManifest))
54+
default:
55+
return nil, fmt.Errorf("unsupported manifest media type %s", image.DockerImageManifestMediaType)
56+
}
57+
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return NewManifestHandler(repo, manifest)
63+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package server
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/docker/distribution"
7+
"github.com/docker/distribution/context"
8+
"github.com/docker/distribution/manifest/schema1"
9+
"github.com/docker/libtrust"
10+
11+
"k8s.io/kubernetes/pkg/util/sets"
12+
13+
imageapi "github.com/openshift/origin/pkg/image/api"
14+
)
15+
16+
func unmarshalManifestSchema1(content []byte, signatures [][]byte) (distribution.Manifest, error) {
17+
// prefer signatures from the manifest
18+
if _, err := libtrust.ParsePrettySignature(content, "signatures"); err == nil {
19+
sm := schema1.SignedManifest{Canonical: content}
20+
if err = json.Unmarshal(content, &sm); err == nil {
21+
return &sm, nil
22+
}
23+
}
24+
25+
jsig, err := libtrust.NewJSONSignature(content, signatures...)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
// Extract the pretty JWS
31+
content, err = jsig.PrettySignature("signatures")
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
var sm schema1.SignedManifest
37+
if err = json.Unmarshal(content, &sm); err != nil {
38+
return nil, err
39+
}
40+
return &sm, nil
41+
}
42+
43+
type manifestSchema1Handler struct {
44+
repo *repository
45+
manifest *schema1.SignedManifest
46+
}
47+
48+
var _ ManifestHandler = &manifestSchema1Handler{}
49+
50+
func (h *manifestSchema1Handler) FillImageMetadata(ctx context.Context, image *imageapi.Image) error {
51+
signatures, err := h.manifest.Signatures()
52+
if err != nil {
53+
return err
54+
}
55+
56+
for _, signDigest := range signatures {
57+
image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest)
58+
}
59+
60+
if err := imageapi.ImageWithMetadata(image); err != nil {
61+
return err
62+
}
63+
64+
refs := h.manifest.References()
65+
66+
blobSet := sets.NewString()
67+
image.DockerImageMetadata.Size = int64(0)
68+
69+
blobs := h.repo.Blobs(ctx)
70+
for i := range image.DockerImageLayers {
71+
layer := &image.DockerImageLayers[i]
72+
// DockerImageLayers represents h.manifest.Manifest.FSLayers in reversed order
73+
desc, err := blobs.Stat(ctx, refs[len(image.DockerImageLayers)-i-1].Digest)
74+
if err != nil {
75+
context.GetLogger(ctx).Errorf("failed to stat blob %s of image %s", layer.Name, image.DockerImageReference)
76+
return err
77+
}
78+
// The MediaType appeared in manifest schema v2. We need to fill it
79+
// manually in the old images if it is not already filled.
80+
if len(layer.MediaType) == 0 {
81+
if len(desc.MediaType) > 0 {
82+
layer.MediaType = desc.MediaType
83+
} else {
84+
layer.MediaType = schema1.MediaTypeManifestLayer
85+
}
86+
}
87+
layer.LayerSize = desc.Size
88+
// count empty layer just once (empty layer may actually have non-zero size)
89+
if !blobSet.Has(layer.Name) {
90+
image.DockerImageMetadata.Size += desc.Size
91+
blobSet.Insert(layer.Name)
92+
}
93+
}
94+
95+
return nil
96+
}
97+
98+
func (h *manifestSchema1Handler) Manifest() distribution.Manifest {
99+
return h.manifest
100+
}
101+
102+
func (h *manifestSchema1Handler) Payload() (mediaType string, payload []byte, canonical []byte, err error) {
103+
mt, payload, err := h.manifest.Payload()
104+
return mt, payload, h.manifest.Canonical, err
105+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package server
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/docker/distribution"
7+
"github.com/docker/distribution/context"
8+
"github.com/docker/distribution/manifest/schema2"
9+
10+
imageapi "github.com/openshift/origin/pkg/image/api"
11+
)
12+
13+
func unmarshalManifestSchema2(content []byte) (distribution.Manifest, error) {
14+
var deserializedManifest schema2.DeserializedManifest
15+
if err := json.Unmarshal(content, &deserializedManifest); err != nil {
16+
return nil, err
17+
}
18+
19+
return &deserializedManifest, nil
20+
}
21+
22+
type manifestSchema2Handler struct {
23+
repo *repository
24+
manifest *schema2.DeserializedManifest
25+
}
26+
27+
var _ ManifestHandler = &manifestSchema2Handler{}
28+
29+
func (h *manifestSchema2Handler) FillImageMetadata(ctx context.Context, image *imageapi.Image) error {
30+
// The manifest.Config references a configuration object for a container by its digest.
31+
// It needs to be fetched in order to fill an image object metadata below.
32+
configBytes, err := h.repo.Blobs(ctx).Get(ctx, h.manifest.Config.Digest)
33+
if err != nil {
34+
context.GetLogger(ctx).Errorf("failed to get image config %s: %v", h.manifest.Config.Digest.String(), err)
35+
return err
36+
}
37+
image.DockerImageConfig = string(configBytes)
38+
39+
if err := imageapi.ImageWithMetadata(image); err != nil {
40+
return err
41+
}
42+
43+
return nil
44+
}
45+
46+
func (h *manifestSchema2Handler) Manifest() distribution.Manifest {
47+
return h.manifest
48+
}
49+
50+
func (h *manifestSchema2Handler) Payload() (mediaType string, payload []byte, canonical []byte, err error) {
51+
mt, p, err := h.manifest.Payload()
52+
return mt, p, p, err
53+
}

0 commit comments

Comments
 (0)