Browse code

Move docker rmi to a job

Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)

Victor Vieux authored on 2014/01/17 11:40:33
Showing 6 changed files
... ...
@@ -668,19 +668,31 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
668 668
 	if vars == nil {
669 669
 		return fmt.Errorf("Missing parameter")
670 670
 	}
671
-	name := vars["name"]
672
-	imgs, err := srv.ImageDelete(name, version > 1.1)
673
-	if err != nil {
671
+	var (
672
+		buffer = bytes.NewBuffer(nil)
673
+		job    = srv.Eng.Job("image_delete", vars["name"])
674
+	)
675
+	job.Stdout.Add(buffer)
676
+	job.SetenvBool("autoPrune", version > 1.1)
677
+	if err := job.Run(); err != nil {
674 678
 		return err
675 679
 	}
676
-	if imgs != nil {
677
-		if len(imgs) != 0 {
678
-			return writeJSON(w, http.StatusOK, imgs)
680
+
681
+	outs := engine.NewTable("", 0)
682
+	if _, err := outs.ReadFrom(buffer); err != nil {
683
+		return err
684
+	}
685
+
686
+	if len(outs.Data) != 0 {
687
+		var err error
688
+		if version < 1.9 {
689
+			_, err = outs.WriteTo(w)
690
+		} else {
691
+			_, err = outs.WriteListTo(w)
679 692
 		}
680
-		return fmt.Errorf("Conflict, %s wasn't deleted", name)
693
+		return err
681 694
 	}
682
-	w.WriteHeader(http.StatusNoContent)
683
-	return nil
695
+	return fmt.Errorf("Conflict, %s wasn't deleted", vars["name"])
684 696
 }
685 697
 
686 698
 func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -828,18 +828,17 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
828 828
 			fmt.Fprintf(cli.err, "%s\n", err)
829 829
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
830 830
 		} else {
831
-			var outs []APIRmi
832
-			err = json.Unmarshal(body, &outs)
833
-			if err != nil {
831
+			outs := engine.NewTable("Created", 0)
832
+			if _, err := outs.ReadFrom(bytes.NewReader(body)); err != nil {
834 833
 				fmt.Fprintf(cli.err, "%s\n", err)
835 834
 				encounteredError = fmt.Errorf("Error: failed to remove one or more images")
836 835
 				continue
837 836
 			}
838
-			for _, out := range outs {
839
-				if out.Deleted != "" {
840
-					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Deleted)
837
+			for _, out := range outs.Data {
838
+				if out.Get("Deleted") != "" {
839
+					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
841 840
 				} else {
842
-					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Untagged)
841
+					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
843 842
 				}
844 843
 			}
845 844
 		}
... ...
@@ -1030,11 +1030,11 @@ func TestContainerOrphaning(t *testing.T) {
1030 1030
 	buildSomething(template2, imageName)
1031 1031
 
1032 1032
 	// remove the second image by name
1033
-	resp, err := srv.ImageDelete(imageName, true)
1033
+	resp, err := srv.DeleteImage(imageName, true)
1034 1034
 
1035 1035
 	// see if we deleted the first image (and orphaned the container)
1036
-	for _, i := range resp {
1037
-		if img1 == i.Deleted {
1036
+	for _, i := range resp.Data {
1037
+		if img1 == i.Get("Deleted") {
1038 1038
 			t.Fatal("Orphaned image with container")
1039 1039
 		}
1040 1040
 	}
... ...
@@ -61,7 +61,7 @@ func cleanup(eng *engine.Engine, t *testing.T) error {
61 61
 	}
62 62
 	for _, image := range images.Data {
63 63
 		if image.Get("ID") != unitTestImageID {
64
-			mkServerFromEngine(eng, t).ImageDelete(image.Get("ID"), false)
64
+			mkServerFromEngine(eng, t).DeleteImage(image.Get("ID"), false)
65 65
 		}
66 66
 	}
67 67
 	return nil
... ...
@@ -35,7 +35,7 @@ func TestImageTagImageDelete(t *testing.T) {
35 35
 		t.Errorf("Expected %d images, %d found", nExpected, nActual)
36 36
 	}
37 37
 
38
-	if _, err := srv.ImageDelete("utest/docker:tag2", true); err != nil {
38
+	if _, err := srv.DeleteImage("utest/docker:tag2", true); err != nil {
39 39
 		t.Fatal(err)
40 40
 	}
41 41
 
... ...
@@ -47,7 +47,7 @@ func TestImageTagImageDelete(t *testing.T) {
47 47
 		t.Errorf("Expected %d images, %d found", nExpected, nActual)
48 48
 	}
49 49
 
50
-	if _, err := srv.ImageDelete("utest:5000/docker:tag3", true); err != nil {
50
+	if _, err := srv.DeleteImage("utest:5000/docker:tag3", true); err != nil {
51 51
 		t.Fatal(err)
52 52
 	}
53 53
 
... ...
@@ -56,7 +56,7 @@ func TestImageTagImageDelete(t *testing.T) {
56 56
 	nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
57 57
 	nActual = len(images.Data[0].GetList("RepoTags"))
58 58
 
59
-	if _, err := srv.ImageDelete("utest:tag1", true); err != nil {
59
+	if _, err := srv.DeleteImage("utest:tag1", true); err != nil {
60 60
 		t.Fatal(err)
61 61
 	}
62 62
 
... ...
@@ -358,7 +358,7 @@ func TestRmi(t *testing.T) {
358 358
 		t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
359 359
 	}
360 360
 
361
-	_, err = srv.ImageDelete(imageID, true)
361
+	_, err = srv.DeleteImage(imageID, true)
362 362
 	if err != nil {
363 363
 		t.Fatal(err)
364 364
 	}
... ...
@@ -472,18 +472,16 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
472 472
 	}
473 473
 
474 474
 	// Try to remove the tag
475
-	imgs, err := srv.ImageDelete("utest:tag1", true)
475
+	imgs, err := srv.DeleteImage("utest:tag1", true)
476 476
 	if err != nil {
477 477
 		t.Fatal(err)
478 478
 	}
479 479
 
480
-	if len(imgs) != 1 {
481
-		t.Fatalf("Should only have deleted one untag %d", len(imgs))
480
+	if len(imgs.Data) != 1 {
481
+		t.Fatalf("Should only have deleted one untag %d", len(imgs.Data))
482 482
 	}
483 483
 
484
-	untag := imgs[0]
485
-
486
-	if untag.Untagged != unitTestImageID {
487
-		t.Fatalf("Expected %s got %s", unitTestImageID, untag.Untagged)
484
+	if untag := imgs.Data[0].Get("Untagged"); untag != unitTestImageID {
485
+		t.Fatalf("Expected %s got %s", unitTestImageID, untag)
488 486
 	}
489 487
 }
... ...
@@ -336,6 +336,10 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status {
336 336
 		job.Error(err)
337 337
 		return engine.StatusErr
338 338
 	}
339
+	if err := job.Eng.Register("image_delete", srv.ImageDelete); err != nil {
340
+		job.Error(err)
341
+		return engine.StatusErr
342
+	}
339 343
 	return engine.StatusOK
340 344
 }
341 345
 
... ...
@@ -1813,7 +1817,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
1813 1813
 
1814 1814
 var ErrImageReferenced = errors.New("Image referenced by a repository")
1815 1815
 
1816
-func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi, byParents map[string][]*Image) error {
1816
+func (srv *Server) deleteImageAndChildren(id string, imgs *engine.Table, byParents map[string][]*Image) error {
1817 1817
 	// If the image is referenced by a repo, do not delete
1818 1818
 	if len(srv.runtime.repositories.ByID()[id]) != 0 {
1819 1819
 		return ErrImageReferenced
... ...
@@ -1845,14 +1849,16 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi, byParents m
1845 1845
 		if err != nil {
1846 1846
 			return err
1847 1847
 		}
1848
-		*imgs = append(*imgs, APIRmi{Deleted: id})
1848
+		out := &engine.Env{}
1849
+		out.Set("Deleted", id)
1850
+		imgs.Add(out)
1849 1851
 		srv.LogEvent("delete", id, "")
1850 1852
 		return nil
1851 1853
 	}
1852 1854
 	return nil
1853 1855
 }
1854 1856
 
1855
-func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
1857
+func (srv *Server) deleteImageParents(img *Image, imgs *engine.Table) error {
1856 1858
 	if img.Parent != "" {
1857 1859
 		parent, err := srv.runtime.graph.Get(img.Parent)
1858 1860
 		if err != nil {
... ...
@@ -1871,12 +1877,42 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
1871 1871
 	return nil
1872 1872
 }
1873 1873
 
1874
-func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) {
1874
+func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, error) {
1875 1875
 	var (
1876
-		imgs = []APIRmi{}
1877
-		tags = []string{}
1876
+		repoName, tag string
1877
+		img, err      = srv.runtime.repositories.LookupImage(name)
1878
+		imgs          = engine.NewTable("", 0)
1879
+		tags          = []string{}
1878 1880
 	)
1879 1881
 
1882
+	if err != nil {
1883
+		return nil, fmt.Errorf("No such image: %s", name)
1884
+	}
1885
+
1886
+	// FIXME: What does autoPrune mean ?
1887
+	if !autoPrune {
1888
+		if err := srv.runtime.graph.Delete(img.ID); err != nil {
1889
+			return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
1890
+		}
1891
+		return nil, nil
1892
+	}
1893
+
1894
+	if !strings.Contains(img.ID, name) {
1895
+		repoName, tag = utils.ParseRepositoryTag(name)
1896
+	}
1897
+
1898
+	// If we have a repo and the image is not referenced anywhere else
1899
+	// then just perform an untag and do not validate.
1900
+	//
1901
+	// i.e. only validate if we are performing an actual delete and not
1902
+	// an untag op
1903
+	if repoName != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
1904
+		// Prevent deletion if image is used by a container
1905
+		if err := srv.canDeleteImage(img.ID); err != nil {
1906
+			return nil, err
1907
+		}
1908
+	}
1909
+
1880 1910
 	//If delete by id, see if the id belong only to one repository
1881 1911
 	if repoName == "" {
1882 1912
 		for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] {
... ...
@@ -1903,17 +1939,19 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
1903 1903
 			return nil, err
1904 1904
 		}
1905 1905
 		if tagDeleted {
1906
-			imgs = append(imgs, APIRmi{Untagged: img.ID})
1906
+			out := &engine.Env{}
1907
+			out.Set("Untagged", img.ID)
1908
+			imgs.Add(out)
1907 1909
 			srv.LogEvent("untag", img.ID, "")
1908 1910
 		}
1909 1911
 	}
1910 1912
 
1911 1913
 	if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
1912
-		if err := srv.deleteImageAndChildren(img.ID, &imgs, nil); err != nil {
1914
+		if err := srv.deleteImageAndChildren(img.ID, imgs, nil); err != nil {
1913 1915
 			if err != ErrImageReferenced {
1914 1916
 				return imgs, err
1915 1917
 			}
1916
-		} else if err := srv.deleteImageParents(img, &imgs); err != nil {
1918
+		} else if err := srv.deleteImageParents(img, imgs); err != nil {
1917 1919
 			if err != ErrImageReferenced {
1918 1920
 				return imgs, err
1919 1921
 			}
... ...
@@ -1922,39 +1960,22 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
1922 1922
 	return imgs, nil
1923 1923
 }
1924 1924
 
1925
-func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) {
1926
-	var (
1927
-		repository, tag string
1928
-		img, err        = srv.runtime.repositories.LookupImage(name)
1929
-	)
1930
-	if err != nil {
1931
-		return nil, fmt.Errorf("No such image: %s", name)
1932
-	}
1933
-
1934
-	// FIXME: What does autoPrune mean ?
1935
-	if !autoPrune {
1936
-		if err := srv.runtime.graph.Delete(img.ID); err != nil {
1937
-			return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
1938
-		}
1939
-		return nil, nil
1925
+func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
1926
+	if n := len(job.Args); n != 1 {
1927
+		job.Errorf("Usage: %s IMAGE", job.Name)
1928
+		return engine.StatusErr
1940 1929
 	}
1941 1930
 
1942
-	if !strings.Contains(img.ID, name) {
1943
-		repository, tag = utils.ParseRepositoryTag(name)
1931
+	imgs, err := srv.DeleteImage(job.Args[0], job.GetenvBool("autoPrune"))
1932
+	if err != nil {
1933
+		job.Error(err)
1934
+		return engine.StatusErr
1944 1935
 	}
1945
-
1946
-	// If we have a repo and the image is not referenced anywhere else
1947
-	// then just perform an untag and do not validate.
1948
-	//
1949
-	// i.e. only validate if we are performing an actual delete and not
1950
-	// an untag op
1951
-	if repository != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
1952
-		// Prevent deletion if image is used by a container
1953
-		if err := srv.canDeleteImage(img.ID); err != nil {
1954
-			return nil, err
1955
-		}
1936
+	if _, err := imgs.WriteTo(job.Stdout); err != nil {
1937
+		job.Error(err)
1938
+		return engine.StatusErr
1956 1939
 	}
1957
-	return srv.deleteImage(img, repository, tag)
1940
+	return engine.StatusOK
1958 1941
 }
1959 1942
 
1960 1943
 func (srv *Server) canDeleteImage(imgID string) error {