123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- package daemon
- import (
- "fmt"
- "strings"
- "github.com/docker/docker/engine"
- "github.com/docker/docker/graph"
- "github.com/docker/docker/image"
- "github.com/docker/docker/pkg/parsers"
- "github.com/docker/docker/utils"
- )
- 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
- }
- img, err := daemon.Repositories().LookupImage(name)
- if err != nil {
- if r, _ := daemon.Repositories().Get(repoName); r != nil {
- return fmt.Errorf("No such image: %s:%s", repoName, tag)
- }
- 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
- }
- repos := daemon.Repositories().ByID()[img.ID]
- //If delete by id, see if the id belong only to one repository
- if repoName == "" {
- for _, repoAndTag := range repos {
- parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag)
- if repoName == "" || repoName == parsedRepo {
- repoName = parsedRepo
- if parsedTag != "" {
- tags = append(tags, parsedTag)
- }
- } else if repoName != parsedRepo && !force {
- // 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
- }
- if len(repos) <= 1 {
- if err := daemon.canDeleteImage(img.ID, force); err != nil {
- return err
- }
- }
- // Untag the current image
- for _, tag := range tags {
- tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
- if err != nil {
- return err
- }
- if tagDeleted {
- out := &engine.Env{}
- out.Set("Untagged", repoName+":"+tag)
- imgs.Add(out)
- eng.Job("log", "untag", img.ID, "").Run()
- }
- }
- 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{}
- out.SetJson("Deleted", img.ID)
- imgs.Add(out)
- eng.Job("log", "delete", img.ID, "").Run()
- if img.Parent != "" && !noprune {
- err := daemon.DeleteImage(eng, img.Parent, imgs, false, force, noprune)
- if first {
- return err
- }
- }
- }
- }
- return nil
- }
- func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
- for _, container := range daemon.List() {
- parent, err := daemon.Repositories().LookupImage(container.ImageID)
- if err != nil {
- if daemon.Graph().IsNotExist(err) {
- return nil
- }
- return err
- }
- if err := parent.WalkHistory(func(p *image.Image) error {
- if imgID == p.ID {
- if container.IsRunning() {
- if force {
- return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it, stop it and retry", utils.TruncateID(imgID), utils.TruncateID(container.ID))
- }
- return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it, stop it and use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID))
- } else if !force {
- return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it, use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID))
- }
- }
- return nil
- }); err != nil {
- return err
- }
- }
- return nil
- }
|