mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Rewrite docker rmi
Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
This commit is contained in:
parent
79089720c4
commit
795ed6b1e5
6 changed files with 58 additions and 110 deletions
|
@ -781,6 +781,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
||||||
// 'docker rmi IMAGE' removes all images with the name IMAGE
|
// 'docker rmi IMAGE' removes all images with the name IMAGE
|
||||||
func (cli *DockerCli) CmdRmi(args ...string) error {
|
func (cli *DockerCli) CmdRmi(args ...string) error {
|
||||||
cmd := cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
cmd := cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
||||||
|
force := cmd.Bool([]string{"f", "-force"}, false, "Force")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -789,9 +790,14 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
if *force {
|
||||||
|
v.Set("force", "1")
|
||||||
|
}
|
||||||
|
|
||||||
var encounteredError error
|
var encounteredError error
|
||||||
for _, name := range cmd.Args() {
|
for _, name := range cmd.Args() {
|
||||||
body, _, err := readBody(cli.call("DELETE", "/images/"+name, nil, false))
|
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(cli.err, "%s\n", err)
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
|
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
|
||||||
|
|
|
@ -631,7 +631,7 @@ func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
||||||
}
|
}
|
||||||
var job = eng.Job("image_delete", vars["name"])
|
var job = eng.Job("image_delete", vars["name"])
|
||||||
streamJSON(job, w, false)
|
streamJSON(job, w, false)
|
||||||
job.SetenvBool("autoPrune", version > 1.1)
|
job.Setenv("force", r.Form.Get("force"))
|
||||||
|
|
||||||
return job.Run()
|
return job.Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1175,6 +1175,8 @@ func TestGetEnabledCors(t *testing.T) {
|
||||||
|
|
||||||
func TestDeleteImages(t *testing.T) {
|
func TestDeleteImages(t *testing.T) {
|
||||||
eng := NewTestEngine(t)
|
eng := NewTestEngine(t)
|
||||||
|
//we expect errors, so we disable stderr
|
||||||
|
eng.Stderr = ioutil.Discard
|
||||||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||||
|
|
||||||
initialImages := getImages(eng, t, true, "")
|
initialImages := getImages(eng, t, true, "")
|
||||||
|
|
|
@ -1031,7 +1031,10 @@ func TestContainerOrphaning(t *testing.T) {
|
||||||
buildSomething(template2, imageName)
|
buildSomething(template2, imageName)
|
||||||
|
|
||||||
// remove the second image by name
|
// remove the second image by name
|
||||||
resp, err := srv.DeleteImage(imageName, true)
|
resp := engine.NewTable("", 0)
|
||||||
|
if err := srv.DeleteImage(imageName, resp, true, false); err == nil {
|
||||||
|
t.Fatal("Expected error, got none")
|
||||||
|
}
|
||||||
|
|
||||||
// see if we deleted the first image (and orphaned the container)
|
// see if we deleted the first image (and orphaned the container)
|
||||||
for _, i := range resp.Data {
|
for _, i := range resp.Data {
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
||||||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := srv.DeleteImage("utest/docker:tag2", true); err != nil {
|
if err := srv.DeleteImage("utest/docker:tag2", engine.NewTable("", 0), true, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
||||||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := srv.DeleteImage("utest:5000/docker:tag3", true); err != nil {
|
if err := srv.DeleteImage("utest:5000/docker:tag3", engine.NewTable("", 0), true, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
||||||
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
|
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
|
||||||
nActual = len(images.Data[0].GetList("RepoTags"))
|
nActual = len(images.Data[0].GetList("RepoTags"))
|
||||||
|
|
||||||
if _, err := srv.DeleteImage("utest:tag1", true); err != nil {
|
if err := srv.DeleteImage("utest:tag1", engine.NewTable("", 0), true, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,8 +447,7 @@ func TestRmi(t *testing.T) {
|
||||||
t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
|
t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = srv.DeleteImage(imageID, true)
|
if err = srv.DeleteImage(imageID, engine.NewTable("", 0), true, false); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,8 +682,8 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to remove the tag
|
// Try to remove the tag
|
||||||
imgs, err := srv.DeleteImage("utest:tag1", true)
|
imgs := engine.NewTable("", 0)
|
||||||
if err != nil {
|
if err := srv.DeleteImage("utest:tag1", imgs, true, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
138
server.go
138
server.go
|
@ -2,7 +2,6 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/archive"
|
"github.com/dotcloud/docker/archive"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
|
@ -1810,102 +1809,27 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrImageReferenced = errors.New("Image referenced by a repository")
|
func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force bool) error {
|
||||||
|
|
||||||
func (srv *Server) deleteImageAndChildren(id string, imgs *engine.Table, byParents map[string][]*Image) error {
|
|
||||||
// If the image is referenced by a repo, do not delete
|
|
||||||
if len(srv.runtime.repositories.ByID()[id]) != 0 {
|
|
||||||
return ErrImageReferenced
|
|
||||||
}
|
|
||||||
// If the image is not referenced but has children, go recursive
|
|
||||||
referenced := false
|
|
||||||
for _, img := range byParents[id] {
|
|
||||||
if err := srv.deleteImageAndChildren(img.ID, imgs, byParents); err != nil {
|
|
||||||
if err != ErrImageReferenced {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
referenced = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if referenced {
|
|
||||||
return ErrImageReferenced
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the image is not referenced and has no children, remove it
|
|
||||||
byParents, err := srv.runtime.graph.ByParent()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(byParents[id]) == 0 && srv.canDeleteImage(id) == nil {
|
|
||||||
if err := srv.runtime.repositories.DeleteAll(id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err := srv.runtime.graph.Delete(id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out := &engine.Env{}
|
|
||||||
out.Set("Deleted", id)
|
|
||||||
imgs.Add(out)
|
|
||||||
srv.LogEvent("delete", id, "")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) deleteImageParents(img *Image, imgs *engine.Table) error {
|
|
||||||
if img.Parent != "" {
|
|
||||||
parent, err := srv.runtime.graph.Get(img.Parent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
byParents, err := srv.runtime.graph.ByParent()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Remove all children images
|
|
||||||
if err := srv.deleteImageAndChildren(img.Parent, imgs, byParents); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return srv.deleteImageParents(parent, imgs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, error) {
|
|
||||||
var (
|
var (
|
||||||
repoName, tag string
|
repoName, tag string
|
||||||
img, err = srv.runtime.repositories.LookupImage(name)
|
img, err = srv.runtime.repositories.LookupImage(name)
|
||||||
imgs = engine.NewTable("", 0)
|
|
||||||
tags = []string{}
|
tags = []string{}
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("No such image: %s", name)
|
return fmt.Errorf("No such image: %s", name)
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: What does autoPrune mean ?
|
|
||||||
if !autoPrune {
|
|
||||||
if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
|
||||||
return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(img.ID, name) {
|
if !strings.Contains(img.ID, name) {
|
||||||
repoName, tag = utils.ParseRepositoryTag(name)
|
repoName, tag = utils.ParseRepositoryTag(name)
|
||||||
|
if tag == "" {
|
||||||
|
tag = DEFAULTTAG
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a repo and the image is not referenced anywhere else
|
byParents, err := srv.runtime.graph.ByParent()
|
||||||
// then just perform an untag and do not validate.
|
if err != nil {
|
||||||
//
|
return err
|
||||||
// i.e. only validate if we are performing an actual delete and not
|
|
||||||
// an untag op
|
|
||||||
if repoName != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
|
|
||||||
// Prevent deletion if image is used by a container
|
|
||||||
if err := srv.canDeleteImage(img.ID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//If delete by id, see if the id belong only to one repository
|
//If delete by id, see if the id belong only to one repository
|
||||||
|
@ -1917,10 +1841,10 @@ func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, erro
|
||||||
if parsedTag != "" {
|
if parsedTag != "" {
|
||||||
tags = append(tags, parsedTag)
|
tags = append(tags, parsedTag)
|
||||||
}
|
}
|
||||||
} else if repoName != parsedRepo {
|
} else if repoName != parsedRepo && !force {
|
||||||
// the id belongs to multiple repos, like base:latest and user:test,
|
// the id belongs to multiple repos, like base:latest and user:test,
|
||||||
// in that case return conflict
|
// in that case return conflict
|
||||||
return nil, fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories", utils.TruncateID(img.ID))
|
return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1931,37 +1855,51 @@ func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, erro
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
|
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if tagDeleted {
|
if tagDeleted {
|
||||||
out := &engine.Env{}
|
out := &engine.Env{}
|
||||||
out.Set("Untagged", img.ID)
|
out.Set("Untagged", repoName+":"+tag)
|
||||||
imgs.Add(out)
|
imgs.Add(out)
|
||||||
srv.LogEvent("untag", img.ID, "")
|
srv.LogEvent("untag", img.ID, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tags = srv.runtime.repositories.ByID()[img.ID]
|
||||||
|
if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
|
||||||
|
if len(byParents[img.ID]) == 0 {
|
||||||
|
if err := srv.canDeleteImage(img.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := srv.runtime.repositories.DeleteAll(img.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := srv.runtime.graph.Delete(img.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out := &engine.Env{}
|
||||||
|
out.Set("Deleted", img.ID)
|
||||||
|
imgs.Add(out)
|
||||||
|
srv.LogEvent("delete", img.ID, "")
|
||||||
|
if img.Parent != "" {
|
||||||
|
err := srv.DeleteImage(img.Parent, imgs, false, force)
|
||||||
|
if first {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
|
|
||||||
if err := srv.deleteImageAndChildren(img.ID, imgs, nil); err != nil {
|
|
||||||
if err != ErrImageReferenced {
|
|
||||||
return imgs, err
|
|
||||||
}
|
|
||||||
} else if err := srv.deleteImageParents(img, imgs); err != nil {
|
|
||||||
if err != ErrImageReferenced {
|
|
||||||
return imgs, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return imgs, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
||||||
if n := len(job.Args); n != 1 {
|
if n := len(job.Args); n != 1 {
|
||||||
return job.Errorf("Usage: %s IMAGE", job.Name)
|
return job.Errorf("Usage: %s IMAGE", job.Name)
|
||||||
}
|
}
|
||||||
|
var imgs = engine.NewTable("", 0)
|
||||||
imgs, err := srv.DeleteImage(job.Args[0], job.GetenvBool("autoPrune"))
|
if err := srv.DeleteImage(job.Args[0], imgs, true, job.GetenvBool("force")); err != nil {
|
||||||
if err != nil {
|
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
if len(imgs.Data) == 0 {
|
if len(imgs.Data) == 0 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue