From d8908527bd703b55e2fe2dbd17f38ae85836b565 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Fri, 26 Aug 2022 10:05:04 +0100 Subject: [PATCH 1/3] testutil: refactor image info to capture manifests and indexes Signed-off-by: Justin Chadwell --- client/client_test.go | 20 +++++------ frontend/dockerfile/dockerfile_test.go | 20 +++++------ util/testutil/imageinfo.go | 47 ++++++++++++++------------ 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 1ccb1c88fe66..8cf0a5275bf0 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -6425,28 +6425,28 @@ func testExportAttestations(t *testing.T, sb integration.Sandbox) { desc, provider, err := contentutil.ProviderFromRef(target) require.NoError(t, err) - index, err := testutil.ReadIndex(sb.Context(), provider, desc) + imgs, err := testutil.ReadImages(sb.Context(), provider, desc) require.NoError(t, err) - require.Equal(t, len(ps)*2, len(index)) + require.Equal(t, len(ps)*2, len(imgs.Images)) - var imgs []*testutil.ImageInfo + var bases []*testutil.ImageInfo for _, p := range ps { pk := platforms.Format(p) - img := index.Find(pk) + img := imgs.Find(pk) require.NotNil(t, img) require.Equal(t, pk, platforms.Format(*img.Desc.Platform)) require.Equal(t, 1, len(img.Layers)) require.Equal(t, []byte(fmt.Sprintf("hello %s!", pk)), img.Layers[0]["greeting"].Data) - imgs = append(imgs, img) + bases = append(bases, img) } - atts := index.Filter("unknown/unknown") - require.Equal(t, len(ps), len(atts)) - for i, att := range atts { + atts := imgs.Filter("unknown/unknown") + require.Equal(t, len(ps), len(atts.Images)) + for i, att := range atts.Images { require.Equal(t, "unknown/unknown", platforms.Format(*att.Desc.Platform)) require.Equal(t, "unknown/unknown", att.Img.OS+"/"+att.Img.Architecture) require.Equal(t, attestation.DockerAnnotationReferenceTypeDefault, att.Desc.Annotations[attestation.DockerAnnotationReferenceType]) - require.Equal(t, imgs[i].Desc.Digest.String(), att.Desc.Annotations[attestation.DockerAnnotationReferenceDigest]) + require.Equal(t, bases[i].Desc.Digest.String(), att.Desc.Annotations[attestation.DockerAnnotationReferenceDigest]) require.Equal(t, 2, len(att.Layers)) require.Equal(t, len(att.Layers), len(att.Img.RootFS.DiffIDs)) require.Equal(t, len(att.Img.History), 0) @@ -6460,7 +6460,7 @@ func testExportAttestations(t *testing.T, sb integration.Sandbox) { subjects := []intoto.Subject{{ Name: "_", Digest: map[string]string{ - "sha256": imgs[i].Desc.Digest.Encoded(), + "sha256": bases[i].Desc.Digest.Encoded(), }, }} require.Equal(t, subjects, attest.Subject) diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 5ea27f8a012b..0d8cbfa23939 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -3954,14 +3954,14 @@ COPY --from=base arch / desc, provider, err := contentutil.ProviderFromRef(target + "-img") require.NoError(t, err) - imgMap, err := testutil.ReadIndex(sb.Context(), provider, desc) + imgs, err := testutil.ReadImages(sb.Context(), provider, desc) require.NoError(t, err) - require.Equal(t, 2, len(imgMap)) + require.Equal(t, 2, len(imgs.Images)) - require.Equal(t, "amd64", string(imgMap.Find("linux/amd64").Layers[1]["arch"].Data)) - dtamd := imgMap.Find("linux/amd64").Layers[0]["unique"].Data - dtarm := imgMap.Find("linux/arm/v7").Layers[0]["unique"].Data + require.Equal(t, "amd64", string(imgs.Find("linux/amd64").Layers[1]["arch"].Data)) + dtamd := imgs.Find("linux/amd64").Layers[0]["unique"].Data + dtarm := imgs.Find("linux/arm/v7").Layers[0]["unique"].Data require.NotEqual(t, dtamd, dtarm) for i := 0; i < 2; i++ { @@ -3994,14 +3994,14 @@ COPY --from=base arch / require.Equal(t, desc.Digest, desc2.Digest) - imgMap, err = testutil.ReadIndex(sb.Context(), provider, desc2) + imgs, err = testutil.ReadImages(sb.Context(), provider, desc2) require.NoError(t, err) - require.Equal(t, 2, len(imgMap)) + require.Equal(t, 2, len(imgs.Images)) - require.Equal(t, "arm", string(imgMap.Find("linux/arm/v7").Layers[1]["arch"].Data)) - dtamd2 := imgMap.Find("linux/amd64").Layers[0]["unique"].Data - dtarm2 := imgMap.Find("linux/arm/v7").Layers[0]["unique"].Data + require.Equal(t, "arm", string(imgs.Find("linux/arm/v7").Layers[1]["arch"].Data)) + dtamd2 := imgs.Find("linux/amd64").Layers[0]["unique"].Data + dtarm2 := imgs.Find("linux/arm/v7").Layers[0]["unique"].Data require.Equal(t, string(dtamd), string(dtamd2)) require.Equal(t, string(dtarm), string(dtarm2)) } diff --git a/util/testutil/imageinfo.go b/util/testutil/imageinfo.go index 52e2c2501325..64e01dafa5b1 100644 --- a/util/testutil/imageinfo.go +++ b/util/testutil/imageinfo.go @@ -12,53 +12,57 @@ import ( type ImageInfo struct { Desc ocispecs.Descriptor + Manifest ocispecs.Manifest Img ocispecs.Image Layers []map[string]*TarItem LayersRaw [][]byte descPlatform string } -type ImageInfos []*ImageInfo +type ImagesInfo struct { + Desc ocispecs.Descriptor + Index ocispecs.Index + Images []*ImageInfo +} -func (infos ImageInfos) Find(platform string) *ImageInfo { - result := infos.Filter(platform) - if len(result) == 0 { +func (idx ImagesInfo) Find(platform string) *ImageInfo { + result := idx.Filter(platform) + if len(result.Images) == 0 { return nil } - return result[0] + return result.Images[0] } -func (infos ImageInfos) Filter(platform string) ImageInfos { - result := ImageInfos{} - for _, info := range infos { +func (idx ImagesInfo) Filter(platform string) *ImagesInfo { + result := &ImagesInfo{Desc: idx.Desc} + for _, info := range idx.Images { if info.descPlatform == platform { - result = append(result, info) + result.Images = append(result.Images, info) } } return result } -func ReadIndex(ctx context.Context, p content.Provider, desc ocispecs.Descriptor) (ImageInfos, error) { - infos := ImageInfos{} +func ReadImages(ctx context.Context, p content.Provider, desc ocispecs.Descriptor) (*ImagesInfo, error) { + idx := &ImagesInfo{Desc: desc} dt, err := content.ReadBlob(ctx, p, desc) if err != nil { return nil, err } - var idx ocispecs.Index - if err := json.Unmarshal(dt, &idx); err != nil { + if err := json.Unmarshal(dt, &idx.Index); err != nil { return nil, err } - for _, m := range idx.Manifests { + for _, m := range idx.Index.Manifests { img, err := ReadImage(ctx, p, m) if err != nil { return nil, err } img.descPlatform = platforms.Format(*m.Platform) - infos = append(infos, img) + idx.Images = append(idx.Images, img) } - return infos, nil + return idx, nil } func ReadImage(ctx context.Context, p content.Provider, desc ocispecs.Descriptor) (*ImageInfo, error) { @@ -68,12 +72,11 @@ func ReadImage(ctx context.Context, p content.Provider, desc ocispecs.Descriptor if err != nil { return nil, err } - var mfst ocispecs.Manifest - if err := json.Unmarshal(dt, &mfst); err != nil { + if err := json.Unmarshal(dt, &ii.Manifest); err != nil { return nil, err } - dt, err = content.ReadBlob(ctx, p, mfst.Config) + dt, err = content.ReadBlob(ctx, p, ii.Manifest.Config) if err != nil { return nil, err } @@ -81,9 +84,9 @@ func ReadImage(ctx context.Context, p content.Provider, desc ocispecs.Descriptor return nil, err } - ii.Layers = make([]map[string]*TarItem, len(mfst.Layers)) - ii.LayersRaw = make([][]byte, len(mfst.Layers)) - for i, l := range mfst.Layers { + ii.Layers = make([]map[string]*TarItem, len(ii.Manifest.Layers)) + ii.LayersRaw = make([][]byte, len(ii.Manifest.Layers)) + for i, l := range ii.Manifest.Layers { dt, err := content.ReadBlob(ctx, p, l) if err != nil { return nil, err From ec5084fccaa8facdfe7360cd089125e0dad9e817 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 24 Aug 2022 11:38:58 +0100 Subject: [PATCH 2/3] containerimage: force oci-mediatypes for annotations/attestations The new annotations and attestations features both utilize annotations in the oci image format. Many registries allow setting these annotation fields on docker formats, however, notably, GCR will reject these objects and only allow them for OCI media types. To work around this, we enable oci-mediatypes when annotations that require OCI are enabled by the user, printing a warning to the user, similar to how we already do for stargz compression. Signed-off-by: Justin Chadwell --- client/client_test.go | 121 ++++++++++++++++++++++++++++++ exporter/containerimage/export.go | 5 +- exporter/containerimage/opts.go | 35 ++++++++- exporter/containerimage/writer.go | 12 ++- exporter/oci/export.go | 7 +- 5 files changed, 165 insertions(+), 15 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 8cf0a5275bf0..03cf32f9ee4a 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -171,6 +171,7 @@ func TestIntegration(t *testing.T) { testCallInfo, testPullWithLayerLimit, testExportAnnotations, + testExportAnnotationsMediaTypes, testExportAttestations, ) tests = append(tests, diffOpTestCases()...) @@ -6306,6 +6307,125 @@ func testExportAnnotations(t *testing.T, sb integration.Sandbox) { } } +func testExportAnnotationsMediaTypes(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + p := platforms.DefaultSpec() + ps := []ocispecs.Platform{p} + + frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { + res := gateway.NewResult() + expPlatforms := &exptypes.Platforms{ + Platforms: make([]exptypes.Platform, len(ps)), + } + for i, p := range ps { + st := llb.Scratch().File( + llb.Mkfile("platform", 0600, []byte(platforms.Format(p))), + ) + + def, err := st.Marshal(ctx) + if err != nil { + return nil, err + } + + r, err := c.Solve(ctx, gateway.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, err + } + + ref, err := r.SingleRef() + if err != nil { + return nil, err + } + + _, err = ref.ToState() + if err != nil { + return nil, err + } + + k := platforms.Format(p) + res.AddRef(k, ref) + + expPlatforms.Platforms[i] = exptypes.Platform{ + ID: k, + Platform: p, + } + } + dt, err := json.Marshal(expPlatforms) + if err != nil { + return nil, err + } + res.AddMeta(exptypes.ExporterPlatformsKey, dt) + + return res, nil + } + + target := "testannotationsmedia:1" + _, err = c.Build(sb.Context(), SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterImage, + Attrs: map[string]string{ + "name": target, + "annotation-manifest.x": "y", + }, + }, + }, + }, "", frontend, nil) + require.NoError(t, err) + + target2 := "testannotationsmedia:2" + _, err = c.Build(sb.Context(), SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterImage, + Attrs: map[string]string{ + "name": target2, + "annotation-index.x": "y", + }, + }, + }, + }, "", frontend, nil) + require.NoError(t, err) + + ctx := namespaces.WithNamespace(sb.Context(), "buildkit") + cdAddress := sb.ContainerdAddress() + if cdAddress != "" { + client, err := newContainerd(cdAddress) + require.NoError(t, err) + defer client.Close() + + // test annotation in manifest + img, err := client.GetImage(ctx, target) + require.NoError(t, err) + + var index ocispecs.Index + indexBytes, err := content.ReadBlob(ctx, client.ContentStore(), img.Target()) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(indexBytes, &index)) + require.Equal(t, images.MediaTypeDockerSchema2ManifestList, index.MediaType) + + mfst, err := images.Manifest(ctx, client.ContentStore(), img.Target(), platforms.Only(p)) + require.NoError(t, err) + require.Equal(t, "y", mfst.Annotations["x"]) + + // test annotation in index + img, err = client.GetImage(ctx, target2) + require.NoError(t, err) + + indexBytes, err = content.ReadBlob(ctx, client.ContentStore(), img.Target()) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(indexBytes, &index)) + require.Equal(t, ocispecs.MediaTypeImageIndex, index.MediaType) + require.Equal(t, "y", index.Annotations["x"]) + } +} + func testExportAttestations(t *testing.T, sb integration.Sandbox) { requiresLinux(t) c, err := New(sb.Context(), sb.Address()) @@ -6443,6 +6563,7 @@ func testExportAttestations(t *testing.T, sb integration.Sandbox) { atts := imgs.Filter("unknown/unknown") require.Equal(t, len(ps), len(atts.Images)) for i, att := range atts.Images { + require.Equal(t, ocispecs.MediaTypeImageManifest, att.Desc.MediaType) require.Equal(t, "unknown/unknown", platforms.Format(*att.Desc.Platform)) require.Equal(t, "unknown/unknown", att.Img.OS+"/"+att.Img.Architecture) require.Equal(t, attestation.DockerAnnotationReferenceTypeDefault, att.Desc.Annotations[attestation.DockerAnnotationReferenceType]) diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index e20eb19d43cf..27e58e7e2cd1 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -78,8 +78,7 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp RefCfg: cacheconfig.RefConfig{ Compression: compression.New(compression.Default), }, - BuildInfo: true, - Annotations: make(AnnotationsGroup), + BuildInfo: true, }, store: true, } @@ -210,7 +209,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source if err != nil { return nil, err } - opts.Annotations = as.Merge(opts.Annotations) + opts.AddAnnotations(as) ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary) if err != nil { diff --git a/exporter/containerimage/opts.go b/exporter/containerimage/opts.go index ddf9cc2a675c..4537043a2a75 100644 --- a/exporter/containerimage/opts.go +++ b/exporter/containerimage/opts.go @@ -42,7 +42,6 @@ func (c *ImageCommitOpts) Load(opt map[string]string) (map[string]string, error) if err != nil { return nil, err } - c.Annotations = as opt = toStringMap(optb) for k, v := range opt { @@ -91,14 +90,42 @@ func (c *ImageCommitOpts) Load(opt map[string]string) (map[string]string, error) } } - if esgz && !c.OCITypes { - logrus.Warn("forcibly turning on oci-mediatype mode for estargz") - c.OCITypes = true + if esgz { + c.EnableOCITypes("estargz") } + c.AddAnnotations(as) + return rest, nil } +func (c *ImageCommitOpts) AddAnnotations(annotations AnnotationsGroup) { + if annotations == nil { + return + } + if c.Annotations == nil { + c.Annotations = AnnotationsGroup{} + } + c.Annotations = c.Annotations.Merge(annotations) + for _, a := range annotations { + if len(a.Index)+len(a.IndexDescriptor)+len(a.ManifestDescriptor) > 0 { + c.EnableOCITypes("annotations") + } + } +} + +func (c *ImageCommitOpts) EnableOCITypes(reason string) { + if !c.OCITypes { + message := "forcibly turning on oci-mediatype mode" + if reason != "" { + message += " for " + reason + } + logrus.Warn(message) + + c.OCITypes = true + } +} + func parseBool(dest *bool, key string, value string) error { b, err := strconv.ParseBool(value) if err != nil { diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index f38752d5f591..bfdb04c0e5a9 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -99,12 +99,16 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session return mfstDesc, nil } - refCount := len(p.Platforms) + attestCount := 0 for _, attests := range inp.Attestations { - refCount += len(attests) + attestCount += len(attests) } - if refCount != len(inp.Refs) { - return nil, errors.Errorf("number of required refs does not match references %d %d", refCount, len(inp.Refs)) + if count := attestCount + len(p.Platforms); count != len(inp.Refs) { + return nil, errors.Errorf("number of required refs does not match references %d %d", count, len(inp.Refs)) + } + + if attestCount > 0 { + opts.EnableOCITypes("attestations") } refs := make([]cache.ImmutableRef, 0, len(inp.Refs)) diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 047d9d5a8e51..1a92cd1a399c 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -57,9 +57,8 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp RefCfg: cacheconfig.RefConfig{ Compression: compression.New(compression.Default), }, - BuildInfo: true, - OCITypes: e.opt.Variant == VariantOCI, - Annotations: make(containerimage.AnnotationsGroup), + BuildInfo: true, + OCITypes: e.opt.Variant == VariantOCI, }, } @@ -110,7 +109,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source if err != nil { return nil, err } - opts.Annotations = as.Merge(opts.Annotations) + opts.AddAnnotations(as) ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary) if err != nil { From 0a5265ab4350dee334df3ac0349837a93a669fef Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Fri, 26 Aug 2022 10:05:19 +0100 Subject: [PATCH 3/3] annotations: refactor tests to remove dependency on containerd Signed-off-by: Justin Chadwell --- client/client_test.go | 154 ++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 82 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 03cf32f9ee4a..c824172a21c5 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -6099,6 +6099,12 @@ func testExportAnnotations(t *testing.T, sb integration.Sandbox) { require.NoError(t, err) defer c.Close() + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + amd64 := platforms.MustParse("linux/amd64") arm64 := platforms.MustParse("linux/arm64") ps := []ocispecs.Platform{amd64, arm64} @@ -6164,18 +6170,19 @@ func testExportAnnotations(t *testing.T, sb integration.Sandbox) { // testing for image exporter - target := "testannotations:latest" + target := registry + "/buildkit/testannotations:latest" _, err = c.Build(sb.Context(), SolveOpt{ Exports: []ExportEntry{ { Type: ExporterImage, Attrs: map[string]string{ - "name": target, - "annotation-index.gio": "generic index opt", - "annotation-manifest.gmo": "generic manifest opt", - "annotation-manifest-descriptor.gmdo": "generic manifest descriptor opt", - "annotation-manifest[linux/amd64].mo": "amd64 manifest opt", + "name": target, + "push": "true", + "annotation-index.gio": "generic index opt", + "annotation-manifest.gmo": "generic manifest opt", + "annotation-manifest-descriptor.gmdo": "generic manifest descriptor opt", + "annotation-manifest[linux/amd64].mo": "amd64 manifest opt", "annotation-manifest-descriptor[linux/amd64].mdo": "amd64 manifest descriptor opt", "annotation-manifest[linux/arm64].mo": "arm64 manifest opt", "annotation-manifest-descriptor[linux/arm64].mdo": "arm64 manifest descriptor opt", @@ -6185,53 +6192,42 @@ func testExportAnnotations(t *testing.T, sb integration.Sandbox) { }, "", frontend, nil) require.NoError(t, err) - ctx := namespaces.WithNamespace(sb.Context(), "buildkit") - cdAddress := sb.ContainerdAddress() - if cdAddress != "" { - client, err := newContainerd(cdAddress) - require.NoError(t, err) - defer client.Close() - - img, err := client.GetImage(ctx, target) - require.NoError(t, err) + desc, provider, err := contentutil.ProviderFromRef(target) + require.NoError(t, err) + imgs, err := testutil.ReadImages(sb.Context(), provider, desc) + require.NoError(t, err) + require.Equal(t, 2, len(imgs.Images)) - var index ocispecs.Index - indexBytes, err := content.ReadBlob(ctx, client.ContentStore(), img.Target()) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(indexBytes, &index)) - - require.Equal(t, "generic index", index.Annotations["gi"]) - require.Equal(t, "generic index opt", index.Annotations["gio"]) - for _, desc := range index.Manifests { - require.Equal(t, "generic manifest descriptor", desc.Annotations["gmd"]) - require.Equal(t, "generic manifest descriptor opt", desc.Annotations["gmdo"]) - switch { - case platforms.Only(amd64).Match(*desc.Platform): - require.Equal(t, "amd64 manifest descriptor", desc.Annotations["md"]) - require.Equal(t, "amd64 manifest descriptor opt", desc.Annotations["mdo"]) - case platforms.Only(arm64).Match(*desc.Platform): - require.Equal(t, "arm64 manifest descriptor", desc.Annotations["md"]) - require.Equal(t, "arm64 manifest descriptor opt", desc.Annotations["mdo"]) - default: - require.Fail(t, "unrecognized platform") - } + require.Equal(t, "generic index", imgs.Index.Annotations["gi"]) + require.Equal(t, "generic index opt", imgs.Index.Annotations["gio"]) + for _, desc := range imgs.Index.Manifests { + require.Equal(t, "generic manifest descriptor", desc.Annotations["gmd"]) + require.Equal(t, "generic manifest descriptor opt", desc.Annotations["gmdo"]) + switch { + case platforms.Only(amd64).Match(*desc.Platform): + require.Equal(t, "amd64 manifest descriptor", desc.Annotations["md"]) + require.Equal(t, "amd64 manifest descriptor opt", desc.Annotations["mdo"]) + case platforms.Only(arm64).Match(*desc.Platform): + require.Equal(t, "arm64 manifest descriptor", desc.Annotations["md"]) + require.Equal(t, "arm64 manifest descriptor opt", desc.Annotations["mdo"]) + default: + require.Fail(t, "unrecognized platform") } + } - mfst, err := images.Manifest(ctx, client.ContentStore(), img.Target(), platforms.Only(amd64)) - require.NoError(t, err) - require.Equal(t, "generic default", mfst.Annotations["gd"]) - require.Equal(t, "generic manifest", mfst.Annotations["gm"]) - require.Equal(t, "generic manifest opt", mfst.Annotations["gmo"]) - require.Equal(t, "amd64 manifest", mfst.Annotations["m"]) - require.Equal(t, "amd64 manifest opt", mfst.Annotations["mo"]) + amdImage := imgs.Find(platforms.Format(amd64)) + require.Equal(t, "generic default", amdImage.Manifest.Annotations["gd"]) + require.Equal(t, "generic manifest", amdImage.Manifest.Annotations["gm"]) + require.Equal(t, "generic manifest opt", amdImage.Manifest.Annotations["gmo"]) + require.Equal(t, "amd64 manifest", amdImage.Manifest.Annotations["m"]) + require.Equal(t, "amd64 manifest opt", amdImage.Manifest.Annotations["mo"]) - mfst, err = images.Manifest(ctx, client.ContentStore(), img.Target(), platforms.Only(arm64)) - require.NoError(t, err) - require.Equal(t, "generic manifest", mfst.Annotations["gm"]) - require.Equal(t, "generic manifest opt", mfst.Annotations["gmo"]) - require.Equal(t, "arm64 manifest", mfst.Annotations["m"]) - require.Equal(t, "arm64 manifest opt", mfst.Annotations["mo"]) - } + armImage := imgs.Find(platforms.Format(arm64)) + require.Equal(t, "generic default", armImage.Manifest.Annotations["gd"]) + require.Equal(t, "generic manifest", armImage.Manifest.Annotations["gm"]) + require.Equal(t, "generic manifest opt", armImage.Manifest.Annotations["gmo"]) + require.Equal(t, "arm64 manifest", armImage.Manifest.Annotations["m"]) + require.Equal(t, "arm64 manifest opt", armImage.Manifest.Annotations["mo"]) // testing for oci exporter @@ -6313,6 +6309,12 @@ func testExportAnnotationsMediaTypes(t *testing.T, sb integration.Sandbox) { require.NoError(t, err) defer c.Close() + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + p := platforms.DefaultSpec() ps := []ocispecs.Platform{p} @@ -6365,65 +6367,53 @@ func testExportAnnotationsMediaTypes(t *testing.T, sb integration.Sandbox) { return res, nil } - target := "testannotationsmedia:1" + target := registry + "/buildkit/testannotationsmedia:1" _, err = c.Build(sb.Context(), SolveOpt{ Exports: []ExportEntry{ { Type: ExporterImage, Attrs: map[string]string{ "name": target, - "annotation-manifest.x": "y", + "push": "true", + "annotation-manifest.a": "b", }, }, }, }, "", frontend, nil) require.NoError(t, err) - target2 := "testannotationsmedia:2" + desc, provider, err := contentutil.ProviderFromRef(target) + require.NoError(t, err) + imgs, err := testutil.ReadImages(sb.Context(), provider, desc) + require.NoError(t, err) + require.Equal(t, 1, len(imgs.Images)) + + target2 := registry + "/buildkit/testannotationsmedia:2" _, err = c.Build(sb.Context(), SolveOpt{ Exports: []ExportEntry{ { Type: ExporterImage, Attrs: map[string]string{ "name": target2, - "annotation-index.x": "y", + "push": "true", + "annotation-index.c": "d", }, }, }, }, "", frontend, nil) require.NoError(t, err) - ctx := namespaces.WithNamespace(sb.Context(), "buildkit") - cdAddress := sb.ContainerdAddress() - if cdAddress != "" { - client, err := newContainerd(cdAddress) - require.NoError(t, err) - defer client.Close() - - // test annotation in manifest - img, err := client.GetImage(ctx, target) - require.NoError(t, err) - - var index ocispecs.Index - indexBytes, err := content.ReadBlob(ctx, client.ContentStore(), img.Target()) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(indexBytes, &index)) - require.Equal(t, images.MediaTypeDockerSchema2ManifestList, index.MediaType) - - mfst, err := images.Manifest(ctx, client.ContentStore(), img.Target(), platforms.Only(p)) - require.NoError(t, err) - require.Equal(t, "y", mfst.Annotations["x"]) + desc, provider, err = contentutil.ProviderFromRef(target2) + require.NoError(t, err) + imgs2, err := testutil.ReadImages(sb.Context(), provider, desc) + require.NoError(t, err) + require.Equal(t, 1, len(imgs2.Images)) - // test annotation in index - img, err = client.GetImage(ctx, target2) - require.NoError(t, err) + require.Equal(t, "b", imgs.Images[0].Manifest.Annotations["a"]) + require.Equal(t, "d", imgs2.Index.Annotations["c"]) - indexBytes, err = content.ReadBlob(ctx, client.ContentStore(), img.Target()) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(indexBytes, &index)) - require.Equal(t, ocispecs.MediaTypeImageIndex, index.MediaType) - require.Equal(t, "y", index.Annotations["x"]) - } + require.Equal(t, images.MediaTypeDockerSchema2ManifestList, imgs.Index.MediaType) + require.Equal(t, ocispecs.MediaTypeImageIndex, imgs2.Index.MediaType) } func testExportAttestations(t *testing.T, sb integration.Sandbox) {