'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>
This commit is contained in:
parent
418195a4fb
commit
795a58fb44
3 changed files with 74 additions and 13 deletions
|
@ -30,6 +30,7 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
|
|||
repoName, tag string
|
||||
tags = []string{}
|
||||
)
|
||||
repoAndTags := make(map[string][]string)
|
||||
|
||||
// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
|
||||
repoName, tag = parsers.ParseRepositoryTag(name)
|
||||
|
@ -68,19 +69,25 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
|
|||
if repoName == "" || repoName == parsedRepo {
|
||||
repoName = parsedRepo
|
||||
if parsedTag != "" {
|
||||
tags = append(tags, parsedTag)
|
||||
repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag)
|
||||
}
|
||||
} else if repoName != parsedRepo && !force && first {
|
||||
// the id belongs to multiple repos, like base:latest and user:test,
|
||||
// in that case return conflict
|
||||
return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
|
||||
} else {
|
||||
//the id belongs to multiple repos, with -f just delete all
|
||||
repoName = parsedRepo
|
||||
if parsedTag != "" {
|
||||
repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tags = append(tags, tag)
|
||||
repoAndTags[repoName] = append(repoAndTags[repoName], tag)
|
||||
}
|
||||
|
||||
if !first && len(tags) > 0 {
|
||||
if !first && len(repoAndTags) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -91,16 +98,18 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
|
|||
}
|
||||
|
||||
// Untag the current image
|
||||
for _, tag := range tags {
|
||||
tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tagDeleted {
|
||||
*list = append(*list, types.ImageDelete{
|
||||
Untagged: utils.ImageReference(repoName, tag),
|
||||
})
|
||||
daemon.EventsService.Log("untag", img.ID, "")
|
||||
for repoName, tags := range repoAndTags {
|
||||
for _, tag := range tags {
|
||||
tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tagDeleted {
|
||||
*list = append(*list, types.ImageDelete{
|
||||
Untagged: utils.ImageReference(repoName, tag),
|
||||
})
|
||||
daemon.EventsService.Log("untag", img.ID, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
tags = daemon.Repositories().ByID()[img.ID]
|
||||
|
|
|
@ -1794,6 +1794,21 @@ before the image is removed.
|
|||
Untagged: test:latest
|
||||
Deleted: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
|
||||
If you use the `-f` flag and specify the image's short or long ID, then this
|
||||
command untags and removes all images that match the specified ID.
|
||||
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
test1 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
test latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
test2 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
|
||||
$ docker rmi -f fd484f19954f
|
||||
Untagged: test1:latest
|
||||
Untagged: test:latest
|
||||
Untagged: test2:latest
|
||||
Deleted: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
|
||||
An image pulled by digest has no tag associated with it:
|
||||
|
||||
$ docker images --digests
|
||||
|
|
|
@ -77,6 +77,43 @@ func TestRmiTag(t *testing.T) {
|
|||
logDone("rmi - tag,rmi - tagging the same images multiple times then removing tags")
|
||||
}
|
||||
|
||||
func TestRmiImgIDForce(t *testing.T) {
|
||||
runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-test'")
|
||||
out, _, err := runCommandWithOutput(runCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create a container:%s, %v", out, err)
|
||||
}
|
||||
containerID := strings.TrimSpace(out)
|
||||
runCmd = exec.Command(dockerBinary, "commit", containerID, "busybox-test")
|
||||
out, _, err = runCommandWithOutput(runCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit a new busybox-test:%s, %v", out, err)
|
||||
}
|
||||
|
||||
imagesBefore, _, _ := dockerCmd(t, "images", "-a")
|
||||
dockerCmd(t, "tag", "busybox-test", "utest:tag1")
|
||||
dockerCmd(t, "tag", "busybox-test", "utest:tag2")
|
||||
dockerCmd(t, "tag", "busybox-test", "utest/docker:tag3")
|
||||
dockerCmd(t, "tag", "busybox-test", "utest:5000/docker:tag4")
|
||||
{
|
||||
imagesAfter, _, _ := dockerCmd(t, "images", "-a")
|
||||
if strings.Count(imagesAfter, "\n") != strings.Count(imagesBefore, "\n")+4 {
|
||||
t.Fatalf("tag busybox to create 4 more images with same imageID; docker images shows: %q\n", imagesAfter)
|
||||
}
|
||||
}
|
||||
out, _, _ = dockerCmd(t, "inspect", "-f", "{{.Id}}", "busybox-test")
|
||||
imgID := strings.TrimSpace(out)
|
||||
dockerCmd(t, "rmi", "-f", imgID)
|
||||
{
|
||||
imagesAfter, _, _ := dockerCmd(t, "images", "-a")
|
||||
if strings.Contains(imagesAfter, imgID[:12]) {
|
||||
t.Fatalf("rmi -f %s failed, image still exists: %q\n\n", imgID, imagesAfter)
|
||||
}
|
||||
|
||||
}
|
||||
logDone("rmi - imgID,rmi -f imgID delete all tagged repos of specific imgID")
|
||||
}
|
||||
|
||||
func TestRmiTagWithExistingContainers(t *testing.T) {
|
||||
defer deleteAllContainers()
|
||||
|
||||
|
|
Loading…
Reference in a new issue