Browse Source

Add dangling image reference on delete when last image has children

Signed-off-by: Derek McGowan <derek@mcg.dev>
Derek McGowan 1 year ago
parent
commit
cf1ea9237c
2 changed files with 23 additions and 2 deletions
  1. 22 1
      daemon/containerd/image_delete.go
  2. 1 1
      integration-cli/docker_cli_rmi_test.go

+ 22 - 1
daemon/containerd/image_delete.go

@@ -5,7 +5,9 @@ import (
 	"fmt"
 	"fmt"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
+	"time"
 
 
+	cerrdefs "github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/log"
 	"github.com/containerd/log"
 	"github.com/distribution/reference"
 	"github.com/distribution/reference"
@@ -135,7 +137,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
 				}
 				}
 			}
 			}
 			return records, nil
 			return records, nil
-		} else if !force {
+		} else if len(all) > 1 && !force {
 			// Since only a single used reference, remove all active
 			// Since only a single used reference, remove all active
 			// TODO: Consider keeping the conflict and changing active
 			// TODO: Consider keeping the conflict and changing active
 			// reference calculation in image checker.
 			// 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 {
 		using := func(c *container.Container) bool {
 			return c.ImageID == imgID
 			return c.ImageID == imgID
 		}
 		}
+		// TODO: Should this also check parentage here?
 		ctr := i.containers.First(using)
 		ctr := i.containers.First(using)
 		if ctr != nil {
 		if ctr != nil {
 			familiarRef := reference.FamiliarString(parsedRef)
 			familiarRef := reference.FamiliarString(parsedRef)
@@ -372,6 +375,24 @@ func (i *ImageService) imageDeleteHelper(ctx context.Context, img images.Image,
 		return err
 		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
 	// TODO: Add target option
 	err = i.images.Delete(ctx, img.Name, images.SynchronousDelete())
 	err = i.images.Delete(ctx, img.Name, images.SynchronousDelete())
 	if err != nil {
 	if err != nil {

+ 1 - 1
integration-cli/docker_cli_rmi_test.go

@@ -295,7 +295,7 @@ RUN echo 2 #layer2
 	// should not be untagged without the -f flag
 	// should not be untagged without the -f flag
 	assert.ErrorContains(c, err, "")
 	assert.ErrorContains(c, err, "")
 	assert.Assert(c, strings.Contains(out, cID[:12]))
 	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.
 	// Add the -f flag and test again.
 	out = cli.DockerCmd(c, "rmi", "-f", newTag).Combined()
 	out = cli.DockerCmd(c, "rmi", "-f", newTag).Combined()
 	// should be allowed to untag with the -f flag
 	// should be allowed to untag with the -f flag