Skip to content

Commit 063f110

Browse files
author
Michal Minář
committed
Correct missing sizes for manifest schema 1 images
Make sure to encode the updated sizes into image.DockerImageMetadata.Raw otherwise the changes will be lost. Signed-off-by: Michal Minář <[email protected]>
1 parent 233358f commit 063f110

6 files changed

+648
-189
lines changed

pkg/dockerregistry/server/manifestschema1handler.go

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,56 +53,75 @@ type manifestSchema1Handler struct {
5353
var _ ManifestHandler = &manifestSchema1Handler{}
5454

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

61-
for _, signDigest := range signatures {
62-
image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest)
63+
manifestData := image.DockerImageManifest
64+
manifest := imageapi.DockerImageManifest{}
65+
if err := json.Unmarshal([]byte(manifestData), &manifest); err != nil {
66+
return err
6367
}
6468

65-
refs := h.manifest.References()
69+
image.DockerImageManifestMediaType = schema1.MediaTypeManifest
6670

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

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

78-
blobs := h.repo.Blobs(ctx)
79-
for i := range image.DockerImageLayers {
80-
layer := &image.DockerImageLayers[i]
81-
// DockerImageLayers represents h.manifest.Manifest.FSLayers in reversed order
82-
desc, err := blobs.Stat(ctx, refs[len(image.DockerImageLayers)-i-1].Digest)
83-
if err != nil {
84-
context.GetLogger(ctx).Errorf("failed to stat blob %s of image %s", layer.Name, image.DockerImageReference)
85-
return err
86-
}
87-
// The MediaType appeared in manifest schema v2. We need to fill it
88-
// manually in the old images if it is not already filled.
89-
if len(layer.MediaType) == 0 {
90-
if len(desc.MediaType) > 0 {
91-
layer.MediaType = desc.MediaType
92-
} else {
93-
layer.MediaType = schema1.MediaTypeManifestLayer
81+
var (
82+
dockerImageSize int64
83+
err error
84+
layerSet = sets.NewString()
85+
sizeContainer = imageapi.DockerV1CompatibilityImageSize{}
86+
)
87+
88+
image.DockerImageLayers = make([]imageapiv1.ImageLayer, len(manifest.FSLayers))
89+
for hi, li := 0, len(manifest.FSLayers)-1; hi < len(manifest.FSLayers) && li >= 0; hi, li = hi+1, li-1 {
90+
layer := &image.DockerImageLayers[li]
91+
if hi == 0 { // the first item in history is v1Metadata
92+
err = h.updateLayerMetadata(ctx, layer, &manifest.FSLayers[hi], v1Metadata.Size)
93+
} else {
94+
sizeContainer.Size = 0
95+
if hi < len(manifest.History) {
96+
if err := json.Unmarshal([]byte(manifest.History[hi].DockerV1Compatibility), &sizeContainer); err != nil {
97+
sizeContainer.Size = 0
98+
}
9499
}
100+
err = h.updateLayerMetadata(ctx, layer, &manifest.FSLayers[hi], sizeContainer.Size)
95101
}
96-
layer.LayerSize = desc.Size
102+
if err != nil {
103+
return fmt.Errorf("failed to update layer metadata of image %s: %v", image.DockerImageReference, err)
104+
}
105+
97106
// count empty layer just once (empty layer may actually have non-zero size)
98-
if !blobSet.Has(layer.Name) {
99-
meta.Size += desc.Size
100-
blobSet.Insert(layer.Name)
107+
if !layerSet.Has(layer.Name) {
108+
dockerImageSize += layer.LayerSize
109+
layerSet.Insert(layer.Name)
101110
}
102111
}
103-
image.DockerImageMetadata.Object = meta
104112

105-
return nil
113+
signatures, err := h.manifest.Signatures()
114+
if err != nil {
115+
return err
116+
}
117+
118+
for _, signDigest := range signatures {
119+
image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest)
120+
}
121+
122+
image.DockerImageMetadata.Object = v1ToDockerImageMetadata(&v1Metadata, "", dockerImageSize)
123+
124+
return encodeRawDockerImageMetadata(image, true)
106125
}
107126

108127
func (h *manifestSchema1Handler) Manifest() distribution.Manifest {
@@ -187,3 +206,20 @@ func (h *manifestSchema1Handler) Verify(ctx context.Context, skipDependencyVerif
187206
func (h *manifestSchema1Handler) Digest() (digest.Digest, error) {
188207
return digest.FromBytes(h.manifest.Canonical), nil
189208
}
209+
210+
func (h *manifestSchema1Handler) updateLayerMetadata(ctx context.Context, layer *imageapiv1.ImageLayer, manifestLayer *imageapi.DockerFSLayer, size int64) error {
211+
layer.Name = manifestLayer.DockerBlobSum
212+
layer.MediaType = schema1.MediaTypeManifestLayer
213+
if size > 0 {
214+
layer.LayerSize = size
215+
return nil
216+
}
217+
218+
desc, err := h.repo.Blobs(ctx).Stat(ctx, digest.Digest(layer.Name))
219+
if err != nil {
220+
context.GetLogger(ctx).Errorf("failed to stat blob %s", layer.Name)
221+
return err
222+
}
223+
layer.LayerSize = desc.Size
224+
return nil
225+
}

0 commit comments

Comments
 (0)