diff --git a/common/libimage/manifest_list.go b/common/libimage/manifest_list.go index f084bbe55f..b8e587468b 100644 --- a/common/libimage/manifest_list.go +++ b/common/libimage/manifest_list.go @@ -508,7 +508,7 @@ func (m *ManifestList) Add(ctx context.Context, name string, options *ManifestLi } } - ref, err := m.parseNameToExtantReference(ctx, systemContext, name, false, "image to add to manifest list") + ref, err := m.parseNameToExtantReference(ctx, systemContext, name, options.All, "image to add to manifest list") if err != nil { return "", err } diff --git a/common/libimage/manifest_list_test.go b/common/libimage/manifest_list_test.go index 6acc7d645e..6037d5a291 100644 --- a/common/libimage/manifest_list_test.go +++ b/common/libimage/manifest_list_test.go @@ -237,6 +237,32 @@ func TestCreateAndRemoveManifestList(t *testing.T) { require.Equal(t, []string{"localhost/manifestlist:latest"}, rmReports[0].Untagged) } +// TestAddListToList ensures that we can add one list to another, adding all of +// the entries in the source list to the destination list. +func TestAddManifestListToManifestList(t *testing.T) { + const imageListAmd64MemberName = "registry.k8s.io/pause@sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610" + const imageListArm64MemberName = "registry.k8s.io/pause@sha256:f365626a556e58189fc21d099fc64603db0f440bff07f77c740989515c544a39" + + runtime := testNewRuntime(t) + ctx := context.Background() + + firstList, err := runtime.CreateManifestList("firstlist") + require.NoError(t, err) + require.NotNil(t, firstList) + _, err = firstList.Add(ctx, imageListAmd64MemberName, nil) + require.NoError(t, err) + _, err = firstList.Add(ctx, imageListArm64MemberName, nil) + require.NoError(t, err) + require.Equalf(t, 2, len(firstList.list.Instances()), "expected to have added two items to list") + + secondList, err := runtime.CreateManifestList("mythirdlist") + require.NoError(t, err) + require.NotNil(t, secondList) + _, err = secondList.Add(ctx, firstList.ID(), &ManifestListAddOptions{All: true}) + require.NoError(t, err) + require.Equalf(t, len(firstList.list.Instances()), len(secondList.list.Instances()), "expected to have all of the items in the list") +} + // TestAddSomeArtifacts ensures that we don't fail to add artifact manifests to // a manifest list, even (or especially) when their config blobs aren't valid // OCI or Docker config blobs. diff --git a/common/libimage/manifests/manifests.go b/common/libimage/manifests/manifests.go index 3caff540dd..40c485e16c 100644 --- a/common/libimage/manifests/manifests.go +++ b/common/libimage/manifests/manifests.go @@ -518,6 +518,7 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag Features, OSFeatures, Annotations []string Size int64 ConfigInfo types.BlobInfo + MediaType string ArtifactType string URLs []string } @@ -550,6 +551,7 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag Features: append([]string{}, lists.Docker().Manifests[i].Platform.Features...), OSFeatures: append([]string{}, platform.OSFeatures...), Size: instance.Size, + MediaType: instance.MediaType, ArtifactType: instance.ArtifactType, Annotations: mapToSlice(instance.Annotations), URLs: instance.URLs, @@ -583,10 +585,12 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag Features: append([]string{}, lists.Docker().Manifests[i].Platform.Features...), OSFeatures: append([]string{}, platform.OSFeatures...), Size: instance.Size, + MediaType: instance.MediaType, ArtifactType: instance.ArtifactType, Annotations: mapToSlice(instance.Annotations), URLs: instance.URLs, } + manifestDigest = instanceDigest instanceInfos = append(instanceInfos, instanceInfo) added = true } @@ -606,22 +610,28 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag instanceInfo.ArtifactType = m.ArtifactType } } + instanceInfo.MediaType = primaryManifestType + instanceInfo.Size = int64(len(primaryManifestBytes)) instanceInfos = append(instanceInfos, instanceInfo) } knownConfigTypes := []string{manifest.DockerV2Schema2ConfigMediaType, v1.MediaTypeImageConfig} for _, instanceInfo := range instanceInfos { + var hasPlatformConfig bool unparsedInstance := image.UnparsedInstance(src, instanceInfo.instanceDigest) - manifestBytes, manifestType, err := unparsedInstance.Manifest(ctx) - if err != nil { - return "", fmt.Errorf("reading manifest from %q, instance %q: %w", transports.ImageName(ref), instanceInfo.instanceDigest, err) - } - instanceManifest, err := manifest.FromBlob(manifestBytes, manifestType) - if err != nil { - return "", fmt.Errorf("parsing manifest from %q, instance %q: %w", transports.ImageName(ref), instanceInfo.instanceDigest, err) + if mb, mt, err := unparsedInstance.Manifest(ctx); err == nil { + if instanceManifest, err := manifest.FromBlob(mb, mt); err == nil { + instanceDigest, err := manifest.Digest(mb) + if err != nil { + return "", fmt.Errorf("computing digest of manifest from %q: %w", transports.ImageName(ref), err) + } + instanceInfo.instanceDigest = &instanceDigest + instanceInfo.MediaType = mt + instanceInfo.Size = int64(len(mb)) + instanceInfo.ConfigInfo = instanceManifest.ConfigInfo() + hasPlatformConfig = instanceInfo.ArtifactType == "" && slices.Contains(knownConfigTypes, instanceInfo.ConfigInfo.MediaType) + } } - instanceInfo.ConfigInfo = instanceManifest.ConfigInfo() - hasPlatformConfig := instanceInfo.ArtifactType == "" && slices.Contains(knownConfigTypes, instanceInfo.ConfigInfo.MediaType) needToParsePlatformConfig := (instanceInfo.OS == "" || instanceInfo.Architecture == "") if hasPlatformConfig && needToParsePlatformConfig { img, err := image.FromUnparsedImage(ctx, sys, unparsedInstance) @@ -643,16 +653,15 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag } } if instanceInfo.instanceDigest == nil { - manifestDigest, err = manifest.Digest(manifestBytes) + manifestDigest, err = manifest.Digest(primaryManifestBytes) if err != nil { return "", fmt.Errorf("computing digest of manifest from %q: %w", transports.ImageName(ref), err) } instanceInfo.instanceDigest = &manifestDigest - instanceInfo.Size = int64(len(manifestBytes)) } else if manifestDigest == "" { manifestDigest = *instanceInfo.instanceDigest } - err = l.List.AddInstance(*instanceInfo.instanceDigest, instanceInfo.Size, manifestType, instanceInfo.OS, instanceInfo.Architecture, instanceInfo.OSVersion, instanceInfo.OSFeatures, instanceInfo.Variant, instanceInfo.Features, instanceInfo.Annotations) + err = l.List.AddInstance(*instanceInfo.instanceDigest, instanceInfo.Size, instanceInfo.MediaType, instanceInfo.OS, instanceInfo.Architecture, instanceInfo.OSVersion, instanceInfo.OSFeatures, instanceInfo.Variant, instanceInfo.Features, instanceInfo.Annotations) if err != nil { return "", fmt.Errorf("adding instance with digest %q: %w", *instanceInfo.instanceDigest, err) }