imagetools: add platforms filter to imagetools create

Allows specifying platforms that should be included
in the new image, making it possible to reduce platforms
of existing multi-arch image. Previously the individual
image manifests needed to be used as sources, but that
dropped their related attestation manifests.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2025-09-23 08:10:11 -07:00
parent 4ad48f7737
commit f448f3e22b
6 changed files with 174 additions and 22 deletions
+17 -12
View File
@@ -28,7 +28,7 @@ type Source struct {
Ref reference.Named
}
func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes.AnnotationKey]string, preferIndex bool) ([]byte, ocispecs.Descriptor, error) {
func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes.AnnotationKey]string, preferIndex bool) ([]byte, ocispecs.Descriptor, map[digest.Digest]*Source, error) {
eg, ctx := errgroup.WithContext(ctx)
dts := make([][]byte, len(srcs))
@@ -73,7 +73,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
}
if err := eg.Wait(); err != nil {
return nil, ocispecs.Descriptor{}, err
return nil, ocispecs.Descriptor{}, nil, err
}
// on single source, return original bytes
@@ -83,18 +83,22 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
// of preferIndex since if set to true then the source is already in the preferred format, and if false
// it doesn't matter since we're not going to split it into separate manifests
case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
return dts[0], srcs[0].Desc, nil
srcMap := map[digest.Digest]*Source{
srcs[0].Desc.Digest: srcs[0],
}
return dts[0], srcs[0].Desc, srcMap, nil
default:
if !preferIndex {
return dts[0], srcs[0].Desc, nil
return dts[0], srcs[0].Desc, nil, nil
}
}
}
indexes := map[digest.Digest]int{}
sources := map[digest.Digest]*Source{}
descs := make([]ocispecs.Descriptor, 0, len(srcs))
addDesc := func(d ocispecs.Descriptor) {
addDesc := func(d ocispecs.Descriptor, src *Source) {
idx, ok := indexes[d.Digest]
if ok {
old := descs[idx]
@@ -113,6 +117,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
indexes[d.Digest] = len(descs)
descs = append(descs, d)
}
sources[d.Digest] = src
}
for i, src := range srcs {
@@ -120,13 +125,13 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
var mfst ocispecs.Index
if err := json.Unmarshal(dts[i], &mfst); err != nil {
return nil, ocispecs.Descriptor{}, errors.WithStack(err)
return nil, ocispecs.Descriptor{}, nil, errors.WithStack(err)
}
for _, d := range mfst.Manifests {
addDesc(d)
addDesc(d, src)
}
default:
addDesc(src.Desc)
addDesc(src.Desc, src)
}
}
@@ -163,9 +168,9 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
}
}
case exptypes.AnnotationManifest, "":
return nil, ocispecs.Descriptor{}, errors.Errorf("%q annotations are not supported yet", k.Type)
return nil, ocispecs.Descriptor{}, nil, errors.Errorf("%q annotations are not supported yet", k.Type)
case exptypes.AnnotationIndexDescriptor:
return nil, ocispecs.Descriptor{}, errors.Errorf("%q annotations are invalid while creating an image", k.Type)
return nil, ocispecs.Descriptor{}, nil, errors.Errorf("%q annotations are invalid while creating an image", k.Type)
}
}
}
@@ -179,14 +184,14 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
Annotations: indexAnnotation,
}, "", " ")
if err != nil {
return nil, ocispecs.Descriptor{}, errors.Wrap(err, "failed to marshal index")
return nil, ocispecs.Descriptor{}, nil, errors.Wrap(err, "failed to marshal index")
}
return idxBytes, ocispecs.Descriptor{
MediaType: mt,
Size: int64(len(idxBytes)),
Digest: digest.FromBytes(idxBytes),
}, nil
}, sources, nil
}
func (r *Resolver) Push(ctx context.Context, ref reference.Named, desc ocispecs.Descriptor, dt []byte) error {