Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pkg/dockerregistry/server/manifesthandler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
"errors"
"fmt"

"github.com/docker/distribution"
Expand All @@ -9,9 +10,13 @@ import (
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"

imageapi "github.com/openshift/origin/pkg/image/apis/image"
imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1"
)

// ErrNotImplemented is returned by an interface instance that does not implement the method in question.
var ErrNotImplemented = errors.New("not implemented")

// A ManifestHandler defines a common set of operations on all versions of manifest schema.
type ManifestHandler interface {
// Config returns a blob with image configuration associated with the manifest. This applies only to
Expand All @@ -21,6 +26,12 @@ type ManifestHandler interface {
// Digest returns manifest's digest.
Digest() (manifestDigest digest.Digest, err error)

// Layers returns a list of image layers.
Layers(ctx context.Context) ([]imageapiv1.ImageLayer, error)

// Metadata returns image configuration in internal representation.
Metadata(ctx context.Context) (*imageapi.DockerImage, error)

// Manifest returns a deserialized manifest object.
Manifest() distribution.Manifest

Expand Down Expand Up @@ -52,7 +63,7 @@ func NewManifestHandlerFromImage(repo *repository, image *imageapiv1.Image) (Man
)

switch image.DockerImageManifestMediaType {
case "", schema1.MediaTypeManifest:
case "", schema1.MediaTypeManifest, schema1.MediaTypeSignedManifest:
manifest, err = unmarshalManifestSchema1([]byte(image.DockerImageManifest), image.DockerImageSignatures)
case schema2.MediaTypeManifest:
manifest, err = unmarshalManifestSchema2([]byte(image.DockerImageManifest))
Expand Down
112 changes: 110 additions & 2 deletions pkg/dockerregistry/server/manifestschema1handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"encoding/json"
"errors"
"fmt"
"path"

Expand All @@ -11,8 +12,16 @@ import (
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/reference"
"github.com/docker/libtrust"

"k8s.io/apimachinery/pkg/util/sets"

imageapi "github.com/openshift/origin/pkg/image/apis/image"
imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1"
)

// ErrNoManifestMetadata is an error informing about invalid manifest that lacks metadata.
var ErrNoManifestMetadata = errors.New("no manifest metadata found")

func unmarshalManifestSchema1(content []byte, signatures [][]byte) (distribution.Manifest, error) {
// prefer signatures from the manifest
if _, err := libtrust.ParsePrettySignature(content, "signatures"); err == nil {
Expand Down Expand Up @@ -41,8 +50,9 @@ func unmarshalManifestSchema1(content []byte, signatures [][]byte) (distribution
}

type manifestSchema1Handler struct {
repo *repository
manifest *schema1.SignedManifest
repo *repository
manifest *schema1.SignedManifest
cachedLayers []imageapiv1.ImageLayer
}

var _ ManifestHandler = &manifestSchema1Handler{}
Expand All @@ -55,10 +65,86 @@ func (h *manifestSchema1Handler) Digest() (digest.Digest, error) {
return digest.FromBytes(h.manifest.Canonical), nil
}

func (h *manifestSchema1Handler) Layers(ctx context.Context) ([]imageapiv1.ImageLayer, error) {
if h.cachedLayers == nil {
var sizeContainer = imageapi.DockerV1CompatibilityImageSize{}

layers := make([]imageapiv1.ImageLayer, len(h.manifest.FSLayers))
for hi, li := 0, len(h.manifest.FSLayers)-1; hi < len(h.manifest.FSLayers) && li >= 0; hi, li = hi+1, li-1 {
layer := &layers[li]
sizeContainer.Size = 0
if hi < len(h.manifest.History) {
if err := json.Unmarshal([]byte(h.manifest.History[hi].V1Compatibility), &sizeContainer); err != nil {
sizeContainer.Size = 0
}
}
if err := h.updateLayerMetadata(ctx, layer, &h.manifest.FSLayers[hi], sizeContainer.Size); err != nil {
return nil, err
}
}

h.cachedLayers = layers
}

layers := make([]imageapiv1.ImageLayer, len(h.cachedLayers))
for i, l := range h.cachedLayers {
layers[i] = l
}

return layers, nil
}

func (h *manifestSchema1Handler) Manifest() distribution.Manifest {
return h.manifest
}

func (h *manifestSchema1Handler) Metadata(ctx context.Context) (*imageapi.DockerImage, error) {
if len(h.manifest.History) == 0 {
// should never have an empty history, but just in case...
return nil, ErrNoManifestMetadata
}

v1Metadata := imageapi.DockerV1CompatibilityImage{}
if err := json.Unmarshal([]byte(h.manifest.History[0].V1Compatibility), &v1Metadata); err != nil {
return nil, err
}

var (
dockerImageSize int64
layerSet = sets.NewString()
)

layers, err := h.Layers(ctx)
if err != nil {
return nil, err
}
for _, layer := range layers {
if !layerSet.Has(layer.Name) {
dockerImageSize += layer.LayerSize
layerSet.Insert(layer.Name)
}
}

meta := &imageapi.DockerImage{}
meta.ID = v1Metadata.ID
meta.Parent = v1Metadata.Parent
meta.Comment = v1Metadata.Comment
meta.Created = v1Metadata.Created
meta.Container = v1Metadata.Container
meta.ContainerConfig = v1Metadata.ContainerConfig
meta.DockerVersion = v1Metadata.DockerVersion
meta.Author = v1Metadata.Author
meta.Config = v1Metadata.Config
meta.Architecture = v1Metadata.Architecture
meta.Size = dockerImageSize

return meta, nil
}

func (h *manifestSchema1Handler) Signatures(ctx context.Context) ([][]byte, error) {
return h.manifest.Signatures()
}

func (h *manifestSchema1Handler) Payload() (mediaType string, payload []byte, canonical []byte, err error) {
mt, payload, err := h.manifest.Payload()
return mt, payload, h.manifest.Canonical, err
Expand Down Expand Up @@ -133,3 +219,25 @@ func (h *manifestSchema1Handler) Verify(ctx context.Context, skipDependencyVerif
}
return nil
}

func (h *manifestSchema1Handler) updateLayerMetadata(
ctx context.Context,
layerMetadata *imageapiv1.ImageLayer,
manifestLayer *schema1.FSLayer,
size int64,
) error {
layerMetadata.Name = manifestLayer.BlobSum.String()
layerMetadata.MediaType = schema1.MediaTypeManifestLayer
if size > 0 {
layerMetadata.LayerSize = size
return nil
}

desc, err := h.repo.Blobs(ctx).Stat(ctx, digest.Digest(layerMetadata.Name))
if err != nil {
context.GetLogger(ctx).Errorf("failed to stat blob %s", layerMetadata.Name)
return err
}
layerMetadata.LayerSize = desc.Size
return nil
}
Loading