daemon/image_delete.go
7a5e3df1
 package daemon
 
 import (
 	"fmt"
 	"strings"
 
 	"github.com/docker/docker/engine"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/image"
7a9c944b
 	"github.com/docker/docker/pkg/common"
7a5e3df1
 	"github.com/docker/docker/pkg/parsers"
a2b0c977
 	"github.com/docker/docker/utils"
7a5e3df1
 )
 
 func (daemon *Daemon) ImageDelete(job *engine.Job) engine.Status {
 	if n := len(job.Args); n != 1 {
 		return job.Errorf("Usage: %s IMAGE", job.Name)
 	}
 	imgs := engine.NewTable("", 0)
 	if err := daemon.DeleteImage(job.Eng, job.Args[0], imgs, true, job.GetenvBool("force"), job.GetenvBool("noprune")); err != nil {
 		return job.Error(err)
 	}
 	if len(imgs.Data) == 0 {
 		return job.Errorf("Conflict, %s wasn't deleted", job.Args[0])
 	}
 	if _, err := imgs.WriteListTo(job.Stdout); err != nil {
 		return job.Error(err)
 	}
 	return engine.StatusOK
 }
 
 // FIXME: make this private and use the job instead
 func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine.Table, first, force, noprune bool) error {
 	var (
 		repoName, tag string
 		tags          = []string{}
 	)
 
 	// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
 	repoName, tag = parsers.ParseRepositoryTag(name)
 	if tag == "" {
 		tag = graph.DEFAULTTAG
 	}
 
eeb36c93
 	if name == "" {
 		return fmt.Errorf("Image name can not be blank")
 	}
 
7a5e3df1
 	img, err := daemon.Repositories().LookupImage(name)
 	if err != nil {
 		if r, _ := daemon.Repositories().Get(repoName); r != nil {
a2b0c977
 			return fmt.Errorf("No such image: %s", utils.ImageReference(repoName, tag))
7a5e3df1
 		}
 		return fmt.Errorf("No such image: %s", name)
 	}
 
 	if strings.Contains(img.ID, name) {
 		repoName = ""
 		tag = ""
 	}
 
 	byParents, err := daemon.Graph().ByParent()
 	if err != nil {
 		return err
 	}
 
b2efdc53
 	repos := daemon.Repositories().ByID()[img.ID]
 
7a5e3df1
 	//If delete by id, see if the id belong only to one repository
 	if repoName == "" {
b2efdc53
 		for _, repoAndTag := range repos {
7a5e3df1
 			parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag)
 			if repoName == "" || repoName == parsedRepo {
 				repoName = parsedRepo
 				if parsedTag != "" {
 					tags = append(tags, parsedTag)
 				}
d9d91755
 			} else if repoName != parsedRepo && !force && first {
7a5e3df1
 				// the id belongs to multiple repos, like base:latest and user:test,
 				// in that case return conflict
 				return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
 			}
 		}
 	} else {
 		tags = append(tags, tag)
 	}
 
 	if !first && len(tags) > 0 {
 		return nil
 	}
 
b2efdc53
 	if len(repos) <= 1 {
 		if err := daemon.canDeleteImage(img.ID, force); err != nil {
 			return err
 		}
 	}
 
 	// Untag the current image
7a5e3df1
 	for _, tag := range tags {
b2efdc53
 		tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
7a5e3df1
 		if err != nil {
 			return err
 		}
 		if tagDeleted {
 			out := &engine.Env{}
a2b0c977
 			out.Set("Untagged", utils.ImageReference(repoName, tag))
7a5e3df1
 			imgs.Add(out)
1c11d7f9
 			eng.Job("log", "untag", img.ID, "").Run()
7a5e3df1
 		}
 	}
 	tags = daemon.Repositories().ByID()[img.ID]
 	if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
 		if len(byParents[img.ID]) == 0 {
 			if err := daemon.Repositories().DeleteAll(img.ID); err != nil {
 				return err
 			}
 			if err := daemon.Graph().Delete(img.ID); err != nil {
 				return err
 			}
 			out := &engine.Env{}
d942c59b
 			out.SetJson("Deleted", img.ID)
7a5e3df1
 			imgs.Add(out)
1c11d7f9
 			eng.Job("log", "delete", img.ID, "").Run()
7a5e3df1
 			if img.Parent != "" && !noprune {
 				err := daemon.DeleteImage(eng, img.Parent, imgs, false, force, noprune)
 				if first {
 					return err
 				}
 
 			}
 
 		}
 	}
 	return nil
 }
 
b2efdc53
 func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
7a5e3df1
 	for _, container := range daemon.List() {
89367899
 		parent, err := daemon.Repositories().LookupImage(container.ImageID)
7a5e3df1
 		if err != nil {
ac40e7cb
 			if daemon.Graph().IsNotExist(err) {
 				return nil
 			}
7a5e3df1
 			return err
 		}
 
 		if err := parent.WalkHistory(func(p *image.Image) error {
 			if imgID == p.ID {
e0339d4b
 				if container.IsRunning() {
7a5e3df1
 					if force {
7a9c944b
 						return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it, stop it and retry", common.TruncateID(imgID), common.TruncateID(container.ID))
7a5e3df1
 					}
7a9c944b
 					return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it, stop it and use -f to force", common.TruncateID(imgID), common.TruncateID(container.ID))
7a5e3df1
 				} else if !force {
7a9c944b
 					return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it, use -f to force", common.TruncateID(imgID), common.TruncateID(container.ID))
7a5e3df1
 				}
 			}
 			return nil
 		}); err != nil {
 			return err
 		}
 	}
 	return nil
 }