Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
This commit is contained in:
Victor Vieux 2014-01-16 18:40:33 -08:00
Родитель 4a96c329c0
Коммит 564e6bc780
6 изменённых файлов: 101 добавлений и 71 удалений

32
api.go
Просмотреть файл

@ -668,19 +668,31 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
if vars == nil {
return fmt.Errorf("Missing parameter")
}
name := vars["name"]
imgs, err := srv.ImageDelete(name, version > 1.1)
if err != nil {
var (
buffer = bytes.NewBuffer(nil)
job = srv.Eng.Job("image_delete", vars["name"])
)
job.Stdout.Add(buffer)
job.SetenvBool("autoPrune", version > 1.1)
if err := job.Run(); err != nil {
return err
}
if imgs != nil {
if len(imgs) != 0 {
return writeJSON(w, http.StatusOK, imgs)
}
return fmt.Errorf("Conflict, %s wasn't deleted", name)
outs := engine.NewTable("", 0)
if _, err := outs.ReadFrom(buffer); err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
return nil
if len(outs.Data) != 0 {
var err error
if version < 1.9 {
_, err = outs.WriteTo(w)
} else {
_, err = outs.WriteListTo(w)
}
return err
}
return fmt.Errorf("Conflict, %s wasn't deleted", vars["name"])
}
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 {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
} else {
var outs []APIRmi
err = json.Unmarshal(body, &outs)
if err != nil {
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadFrom(bytes.NewReader(body)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
continue
}
for _, out := range outs {
if out.Deleted != "" {
fmt.Fprintf(cli.out, "Deleted: %s\n", out.Deleted)
for _, out := range outs.Data {
if out.Get("Deleted") != "" {
fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
} else {
fmt.Fprintf(cli.out, "Untagged: %s\n", out.Untagged)
fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
}
}
}

Просмотреть файл

@ -1030,11 +1030,11 @@ func TestContainerOrphaning(t *testing.T) {
buildSomething(template2, imageName)
// remove the second image by name
resp, err := srv.ImageDelete(imageName, true)
resp, err := srv.DeleteImage(imageName, true)
// see if we deleted the first image (and orphaned the container)
for _, i := range resp {
if img1 == i.Deleted {
for _, i := range resp.Data {
if img1 == i.Get("Deleted") {
t.Fatal("Orphaned image with container")
}
}

Просмотреть файл

@ -61,7 +61,7 @@ func cleanup(eng *engine.Engine, t *testing.T) error {
}
for _, image := range images.Data {
if image.Get("ID") != unitTestImageID {
mkServerFromEngine(eng, t).ImageDelete(image.Get("ID"), false)
mkServerFromEngine(eng, t).DeleteImage(image.Get("ID"), false)
}
}
return nil

Просмотреть файл

@ -35,7 +35,7 @@ func TestImageTagImageDelete(t *testing.T) {
t.Errorf("Expected %d images, %d found", nExpected, nActual)
}
if _, err := srv.ImageDelete("utest/docker:tag2", true); err != nil {
if _, err := srv.DeleteImage("utest/docker:tag2", true); err != nil {
t.Fatal(err)
}
@ -47,7 +47,7 @@ func TestImageTagImageDelete(t *testing.T) {
t.Errorf("Expected %d images, %d found", nExpected, nActual)
}
if _, err := srv.ImageDelete("utest:5000/docker:tag3", true); err != nil {
if _, err := srv.DeleteImage("utest:5000/docker:tag3", true); err != nil {
t.Fatal(err)
}
@ -56,7 +56,7 @@ func TestImageTagImageDelete(t *testing.T) {
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
nActual = len(images.Data[0].GetList("RepoTags"))
if _, err := srv.ImageDelete("utest:tag1", true); err != nil {
if _, err := srv.DeleteImage("utest:tag1", true); err != nil {
t.Fatal(err)
}
@ -358,7 +358,7 @@ func TestRmi(t *testing.T) {
t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
}
_, err = srv.ImageDelete(imageID, true)
_, err = srv.DeleteImage(imageID, true)
if err != nil {
t.Fatal(err)
}
@ -472,18 +472,16 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
}
// Try to remove the tag
imgs, err := srv.ImageDelete("utest:tag1", true)
imgs, err := srv.DeleteImage("utest:tag1", true)
if err != nil {
t.Fatal(err)
}
if len(imgs) != 1 {
t.Fatalf("Should only have deleted one untag %d", len(imgs))
if len(imgs.Data) != 1 {
t.Fatalf("Should only have deleted one untag %d", len(imgs.Data))
}
untag := imgs[0]
if untag.Untagged != unitTestImageID {
t.Fatalf("Expected %s got %s", unitTestImageID, untag.Untagged)
if untag := imgs.Data[0].Get("Untagged"); untag != unitTestImageID {
t.Fatalf("Expected %s got %s", unitTestImageID, untag)
}
}

Просмотреть файл

@ -336,6 +336,10 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status {
job.Error(err)
return engine.StatusErr
}
if err := job.Eng.Register("image_delete", srv.ImageDelete); err != nil {
job.Error(err)
return engine.StatusErr
}
return engine.StatusOK
}
@ -1813,7 +1817,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
var ErrImageReferenced = errors.New("Image referenced by a repository")
func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi, byParents map[string][]*Image) error {
func (srv *Server) deleteImageAndChildren(id string, imgs *engine.Table, byParents map[string][]*Image) error {
// If the image is referenced by a repo, do not delete
if len(srv.runtime.repositories.ByID()[id]) != 0 {
return ErrImageReferenced
@ -1845,14 +1849,16 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi, byParents m
if err != nil {
return err
}
*imgs = append(*imgs, APIRmi{Deleted: id})
out := &engine.Env{}
out.Set("Deleted", id)
imgs.Add(out)
srv.LogEvent("delete", id, "")
return nil
}
return nil
}
func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
func (srv *Server) deleteImageParents(img *Image, imgs *engine.Table) error {
if img.Parent != "" {
parent, err := srv.runtime.graph.Get(img.Parent)
if err != nil {
@ -1871,12 +1877,42 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
return nil
}
func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) {
func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, error) {
var (
imgs = []APIRmi{}
tags = []string{}
repoName, tag string
img, err = srv.runtime.repositories.LookupImage(name)
imgs = engine.NewTable("", 0)
tags = []string{}
)
if err != nil {
return nil, fmt.Errorf("No such image: %s", name)
}
// FIXME: What does autoPrune mean ?
if !autoPrune {
if err := srv.runtime.graph.Delete(img.ID); err != nil {
return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
}
return nil, nil
}
if !strings.Contains(img.ID, name) {
repoName, tag = utils.ParseRepositoryTag(name)
}
// If we have a repo and the image is not referenced anywhere else
// then just perform an untag and do not validate.
//
// i.e. only validate if we are performing an actual delete and not
// an untag op
if repoName != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
// Prevent deletion if image is used by a container
if err := srv.canDeleteImage(img.ID); err != nil {
return nil, err
}
}
//If delete by id, see if the id belong only to one repository
if repoName == "" {
for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] {
@ -1903,17 +1939,19 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
return nil, err
}
if tagDeleted {
imgs = append(imgs, APIRmi{Untagged: img.ID})
out := &engine.Env{}
out.Set("Untagged", img.ID)
imgs.Add(out)
srv.LogEvent("untag", img.ID, "")
}
}
if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
if err := srv.deleteImageAndChildren(img.ID, &imgs, nil); err != nil {
if err := srv.deleteImageAndChildren(img.ID, imgs, nil); err != nil {
if err != ErrImageReferenced {
return imgs, err
}
} else if err := srv.deleteImageParents(img, &imgs); err != nil {
} else if err := srv.deleteImageParents(img, imgs); err != nil {
if err != ErrImageReferenced {
return imgs, err
}
@ -1922,39 +1960,22 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro
return imgs, nil
}
func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) {
var (
repository, tag string
img, err = srv.runtime.repositories.LookupImage(name)
)
func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
if n := len(job.Args); n != 1 {
job.Errorf("Usage: %s IMAGE", job.Name)
return engine.StatusErr
}
imgs, err := srv.DeleteImage(job.Args[0], job.GetenvBool("autoPrune"))
if err != nil {
return nil, fmt.Errorf("No such image: %s", name)
job.Error(err)
return engine.StatusErr
}
// FIXME: What does autoPrune mean ?
if !autoPrune {
if err := srv.runtime.graph.Delete(img.ID); err != nil {
return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
}
return nil, nil
if _, err := imgs.WriteTo(job.Stdout); err != nil {
job.Error(err)
return engine.StatusErr
}
if !strings.Contains(img.ID, name) {
repository, tag = utils.ParseRepositoryTag(name)
}
// If we have a repo and the image is not referenced anywhere else
// then just perform an untag and do not validate.
//
// i.e. only validate if we are performing an actual delete and not
// an untag op
if repository != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
// Prevent deletion if image is used by a container
if err := srv.canDeleteImage(img.ID); err != nil {
return nil, err
}
}
return srv.deleteImage(img, repository, tag)
return engine.StatusOK
}
func (srv *Server) canDeleteImage(imgID string) error {