diff --git a/libimage/.manifest_list.go.swp b/libimage/.manifest_list.go.swp new file mode 100644 index 000000000..6adfad071 Binary files /dev/null and b/libimage/.manifest_list.go.swp differ diff --git a/libimage/manifest_list_test.go b/libimage/manifest_list_test.go index 68779bf6a..d839237db 100644 --- a/libimage/manifest_list_test.go +++ b/libimage/manifest_list_test.go @@ -75,3 +75,52 @@ func TestCreateAndTagManifestList(t *testing.T) { // Both origin list and newly tagged list should point to same image id require.Equal(t, image.ID(), taggedImage.ID()) } + +// Following test ensure that we test Removing a manifestList +// Test tags two manifestlist and deletes one of them and +// confirms if other one is not deleted. +func TestCreateAndRemoveManifestList(t *testing.T) { + + tagName := "manifestlisttagged" + listName := "manifestlist" + runtime, cleanup := testNewRuntime(t) + defer cleanup() + ctx := context.Background() + + list, err := runtime.CreateManifestList(listName) + require.NoError(t, err) + require.NotNil(t, list) + + manifestListOpts := &ManifestListAddOptions{All: true} + _, err = list.Add(ctx, "docker://busybox", manifestListOpts) + require.NoError(t, err) + + list, err = runtime.LookupManifestList(listName) + require.NoError(t, err) + require.NotNil(t, list) + + lookupOptions := &LookupImageOptions{ManifestList: true} + image, _, err := runtime.LookupImage(listName, lookupOptions) + require.NoError(t, err) + require.NotNil(t, image) + err = image.Tag(tagName) + require.NoError(t, err, "tag should have succeeded: %s", tagName) + + taggedImage, _, err := runtime.LookupImage(tagName, lookupOptions) + require.NoError(t, err) + require.NotNil(t, taggedImage) + // Both origin list and newly tagged list should point to same image id + require.Equal(t, image.ID(), taggedImage.ID()) + + // Try deleting the manifestList with tag + rmReports, rmErrors := runtime.RemoveImages(ctx, []string{"manifestlisttagged"}, &RemoveImagesOptions{Force: true, LookupManifest: true}) + require.Nil(t, rmErrors) + require.Equal(t, []string{"localhost/manifestlisttagged:latest"}, rmReports[0].Untagged) + + // Original tag should still exists + imageRe, _, err := runtime.LookupImage(listName, lookupOptions) + require.NoError(t, err) + require.NotNil(t, imageRe) + require.Equal(t, image.ID(), imageRe.ID()) + +} diff --git a/libimage/runtime.go b/libimage/runtime.go index 7d3fbf3f2..42461014d 100644 --- a/libimage/runtime.go +++ b/libimage/runtime.go @@ -541,6 +541,11 @@ type RemoveImagesOptions struct { // using a removed image. Use RemoveContainerFunc for a custom logic. // If set, all child images will be removed as well. Force bool + // LookupManifest will expect all specified names to be manifest lists (no instance look up). + // This allows for removing manifest lists. + // By default, RemoveImages will attempt to resolve to a manifest instance matching + // the local platform (i.e., os, architecture, variant). + LookupManifest bool // RemoveContainerFunc allows for a custom logic for removing // containers using a specific image. By default, all containers in // the local containers storage will be removed (if Force is set). @@ -600,13 +605,22 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem toDelete := []string{} // Look up images in the local containers storage and fill out // toDelete and the deleteMap. + switch { case len(names) > 0: + // prepare lookupOptions + var lookupOptions *LookupImageOptions + if options.LookupManifest { + // LookupManifest configured as true make sure we only remove manifests and no referenced images. + lookupOptions = &LookupImageOptions{lookupManifest: true} + } else { + lookupOptions = &LookupImageOptions{returnManifestIfNoInstance: true} + } // Look up the images one-by-one. That allows for removing // images that have been looked up successfully while reporting // lookup errors at the end. for _, name := range names { - img, resolvedName, err := r.LookupImage(name, &LookupImageOptions{returnManifestIfNoInstance: true}) + img, resolvedName, err := r.LookupImage(name, lookupOptions) if err != nil { appendError(err) continue