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 }