Skip to content

Commit

Permalink
Correct missing sizes for manifest schema 1 images
Browse files Browse the repository at this point in the history
Make sure to encode the updated sizes into image.DockerImageMetadata.Raw
otherwise the changes will be lost.

Signed-off-by: Michal Minář <miminar@redhat.com>
  • Loading branch information
Michal Minář committed Sep 18, 2017
1 parent 233358f commit 063f110
Show file tree
Hide file tree
Showing 6 changed files with 648 additions and 189 deletions.
106 changes: 71 additions & 35 deletions pkg/dockerregistry/server/manifestschema1handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,56 +53,75 @@ type manifestSchema1Handler struct {
var _ ManifestHandler = &manifestSchema1Handler{}

func (h *manifestSchema1Handler) FillImageMetadata(ctx context.Context, image *imageapiv1.Image) error {
signatures, err := h.manifest.Signatures()
if err != nil {
return err
// If we already have metadata don't mutate existing metadata.
meta, ok := image.DockerImageMetadata.Object.(*imageapi.DockerImage)
hasMetadata := ok && meta.Size > 0
if len(image.DockerImageLayers) > 0 && hasMetadata && len(image.DockerImageManifestMediaType) > 0 {
return nil
}

for _, signDigest := range signatures {
image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest)
manifestData := image.DockerImageManifest
manifest := imageapi.DockerImageManifest{}
if err := json.Unmarshal([]byte(manifestData), &manifest); err != nil {
return err
}

refs := h.manifest.References()
image.DockerImageManifestMediaType = schema1.MediaTypeManifest

if err := imageMetadataFromManifest(image); err != nil {
return fmt.Errorf("unable to fill image %s metadata: %v", image.Name, err)
if len(manifest.History) == 0 {
// should never have an empty history, but just in case...
return nil
}

blobSet := sets.NewString()
meta, ok := image.DockerImageMetadata.Object.(*imageapi.DockerImage)
if !ok {
return fmt.Errorf("image %q does not have metadata", image.Name)
v1Metadata := imageapi.DockerV1CompatibilityImage{}
if err := json.Unmarshal([]byte(manifest.History[0].DockerV1Compatibility), &v1Metadata); err != nil {
return err
}
meta.Size = int64(0)

blobs := h.repo.Blobs(ctx)
for i := range image.DockerImageLayers {
layer := &image.DockerImageLayers[i]
// DockerImageLayers represents h.manifest.Manifest.FSLayers in reversed order
desc, err := blobs.Stat(ctx, refs[len(image.DockerImageLayers)-i-1].Digest)
if err != nil {
context.GetLogger(ctx).Errorf("failed to stat blob %s of image %s", layer.Name, image.DockerImageReference)
return err
}
// The MediaType appeared in manifest schema v2. We need to fill it
// manually in the old images if it is not already filled.
if len(layer.MediaType) == 0 {
if len(desc.MediaType) > 0 {
layer.MediaType = desc.MediaType
} else {
layer.MediaType = schema1.MediaTypeManifestLayer
var (
dockerImageSize int64
err error
layerSet = sets.NewString()
sizeContainer = imageapi.DockerV1CompatibilityImageSize{}
)

image.DockerImageLayers = make([]imageapiv1.ImageLayer, len(manifest.FSLayers))
for hi, li := 0, len(manifest.FSLayers)-1; hi < len(manifest.FSLayers) && li >= 0; hi, li = hi+1, li-1 {
layer := &image.DockerImageLayers[li]
if hi == 0 { // the first item in history is v1Metadata
err = h.updateLayerMetadata(ctx, layer, &manifest.FSLayers[hi], v1Metadata.Size)
} else {
sizeContainer.Size = 0
if hi < len(manifest.History) {
if err := json.Unmarshal([]byte(manifest.History[hi].DockerV1Compatibility), &sizeContainer); err != nil {
sizeContainer.Size = 0
}
}
err = h.updateLayerMetadata(ctx, layer, &manifest.FSLayers[hi], sizeContainer.Size)
}
layer.LayerSize = desc.Size
if err != nil {
return fmt.Errorf("failed to update layer metadata of image %s: %v", image.DockerImageReference, err)
}

// count empty layer just once (empty layer may actually have non-zero size)
if !blobSet.Has(layer.Name) {
meta.Size += desc.Size
blobSet.Insert(layer.Name)
if !layerSet.Has(layer.Name) {
dockerImageSize += layer.LayerSize
layerSet.Insert(layer.Name)
}
}
image.DockerImageMetadata.Object = meta

return nil
signatures, err := h.manifest.Signatures()
if err != nil {
return err
}

for _, signDigest := range signatures {
image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest)
}

image.DockerImageMetadata.Object = v1ToDockerImageMetadata(&v1Metadata, "", dockerImageSize)

return encodeRawDockerImageMetadata(image, true)
}

func (h *manifestSchema1Handler) Manifest() distribution.Manifest {
Expand Down Expand Up @@ -187,3 +206,20 @@ func (h *manifestSchema1Handler) Verify(ctx context.Context, skipDependencyVerif
func (h *manifestSchema1Handler) Digest() (digest.Digest, error) {
return digest.FromBytes(h.manifest.Canonical), nil
}

func (h *manifestSchema1Handler) updateLayerMetadata(ctx context.Context, layer *imageapiv1.ImageLayer, manifestLayer *imageapi.DockerFSLayer, size int64) error {
layer.Name = manifestLayer.DockerBlobSum
layer.MediaType = schema1.MediaTypeManifestLayer
if size > 0 {
layer.LayerSize = size
return nil
}

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

0 comments on commit 063f110

Please sign in to comment.