Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
| ... | ... |
@@ -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 {
|