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>
| ... | ... |
@@ -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 |
|