Browse code

'docker rmi -f IMAGE_ID' untag all names and delete the image

If an image has been tagged to multiple repos and tags, 'docker
rmi -f IMAGE_ID' will just untag one random repo instead of
untagging all and deleting the image. This patch implement
this. This commit is composed of:

*untag all names and delete the image

*add test to this feature

*modify commandline/cli.md to explain this

Signed-off-by: Deng Guangxing <dengguangxing@huawei.com>

Deng Guangxing authored on 2015/04/11 10:24:21
Showing 3 changed files
... ...
@@ -30,6 +30,7 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
30 30
 		repoName, tag string
31 31
 		tags          = []string{}
32 32
 	)
33
+	repoAndTags := make(map[string][]string)
33 34
 
34 35
 	// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
35 36
 	repoName, tag = parsers.ParseRepositoryTag(name)
... ...
@@ -68,19 +69,25 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
68 68
 			if repoName == "" || repoName == parsedRepo {
69 69
 				repoName = parsedRepo
70 70
 				if parsedTag != "" {
71
-					tags = append(tags, parsedTag)
71
+					repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag)
72 72
 				}
73 73
 			} else if repoName != parsedRepo && !force && first {
74 74
 				// the id belongs to multiple repos, like base:latest and user:test,
75 75
 				// in that case return conflict
76 76
 				return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
77
+			} else {
78
+				//the id belongs to multiple repos, with -f just delete all
79
+				repoName = parsedRepo
80
+				if parsedTag != "" {
81
+					repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag)
82
+				}
77 83
 			}
78 84
 		}
79 85
 	} else {
80
-		tags = append(tags, tag)
86
+		repoAndTags[repoName] = append(repoAndTags[repoName], tag)
81 87
 	}
82 88
 
83
-	if !first && len(tags) > 0 {
89
+	if !first && len(repoAndTags) > 0 {
84 90
 		return nil
85 91
 	}
86 92
 
... ...
@@ -91,16 +98,18 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
91 91
 	}
92 92
 
93 93
 	// Untag the current image
94
-	for _, tag := range tags {
95
-		tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
96
-		if err != nil {
97
-			return err
98
-		}
99
-		if tagDeleted {
100
-			*list = append(*list, types.ImageDelete{
101
-				Untagged: utils.ImageReference(repoName, tag),
102
-			})
103
-			daemon.EventsService.Log("untag", img.ID, "")
94
+	for repoName, tags := range repoAndTags {
95
+		for _, tag := range tags {
96
+			tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
97
+			if err != nil {
98
+				return err
99
+			}
100
+			if tagDeleted {
101
+				*list = append(*list, types.ImageDelete{
102
+					Untagged: utils.ImageReference(repoName, tag),
103
+				})
104
+				daemon.EventsService.Log("untag", img.ID, "")
105
+			}
104 106
 		}
105 107
 	}
106 108
 	tags = daemon.Repositories().ByID()[img.ID]
... ...
@@ -1794,6 +1794,21 @@ before the image is removed.
1794 1794
     Untagged: test:latest
1795 1795
     Deleted: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
1796 1796
 
1797
+If you use the `-f` flag and specify the image's short or long ID, then this
1798
+command untags and removes all images that match the specified ID.
1799
+
1800
+    $ docker images
1801
+    REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
1802
+    test1                     latest              fd484f19954f        23 seconds ago      7 B (virtual 4.964 MB)
1803
+    test                      latest              fd484f19954f        23 seconds ago      7 B (virtual 4.964 MB)
1804
+    test2                     latest              fd484f19954f        23 seconds ago      7 B (virtual 4.964 MB)
1805
+
1806
+    $ docker rmi -f fd484f19954f
1807
+    Untagged: test1:latest
1808
+    Untagged: test:latest
1809
+    Untagged: test2:latest
1810
+    Deleted: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
1811
+
1797 1812
 An image pulled by digest has no tag associated with it:
1798 1813
 
1799 1814
     $ docker images --digests
... ...
@@ -77,6 +77,43 @@ func TestRmiTag(t *testing.T) {
77 77
 	logDone("rmi - tag,rmi - tagging the same images multiple times then removing tags")
78 78
 }
79 79
 
80
+func TestRmiImgIDForce(t *testing.T) {
81
+	runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-test'")
82
+	out, _, err := runCommandWithOutput(runCmd)
83
+	if err != nil {
84
+		t.Fatalf("failed to create a container:%s, %v", out, err)
85
+	}
86
+	containerID := strings.TrimSpace(out)
87
+	runCmd = exec.Command(dockerBinary, "commit", containerID, "busybox-test")
88
+	out, _, err = runCommandWithOutput(runCmd)
89
+	if err != nil {
90
+		t.Fatalf("failed to commit a new busybox-test:%s, %v", out, err)
91
+	}
92
+
93
+	imagesBefore, _, _ := dockerCmd(t, "images", "-a")
94
+	dockerCmd(t, "tag", "busybox-test", "utest:tag1")
95
+	dockerCmd(t, "tag", "busybox-test", "utest:tag2")
96
+	dockerCmd(t, "tag", "busybox-test", "utest/docker:tag3")
97
+	dockerCmd(t, "tag", "busybox-test", "utest:5000/docker:tag4")
98
+	{
99
+		imagesAfter, _, _ := dockerCmd(t, "images", "-a")
100
+		if strings.Count(imagesAfter, "\n") != strings.Count(imagesBefore, "\n")+4 {
101
+			t.Fatalf("tag busybox to create 4 more images with same imageID; docker images shows: %q\n", imagesAfter)
102
+		}
103
+	}
104
+	out, _, _ = dockerCmd(t, "inspect", "-f", "{{.Id}}", "busybox-test")
105
+	imgID := strings.TrimSpace(out)
106
+	dockerCmd(t, "rmi", "-f", imgID)
107
+	{
108
+		imagesAfter, _, _ := dockerCmd(t, "images", "-a")
109
+		if strings.Contains(imagesAfter, imgID[:12]) {
110
+			t.Fatalf("rmi -f %s failed, image still exists: %q\n\n", imgID, imagesAfter)
111
+		}
112
+
113
+	}
114
+	logDone("rmi - imgID,rmi -f imgID  delete all tagged repos of specific imgID")
115
+}
116
+
80 117
 func TestRmiTagWithExistingContainers(t *testing.T) {
81 118
 	defer deleteAllContainers()
82 119