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