Browse code

added proper returns type, move the auto-prune in v1.1 api

Victor Vieux authored on 2013/05/31 23:37:02
Showing 8 changed files
... ...
@@ -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
 
... ...
@@ -23,6 +23,11 @@ type ApiInfo struct {
23 23
 	NGoroutines int `json:",omitempty"`
24 24
 }
25 25
 
26
+type ApiRmi struct {
27
+	Deleted  string `json:",omitempty"`
28
+	Untagged string `json:",omitempty"`
29
+}
30
+
26 31
 type ApiContainers struct {
27 32
 	Id      string
28 33
 	Image   string
... ...
@@ -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 {