github.com/dougm/docker@v1.5.0/daemon/image_delete.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/docker/docker/engine"
     8  	"github.com/docker/docker/graph"
     9  	"github.com/docker/docker/image"
    10  	"github.com/docker/docker/pkg/parsers"
    11  	"github.com/docker/docker/utils"
    12  )
    13  
    14  func (daemon *Daemon) ImageDelete(job *engine.Job) engine.Status {
    15  	if n := len(job.Args); n != 1 {
    16  		return job.Errorf("Usage: %s IMAGE", job.Name)
    17  	}
    18  	imgs := engine.NewTable("", 0)
    19  	if err := daemon.DeleteImage(job.Eng, job.Args[0], imgs, true, job.GetenvBool("force"), job.GetenvBool("noprune")); err != nil {
    20  		return job.Error(err)
    21  	}
    22  	if len(imgs.Data) == 0 {
    23  		return job.Errorf("Conflict, %s wasn't deleted", job.Args[0])
    24  	}
    25  	if _, err := imgs.WriteListTo(job.Stdout); err != nil {
    26  		return job.Error(err)
    27  	}
    28  	return engine.StatusOK
    29  }
    30  
    31  // FIXME: make this private and use the job instead
    32  func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine.Table, first, force, noprune bool) error {
    33  	var (
    34  		repoName, tag string
    35  		tags          = []string{}
    36  	)
    37  
    38  	// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
    39  	repoName, tag = parsers.ParseRepositoryTag(name)
    40  	if tag == "" {
    41  		tag = graph.DEFAULTTAG
    42  	}
    43  
    44  	img, err := daemon.Repositories().LookupImage(name)
    45  	if err != nil {
    46  		if r, _ := daemon.Repositories().Get(repoName); r != nil {
    47  			return fmt.Errorf("No such image: %s:%s", repoName, tag)
    48  		}
    49  		return fmt.Errorf("No such image: %s", name)
    50  	}
    51  
    52  	if strings.Contains(img.ID, name) {
    53  		repoName = ""
    54  		tag = ""
    55  	}
    56  
    57  	byParents, err := daemon.Graph().ByParent()
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	repos := daemon.Repositories().ByID()[img.ID]
    63  
    64  	//If delete by id, see if the id belong only to one repository
    65  	if repoName == "" {
    66  		for _, repoAndTag := range repos {
    67  			parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag)
    68  			if repoName == "" || repoName == parsedRepo {
    69  				repoName = parsedRepo
    70  				if parsedTag != "" {
    71  					tags = append(tags, parsedTag)
    72  				}
    73  			} else if repoName != parsedRepo && !force {
    74  				// the id belongs to multiple repos, like base:latest and user:test,
    75  				// in that case return conflict
    76  				return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
    77  			}
    78  		}
    79  	} else {
    80  		tags = append(tags, tag)
    81  	}
    82  
    83  	if !first && len(tags) > 0 {
    84  		return nil
    85  	}
    86  
    87  	if len(repos) <= 1 {
    88  		if err := daemon.canDeleteImage(img.ID, force); err != nil {
    89  			return err
    90  		}
    91  	}
    92  
    93  	// Untag the current image
    94  	for _, tag := range tags {
    95  		tagDeleted, err := daemon.Repositories().Delete(repoName, tag)
    96  		if err != nil {
    97  			return err
    98  		}
    99  		if tagDeleted {
   100  			out := &engine.Env{}
   101  			out.Set("Untagged", repoName+":"+tag)
   102  			imgs.Add(out)
   103  			eng.Job("log", "untag", img.ID, "").Run()
   104  		}
   105  	}
   106  	tags = daemon.Repositories().ByID()[img.ID]
   107  	if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
   108  		if len(byParents[img.ID]) == 0 {
   109  			if err := daemon.Repositories().DeleteAll(img.ID); err != nil {
   110  				return err
   111  			}
   112  			if err := daemon.Graph().Delete(img.ID); err != nil {
   113  				return err
   114  			}
   115  			out := &engine.Env{}
   116  			out.SetJson("Deleted", img.ID)
   117  			imgs.Add(out)
   118  			eng.Job("log", "delete", img.ID, "").Run()
   119  			if img.Parent != "" && !noprune {
   120  				err := daemon.DeleteImage(eng, img.Parent, imgs, false, force, noprune)
   121  				if first {
   122  					return err
   123  				}
   124  
   125  			}
   126  
   127  		}
   128  	}
   129  	return nil
   130  }
   131  
   132  func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
   133  	for _, container := range daemon.List() {
   134  		parent, err := daemon.Repositories().LookupImage(container.ImageID)
   135  		if err != nil {
   136  			if daemon.Graph().IsNotExist(err) {
   137  				return nil
   138  			}
   139  			return err
   140  		}
   141  
   142  		if err := parent.WalkHistory(func(p *image.Image) error {
   143  			if imgID == p.ID {
   144  				if container.IsRunning() {
   145  					if force {
   146  						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))
   147  					}
   148  					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))
   149  				} else if !force {
   150  					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))
   151  				}
   152  			}
   153  			return nil
   154  		}); err != nil {
   155  			return err
   156  		}
   157  	}
   158  	return nil
   159  }