| ... | ... |
@@ -450,10 +450,23 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R |
| 450 | 450 |
return fmt.Errorf("Missing parameter")
|
| 451 | 451 |
} |
| 452 | 452 |
name := vars["name"] |
| 453 |
- if err := srv.ImageDelete(name); err != nil {
|
|
| 453 |
+ imgs, err := srv.ImageDelete(name, version > 1.0) |
|
| 454 |
+ if err != nil {
|
|
| 454 | 455 |
return err |
| 455 | 456 |
} |
| 456 |
- w.WriteHeader(http.StatusNoContent) |
|
| 457 |
+ if imgs != nil {
|
|
| 458 |
+ if len(*imgs) != 0 {
|
|
| 459 |
+ b, err := json.Marshal(imgs) |
|
| 460 |
+ if err != nil {
|
|
| 461 |
+ return err |
|
| 462 |
+ } |
|
| 463 |
+ writeJson(w, b) |
|
| 464 |
+ } else {
|
|
| 465 |
+ return fmt.Errorf("Conflict, %s wasn't deleted", name)
|
|
| 466 |
+ } |
|
| 467 |
+ } else {
|
|
| 468 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 469 |
+ } |
|
| 457 | 470 |
return nil |
| 458 | 471 |
} |
| 459 | 472 |
|
| ... | ... |
@@ -1271,10 +1271,17 @@ func TestDeleteImages(t *testing.T) {
|
| 1271 | 1271 |
if err := deleteImages(srv, API_VERSION, r, req, map[string]string{"name": "test:test"}); err != nil {
|
| 1272 | 1272 |
t.Fatal(err) |
| 1273 | 1273 |
} |
| 1274 |
- if r.Code != http.StatusNoContent {
|
|
| 1275 |
- t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
|
| 1274 |
+ if r.Code != http.StatusOK {
|
|
| 1275 |
+ t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
|
|
| 1276 | 1276 |
} |
| 1277 | 1277 |
|
| 1278 |
+ var outs []ApiRmi |
|
| 1279 |
+ if err := json.Unmarshal(r.Body.Bytes(), &outs); err != nil {
|
|
| 1280 |
+ t.Fatal(err) |
|
| 1281 |
+ } |
|
| 1282 |
+ if len(outs) != 1 {
|
|
| 1283 |
+ t.Fatalf("Expected %d event (untagged), got %d", 1, len(outs))
|
|
| 1284 |
+ } |
|
| 1278 | 1285 |
images, err = srv.Images(false, "") |
| 1279 | 1286 |
if err != nil {
|
| 1280 | 1287 |
t.Fatal(err) |
| ... | ... |
@@ -567,10 +567,22 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
| 567 | 567 |
} |
| 568 | 568 |
|
| 569 | 569 |
for _, name := range cmd.Args() {
|
| 570 |
- if _, _, err := cli.call("DELETE", "/images/"+name, nil); err != nil {
|
|
| 571 |
- fmt.Fprintf(os.Stderr, "%s\n", err) |
|
| 570 |
+ body, _, err := cli.call("DELETE", "/images/"+name, nil)
|
|
| 571 |
+ if err != nil {
|
|
| 572 |
+ fmt.Fprintf(os.Stderr, "%s", err) |
|
| 572 | 573 |
} else {
|
| 573 |
- fmt.Println(name) |
|
| 574 |
+ var outs []ApiRmi |
|
| 575 |
+ err = json.Unmarshal(body, &outs) |
|
| 576 |
+ if err != nil {
|
|
| 577 |
+ return err |
|
| 578 |
+ } |
|
| 579 |
+ for _, out := range outs {
|
|
| 580 |
+ if out.Deleted != "" {
|
|
| 581 |
+ fmt.Println("Deleted:", out.Deleted)
|
|
| 582 |
+ } else {
|
|
| 583 |
+ fmt.Println("Untagged:", out.Untagged)
|
|
| 584 |
+ } |
|
| 585 |
+ } |
|
| 574 | 586 |
} |
| 575 | 587 |
} |
| 576 | 588 |
return nil |
| ... | ... |
@@ -750,13 +750,27 @@ Remove an image |
| 750 | 750 |
|
| 751 | 751 |
DELETE /images/test HTTP/1.1 |
| 752 | 752 |
|
| 753 |
- **Example response**: |
|
| 753 |
+ **Example response v1.0**: |
|
| 754 | 754 |
|
| 755 | 755 |
.. sourcecode:: http |
| 756 | 756 |
|
| 757 | 757 |
HTTP/1.1 204 OK |
| 758 | 758 |
|
| 759 |
+ **Example response v1.1**: |
|
| 760 |
+ |
|
| 761 |
+ .. sourcecode:: http |
|
| 762 |
+ |
|
| 763 |
+ HTTP/1.1 200 OK |
|
| 764 |
+ Content-type: application/json |
|
| 765 |
+ |
|
| 766 |
+ [ |
|
| 767 |
+ {"Untagged":"3e2f21a89f"},
|
|
| 768 |
+ {"Deleted":"3e2f21a89f"},
|
|
| 769 |
+ {"Deleted":"53b4f83ac9"}
|
|
| 770 |
+ ] |
|
| 771 |
+ |
|
| 759 | 772 |
:query force: 1/True/true or 0/False/false, default false |
| 773 |
+ :statuscode 200: no error |
|
| 760 | 774 |
:statuscode 204: no error |
| 761 | 775 |
:statuscode 404: no such image |
| 762 | 776 |
:statuscode 409: conflict |
| ... | ... |
@@ -707,7 +707,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
|
| 707 | 707 |
|
| 708 | 708 |
var ErrImageReferenced = errors.New("Image referenced by a repository")
|
| 709 | 709 |
|
| 710 |
-func (srv *Server) deleteImageAndChildren(id string) error {
|
|
| 710 |
+func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error {
|
|
| 711 | 711 |
// If the image is referenced by a repo, do not delete |
| 712 | 712 |
if len(srv.runtime.repositories.ById()[id]) != 0 {
|
| 713 | 713 |
return ErrImageReferenced |
| ... | ... |
@@ -720,7 +720,7 @@ func (srv *Server) deleteImageAndChildren(id string) error {
|
| 720 | 720 |
return err |
| 721 | 721 |
} |
| 722 | 722 |
for _, img := range byParents[id] {
|
| 723 |
- if err := srv.deleteImageAndChildren(img.Id); err != nil {
|
|
| 723 |
+ if err := srv.deleteImageAndChildren(img.Id, imgs); err != nil {
|
|
| 724 | 724 |
if err != ErrImageReferenced {
|
| 725 | 725 |
return err |
| 726 | 726 |
} else {
|
| ... | ... |
@@ -741,49 +741,65 @@ func (srv *Server) deleteImageAndChildren(id string) error {
|
| 741 | 741 |
if err := srv.runtime.repositories.DeleteAll(id); err != nil {
|
| 742 | 742 |
return err |
| 743 | 743 |
} |
| 744 |
- return srv.runtime.graph.Delete(id) |
|
| 744 |
+ err := srv.runtime.graph.Delete(id) |
|
| 745 |
+ if err != nil {
|
|
| 746 |
+ return err |
|
| 747 |
+ } |
|
| 748 |
+ *imgs = append(*imgs, ApiRmi{Deleted: utils.TruncateId(id)})
|
|
| 749 |
+ return nil |
|
| 745 | 750 |
} |
| 746 | 751 |
return nil |
| 747 | 752 |
} |
| 748 | 753 |
|
| 749 |
-func (srv *Server) deleteImageParents(img *Image) error {
|
|
| 754 |
+func (srv *Server) deleteImageParents(img *Image, imgs *[]ApiRmi) error {
|
|
| 750 | 755 |
if img.Parent != "" {
|
| 751 | 756 |
parent, err := srv.runtime.graph.Get(img.Parent) |
| 752 | 757 |
if err != nil {
|
| 753 | 758 |
return err |
| 754 | 759 |
} |
| 755 | 760 |
// Remove all children images |
| 756 |
- if err := srv.deleteImageAndChildren(img.Parent); err != nil {
|
|
| 761 |
+ if err := srv.deleteImageAndChildren(img.Parent, imgs); err != nil {
|
|
| 757 | 762 |
return err |
| 758 | 763 |
} |
| 759 |
- return srv.deleteImageParents(parent) |
|
| 764 |
+ return srv.deleteImageParents(parent, imgs) |
|
| 760 | 765 |
} |
| 761 | 766 |
return nil |
| 762 | 767 |
} |
| 763 | 768 |
|
| 764 |
-func (srv *Server) deleteImage(img *Image, repoName, tag string) error {
|
|
| 769 |
+func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]ApiRmi, error) {
|
|
| 765 | 770 |
//Untag the current image |
| 766 |
- if err := srv.runtime.repositories.Delete(repoName, tag); err != nil {
|
|
| 767 |
- return err |
|
| 771 |
+ var imgs []ApiRmi |
|
| 772 |
+ tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) |
|
| 773 |
+ if err != nil {
|
|
| 774 |
+ return nil, err |
|
| 775 |
+ } |
|
| 776 |
+ if tagDeleted {
|
|
| 777 |
+ imgs = append(imgs, ApiRmi{Untagged: img.ShortId()})
|
|
| 768 | 778 |
} |
| 769 | 779 |
if len(srv.runtime.repositories.ById()[img.Id]) == 0 {
|
| 770 |
- if err := srv.deleteImageAndChildren(img.Id); err != nil {
|
|
| 780 |
+ if err := srv.deleteImageAndChildren(img.Id, &imgs); err != nil {
|
|
| 771 | 781 |
if err != ErrImageReferenced {
|
| 772 |
- return err |
|
| 782 |
+ return &imgs, err |
|
| 773 | 783 |
} |
| 774 |
- } else if err := srv.deleteImageParents(img); err != nil {
|
|
| 784 |
+ } else if err := srv.deleteImageParents(img, &imgs); err != nil {
|
|
| 775 | 785 |
if err != ErrImageReferenced {
|
| 776 |
- return err |
|
| 786 |
+ return &imgs, err |
|
| 777 | 787 |
} |
| 778 | 788 |
} |
| 779 | 789 |
} |
| 780 |
- return nil |
|
| 790 |
+ return &imgs, nil |
|
| 781 | 791 |
} |
| 782 | 792 |
|
| 783 |
-func (srv *Server) ImageDelete(name string) error {
|
|
| 793 |
+func (srv *Server) ImageDelete(name string, autoPrune bool) (*[]ApiRmi, error) {
|
|
| 784 | 794 |
img, err := srv.runtime.repositories.LookupImage(name) |
| 785 | 795 |
if err != nil {
|
| 786 |
- return fmt.Errorf("No such image: %s", name)
|
|
| 796 |
+ return nil, fmt.Errorf("No such image: %s", name)
|
|
| 797 |
+ } |
|
| 798 |
+ if !autoPrune {
|
|
| 799 |
+ if err := srv.runtime.graph.Delete(img.Id); err != nil {
|
|
| 800 |
+ return nil, fmt.Errorf("Error deleting image %s: %s", name, err.Error())
|
|
| 801 |
+ } |
|
| 802 |
+ return nil, nil |
|
| 787 | 803 |
} |
| 788 | 804 |
|
| 789 | 805 |
var tag string |
| ... | ... |
@@ -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 |
|
| ... | ... |
@@ -118,11 +118,11 @@ func (store *TagStore) DeleteAll(id string) error {
|
| 118 | 118 |
for _, name := range names {
|
| 119 | 119 |
if strings.Contains(name, ":") {
|
| 120 | 120 |
nameParts := strings.Split(name, ":") |
| 121 |
- if err := store.Delete(nameParts[0], nameParts[1]); err != nil {
|
|
| 121 |
+ if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
|
|
| 122 | 122 |
return err |
| 123 | 123 |
} |
| 124 | 124 |
} else {
|
| 125 |
- if err := store.Delete(name, ""); err != nil {
|
|
| 125 |
+ if _, err := store.Delete(name, ""); err != nil {
|
|
| 126 | 126 |
return err |
| 127 | 127 |
} |
| 128 | 128 |
} |
| ... | ... |
@@ -130,9 +130,10 @@ func (store *TagStore) DeleteAll(id string) error {
|
| 130 | 130 |
return nil |
| 131 | 131 |
} |
| 132 | 132 |
|
| 133 |
-func (store *TagStore) Delete(repoName, tag string) error {
|
|
| 133 |
+func (store *TagStore) Delete(repoName, tag string) (bool, error) {
|
|
| 134 |
+ deleted := false |
|
| 134 | 135 |
if err := store.Reload(); err != nil {
|
| 135 |
- return err |
|
| 136 |
+ return false, err |
|
| 136 | 137 |
} |
| 137 | 138 |
if r, exists := store.Repositories[repoName]; exists {
|
| 138 | 139 |
if tag != "" {
|
| ... | ... |
@@ -141,16 +142,18 @@ func (store *TagStore) Delete(repoName, tag string) error {
|
| 141 | 141 |
if len(r) == 0 {
|
| 142 | 142 |
delete(store.Repositories, repoName) |
| 143 | 143 |
} |
| 144 |
+ deleted = true |
|
| 144 | 145 |
} else {
|
| 145 |
- return fmt.Errorf("No such tag: %s:%s", repoName, tag)
|
|
| 146 |
+ return false, fmt.Errorf("No such tag: %s:%s", repoName, tag)
|
|
| 146 | 147 |
} |
| 147 | 148 |
} else {
|
| 148 | 149 |
delete(store.Repositories, repoName) |
| 150 |
+ deleted = true |
|
| 149 | 151 |
} |
| 150 | 152 |
} else {
|
| 151 | 153 |
fmt.Errorf("No such repository: %s", repoName)
|
| 152 | 154 |
} |
| 153 |
- return store.Save() |
|
| 155 |
+ return deleted, store.Save() |
|
| 154 | 156 |
} |
| 155 | 157 |
|
| 156 | 158 |
func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|