Browse code

Add dangling image reference on delete when last image has children

Signed-off-by: Derek McGowan <derek@mcg.dev>

Derek McGowan authored on 2023/12/19 05:48:38
Showing 2 changed files
... ...
@@ -5,7 +5,9 @@ import (
5 5
 	"fmt"
6 6
 	"sort"
7 7
 	"strings"
8
+	"time"
8 9
 
10
+	cerrdefs "github.com/containerd/containerd/errdefs"
9 11
 	"github.com/containerd/containerd/images"
10 12
 	"github.com/containerd/log"
11 13
 	"github.com/distribution/reference"
... ...
@@ -135,7 +137,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
135 135
 				}
136 136
 			}
137 137
 			return records, nil
138
-		} else if !force {
138
+		} else if len(all) > 1 && !force {
139 139
 			// Since only a single used reference, remove all active
140 140
 			// TODO: Consider keeping the conflict and changing active
141 141
 			// reference calculation in image checker.
... ...
@@ -145,6 +147,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
145 145
 		using := func(c *container.Container) bool {
146 146
 			return c.ImageID == imgID
147 147
 		}
148
+		// TODO: Should this also check parentage here?
148 149
 		ctr := i.containers.First(using)
149 150
 		if ctr != nil {
150 151
 			familiarRef := reference.FamiliarString(parsedRef)
... ...
@@ -372,6 +375,24 @@ func (i *ImageService) imageDeleteHelper(ctx context.Context, img images.Image,
372 372
 		return err
373 373
 	}
374 374
 
375
+	if !isDanglingImage(img) && len(all) == 1 && extra&conflictActiveReference != 0 {
376
+		children, err := i.Children(ctx, imgID)
377
+		if err != nil {
378
+			return err
379
+		}
380
+		if len(children) > 0 {
381
+			img := images.Image{
382
+				Name:      danglingImageName(img.Target.Digest),
383
+				Target:    img.Target,
384
+				CreatedAt: time.Now(),
385
+				Labels:    img.Labels,
386
+			}
387
+			if _, err = i.client.ImageService().Create(ctx, img); err != nil && !cerrdefs.IsAlreadyExists(err) {
388
+				return fmt.Errorf("failed to create dangling image: %w", err)
389
+			}
390
+		}
391
+	}
392
+
375 393
 	// TODO: Add target option
376 394
 	err = i.images.Delete(ctx, img.Name, images.SynchronousDelete())
377 395
 	if err != nil {
... ...
@@ -295,7 +295,7 @@ RUN echo 2 #layer2
295 295
 	// should not be untagged without the -f flag
296 296
 	assert.ErrorContains(c, err, "")
297 297
 	assert.Assert(c, strings.Contains(out, cID[:12]))
298
-	assert.Assert(c, strings.Contains(out, "(must force)"))
298
+	assert.Assert(c, strings.Contains(out, "(must force)") || strings.Contains(out, "(must be forced)"))
299 299
 	// Add the -f flag and test again.
300 300
 	out = cli.DockerCmd(c, "rmi", "-f", newTag).Combined()
301 301
 	// should be allowed to untag with the -f flag