| ... | ... |
@@ -36,6 +36,8 @@ func httpError(w http.ResponseWriter, err error) {
|
| 36 | 36 |
http.Error(w, err.Error(), http.StatusNotFound) |
| 37 | 37 |
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
| 38 | 38 |
http.Error(w, err.Error(), http.StatusBadRequest) |
| 39 |
+ } else if strings.HasPrefix(err.Error(), "Conflict") {
|
|
| 40 |
+ http.Error(w, err.Error(), http.StatusConflict) |
|
| 39 | 41 |
} else {
|
| 40 | 42 |
http.Error(w, err.Error(), http.StatusInternalServerError) |
| 41 | 43 |
} |
| ... | ... |
@@ -453,11 +455,18 @@ func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars |
| 453 | 453 |
} |
| 454 | 454 |
|
| 455 | 455 |
func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 456 |
+ if err := parseForm(r); err != nil {
|
|
| 457 |
+ return err |
|
| 458 |
+ } |
|
| 456 | 459 |
if vars == nil {
|
| 457 | 460 |
return fmt.Errorf("Missing parameter")
|
| 458 | 461 |
} |
| 459 | 462 |
name := vars["name"] |
| 460 |
- if err := srv.ImageDelete(name); err != nil {
|
|
| 463 |
+ force, err := getBoolParam(r.Form.Get("force"))
|
|
| 464 |
+ if err != nil {
|
|
| 465 |
+ return err |
|
| 466 |
+ } |
|
| 467 |
+ if err := srv.ImageDelete(name, force); err != nil {
|
|
| 461 | 468 |
return err |
| 462 | 469 |
} |
| 463 | 470 |
w.WriteHeader(http.StatusNoContent) |
| ... | ... |
@@ -1312,8 +1312,13 @@ func TestDeleteImages(t *testing.T) {
|
| 1312 | 1312 |
t.Errorf("Excepted 2 images, %d found", len(images))
|
| 1313 | 1313 |
} |
| 1314 | 1314 |
|
| 1315 |
+ req, err := http.NewRequest("DELETE", "/images/test:test", nil)
|
|
| 1316 |
+ if err != nil {
|
|
| 1317 |
+ t.Fatal(err) |
|
| 1318 |
+ } |
|
| 1319 |
+ |
|
| 1315 | 1320 |
r := httptest.NewRecorder() |
| 1316 |
- if err := deleteImages(srv, r, nil, map[string]string{"name": "test:test"}); err != nil {
|
|
| 1321 |
+ if err := deleteImages(srv, r, req, map[string]string{"name": "test:test"}); err != nil {
|
|
| 1317 | 1322 |
t.Fatal(err) |
| 1318 | 1323 |
} |
| 1319 | 1324 |
if r.Code != http.StatusNoContent {
|
| ... | ... |
@@ -459,6 +459,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
| 459 | 459 |
// 'docker rmi IMAGE' removes all images with the name IMAGE |
| 460 | 460 |
func (cli *DockerCli) CmdRmi(args ...string) error {
|
| 461 | 461 |
cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
|
| 462 |
+ force := cmd.Bool("f", false, "Force")
|
|
| 462 | 463 |
if err := cmd.Parse(args); err != nil {
|
| 463 | 464 |
return nil |
| 464 | 465 |
} |
| ... | ... |
@@ -467,8 +468,13 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
| 467 | 467 |
return nil |
| 468 | 468 |
} |
| 469 | 469 |
|
| 470 |
+ v := url.Values{}
|
|
| 471 |
+ if *force {
|
|
| 472 |
+ v.Set("force", "1")
|
|
| 473 |
+ } |
|
| 474 |
+ |
|
| 470 | 475 |
for _, name := range cmd.Args() {
|
| 471 |
- _, _, err := cli.call("DELETE", "/images/"+name, nil)
|
|
| 476 |
+ _, _, err := cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil)
|
|
| 472 | 477 |
if err != nil {
|
| 473 | 478 |
fmt.Printf("%s", err)
|
| 474 | 479 |
} else {
|
| ... | ... |
@@ -728,6 +728,7 @@ Tag an image into a repository |
| 728 | 728 |
:statuscode 200: no error |
| 729 | 729 |
:statuscode 400: bad parameter |
| 730 | 730 |
:statuscode 404: no such image |
| 731 |
+ :statuscode 409: conflict |
|
| 731 | 732 |
:statuscode 500: server error |
| 732 | 733 |
|
| 733 | 734 |
|
| ... | ... |
@@ -750,8 +751,10 @@ Remove an image |
| 750 | 750 |
|
| 751 | 751 |
HTTP/1.1 204 OK |
| 752 | 752 |
|
| 753 |
+ :query force: 1/True/true or 0/False/false, default false |
|
| 753 | 754 |
:statuscode 204: no error |
| 754 | 755 |
:statuscode 404: no such image |
| 756 |
+ :statuscode 409: conflict |
|
| 755 | 757 |
:statuscode 500: server error |
| 756 | 758 |
|
| 757 | 759 |
|
| ... | ... |
@@ -710,7 +710,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
|
| 710 | 710 |
return nil |
| 711 | 711 |
} |
| 712 | 712 |
|
| 713 |
-func (srv *Server) ImageDelete(name string) error {
|
|
| 713 |
+func (srv *Server) ImageDelete(name string, force bool) error {
|
|
| 714 | 714 |
img, err := srv.runtime.repositories.LookupImage(name) |
| 715 | 715 |
if err != nil {
|
| 716 | 716 |
return fmt.Errorf("No such image: %s", name)
|
| ... | ... |
@@ -745,11 +745,13 @@ func (srv *Server) ImageDelete(name string) error {
|
| 745 | 745 |
} |
| 746 | 746 |
} |
| 747 | 747 |
// check is the image to delete isn't parent of another image |
| 748 |
- images, _ := srv.runtime.graph.All() |
|
| 749 |
- for _, image := range images {
|
|
| 750 |
- if imgParent, err := image.GetParent(); err == nil && imgParent != nil {
|
|
| 751 |
- if imgParent.Id == img.Id {
|
|
| 752 |
- return fmt.Errorf("Can't delete %s, otherwise %s will be broken", name, image.ShortId())
|
|
| 748 |
+ if !force {
|
|
| 749 |
+ images, _ := srv.runtime.graph.All() |
|
| 750 |
+ for _, image := range images {
|
|
| 751 |
+ if imgParent, err := image.GetParent(); err == nil && imgParent != nil {
|
|
| 752 |
+ if imgParent.Id == img.Id {
|
|
| 753 |
+ return fmt.Errorf("Conflict: Can't delete %s otherwise %s will be broken", name, image.ShortId())
|
|
| 754 |
+ } |
|
| 753 | 755 |
} |
| 754 | 756 |
} |
| 755 | 757 |
} |
| ... | ... |
@@ -29,7 +29,7 @@ func TestContainerTagImageDelete(t *testing.T) {
|
| 29 | 29 |
t.Errorf("Excepted 3 images, %d found", len(images))
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 |
- if err := srv.ImageDelete("utest/docker:tag2"); err != nil {
|
|
| 32 |
+ if err := srv.ImageDelete("utest/docker:tag2", true); err != nil {
|
|
| 33 | 33 |
t.Fatal(err) |
| 34 | 34 |
} |
| 35 | 35 |
|
| ... | ... |
@@ -42,7 +42,7 @@ func TestContainerTagImageDelete(t *testing.T) {
|
| 42 | 42 |
t.Errorf("Excepted 2 images, %d found", len(images))
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
- if err := srv.ImageDelete("utest:tag1"); err != nil {
|
|
| 45 |
+ if err := srv.ImageDelete("utest:tag1", true); err != nil {
|
|
| 46 | 46 |
t.Fatal(err) |
| 47 | 47 |
} |
| 48 | 48 |
|
| ... | ... |
@@ -156,7 +156,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
| 156 | 156 |
} else {
|
| 157 | 157 |
repo = make(map[string]string) |
| 158 | 158 |
if old, exists := store.Repositories[repoName]; exists && !force {
|
| 159 |
- return fmt.Errorf("Tag %s:%s is already set to %s", repoName, tag, old)
|
|
| 159 |
+ return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old)
|
|
| 160 | 160 |
} |
| 161 | 161 |
store.Repositories[repoName] = repo |
| 162 | 162 |
} |