fix: gc for untagged docker manifests (#3349)

- fixes #3347: removeUntaggedManifests() did not consider compatible manifest types
- add AsDockerImage() to Image and MultiarchImage for testing
- extend TestGarbageCollectAndRetentionMetaDB to test docker image and multiarch image

Signed-off-by: Stephan Merker <stephan.merker@sap.com>
This commit is contained in:
Stephan Merker
2025-09-01 18:20:35 +02:00
committed by GitHub
parent cb520aa9e4
commit f0404e7e72
6 changed files with 182 additions and 9 deletions
+30
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"time"
docker "github.com/distribution/distribution/v3/manifest/schema2"
godigest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -173,6 +174,35 @@ func (img Image) AsImageMeta() mTypes.ImageMeta {
}
}
func (img Image) AsDockerImage() Image {
// Convert manifest media type
img.Manifest.MediaType = docker.MediaTypeManifest
// Convert config media type
img.Manifest.Config.MediaType = docker.MediaTypeImageConfig
img.ConfigDescriptor.MediaType = docker.MediaTypeImageConfig
// Convert layer media types
for i := range img.Manifest.Layers {
img.Manifest.Layers[i].MediaType = docker.MediaTypeLayer
}
manifestBlob, err := json.Marshal(img.Manifest)
if err != nil {
panic("unreachable: ispec.Manifest should always be marshable")
}
img.ManifestDescriptor = ispec.Descriptor{
MediaType: docker.MediaTypeImageConfig,
Digest: img.digestAlgorithm.FromBytes(manifestBlob),
Size: int64(len(manifestBlob)),
Data: manifestBlob,
Platform: img.ConfigDescriptor.Platform,
}
return img
}
type Layer struct {
Blob []byte
MediaType string
+17
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"testing"
docker "github.com/distribution/distribution/v3/manifest/schema2"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
@@ -197,3 +198,19 @@ func TestImageMethods(t *testing.T) {
})
})
}
func TestConvertImageToDocker(t *testing.T) {
Convey("AsDockerImage", t, func() {
Convey("DefaultImage", func() {
img := CreateDefaultImage()
dockerImage := img.AsDockerImage()
So(dockerImage.Manifest.MediaType, ShouldEqual, docker.MediaTypeManifest)
So(dockerImage.ConfigDescriptor.MediaType, ShouldEqual, docker.MediaTypeImageConfig)
for _, layer := range dockerImage.Manifest.Layers {
So(layer.MediaType, ShouldEqual, docker.MediaTypeLayer)
}
})
})
}
+29
View File
@@ -3,6 +3,7 @@ package image
import (
"encoding/json"
dockerList "github.com/distribution/distribution/v3/manifest/manifestlist"
godigest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -75,6 +76,34 @@ func (mi MultiarchImage) AsImageMeta() mTypes.ImageMeta {
}
}
func (mi MultiarchImage) AsDockerImage() MultiarchImage {
for i := range mi.Images {
mi.Images[i] = mi.Images[i].AsDockerImage()
}
mi.Index.MediaType = dockerList.MediaTypeManifestList
for i := range mi.Index.Manifests {
mi.Index.Manifests[i].Digest = mi.Images[i].ManifestDescriptor.Digest
mi.Index.Manifests[i].Size = mi.Images[i].ManifestDescriptor.Size
mi.Index.Manifests[i].MediaType = mi.Images[i].ManifestDescriptor.MediaType
}
indexBlob, err := json.Marshal(mi.Index)
if err != nil {
panic("unreachable: ispec.Index should always be marshable")
}
mi.IndexDescriptor.MediaType = dockerList.MediaTypeManifestList
mi.IndexDescriptor = ispec.Descriptor{
MediaType: dockerList.MediaTypeManifestList,
Digest: mi.digestAlgorithm.FromBytes(indexBlob),
Size: int64(len(indexBlob)),
Data: indexBlob,
}
return mi
}
type ImagesBuilder interface {
Images(images []Image) MultiarchBuilder
RandomImages(count int) MultiarchBuilder
+2 -3
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
stypes "zotregistry.dev/zot/pkg/storage/types"
)
@@ -52,7 +51,7 @@ func WriteImageToFileSystem(image Image, repoName, ref string, storeController s
return err
}
_, _, err = store.PutImageManifest(repoName, ref, ispec.MediaTypeImageManifest, manifestBlob)
_, _, err = store.PutImageManifest(repoName, ref, image.Manifest.MediaType, manifestBlob)
if err != nil {
return err
}
@@ -82,7 +81,7 @@ func WriteMultiArchImageToFileSystem(multiarchImage MultiarchImage, repoName, re
return err
}
_, _, err = store.PutImageManifest(repoName, ref, ispec.MediaTypeImageIndex,
_, _, err = store.PutImageManifest(repoName, ref, multiarchImage.Index.MediaType,
indexBlob)
return err