github.com/erriapo/docker@v1.6.0-rc2/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 }