From cf1ea9237c6cd3be289fafa7f5047ae8154ce247 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Mon, 18 Dec 2023 12:48:38 -0800 Subject: [PATCH] Add dangling image reference on delete when last image has children Signed-off-by: Derek McGowan --- daemon/containerd/image_delete.go | 23 ++++++++++++++++++++++- integration-cli/docker_cli_rmi_test.go | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/daemon/containerd/image_delete.go b/daemon/containerd/image_delete.go index d9843f3e56..728e137482 100644 --- a/daemon/containerd/image_delete.go +++ b/daemon/containerd/image_delete.go @@ -5,7 +5,9 @@ import ( "fmt" "sort" "strings" + "time" + cerrdefs "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/log" "github.com/distribution/reference" @@ -135,7 +137,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force, } } return records, nil - } else if !force { + } else if len(all) > 1 && !force { // Since only a single used reference, remove all active // TODO: Consider keeping the conflict and changing active // reference calculation in image checker. @@ -145,6 +147,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force, using := func(c *container.Container) bool { return c.ImageID == imgID } + // TODO: Should this also check parentage here? ctr := i.containers.First(using) if ctr != nil { familiarRef := reference.FamiliarString(parsedRef) @@ -372,6 +375,24 @@ func (i *ImageService) imageDeleteHelper(ctx context.Context, img images.Image, return err } + if !isDanglingImage(img) && len(all) == 1 && extra&conflictActiveReference != 0 { + children, err := i.Children(ctx, imgID) + if err != nil { + return err + } + if len(children) > 0 { + img := images.Image{ + Name: danglingImageName(img.Target.Digest), + Target: img.Target, + CreatedAt: time.Now(), + Labels: img.Labels, + } + if _, err = i.client.ImageService().Create(ctx, img); err != nil && !cerrdefs.IsAlreadyExists(err) { + return fmt.Errorf("failed to create dangling image: %w", err) + } + } + } + // TODO: Add target option err = i.images.Delete(ctx, img.Name, images.SynchronousDelete()) if err != nil { diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go index 44a4c61c72..584ba29fbd 100644 --- a/integration-cli/docker_cli_rmi_test.go +++ b/integration-cli/docker_cli_rmi_test.go @@ -295,7 +295,7 @@ RUN echo 2 #layer2 // should not be untagged without the -f flag assert.ErrorContains(c, err, "") assert.Assert(c, strings.Contains(out, cID[:12])) - assert.Assert(c, strings.Contains(out, "(must force)")) + assert.Assert(c, strings.Contains(out, "(must force)") || strings.Contains(out, "(must be forced)")) // Add the -f flag and test again. out = cli.DockerCmd(c, "rmi", "-f", newTag).Combined() // should be allowed to untag with the -f flag