github.com/jogo/docker@v1.7.0-rc1/daemon/image_delete.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/docker/docker/api/types" 8 "github.com/docker/docker/graph" 9 "github.com/docker/docker/image" 10 "github.com/docker/docker/pkg/parsers" 11 "github.com/docker/docker/pkg/stringid" 12 "github.com/docker/docker/utils" 13 ) 14 15 // FIXME: remove ImageDelete's dependency on Daemon, then move to graph/ 16 func (daemon *Daemon) ImageDelete(name string, force, noprune bool) ([]types.ImageDelete, error) { 17 list := []types.ImageDelete{} 18 if err := daemon.imgDeleteHelper(name, &list, true, force, noprune); err != nil { 19 return nil, err 20 } 21 if len(list) == 0 { 22 return nil, fmt.Errorf("Conflict, %s wasn't deleted", name) 23 } 24 25 return list, nil 26 } 27 28 func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, first, force, noprune bool) error { 29 var ( 30 repoName, tag string 31 tags = []string{} 32 ) 33 repoAndTags := make(map[string][]string) 34 35 // FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes 36 repoName, tag = parsers.ParseRepositoryTag(name) 37 if tag == "" { 38 tag = graph.DEFAULTTAG 39 } 40 41 if name == "" { 42 return fmt.Errorf("Image name can not be blank") 43 } 44 45 img, err := daemon.Repositories().LookupImage(name) 46 if err != nil { 47 if r, _ := daemon.Repositories().Get(repoName); r != nil { 48 return fmt.Errorf("No such image: %s", utils.ImageReference(repoName, tag)) 49 } 50 return fmt.Errorf("No such image: %s", name) 51 } 52 53 if strings.Contains(img.ID, name) { 54 repoName = "" 55 tag = "" 56 } 57 58 byParents, err := daemon.Graph().ByParent() 59 if err != nil { 60 return err 61 } 62 63 repos := daemon.Repositories().ByID()[img.ID] 64 65 //If delete by id, see if the id belong only to one repository 66 if repoName == "" { 67 for _, repoAndTag := range repos { 68 parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag) 69 if repoName == "" || repoName == parsedRepo { 70 repoName = parsedRepo 71 if parsedTag != "" { 72 repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag) 73 } 74 } else if repoName != parsedRepo && !force && first { 75 // the id belongs to multiple repos, like base:latest and user:test, 76 // in that case return conflict 77 return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name) 78 } else { 79 //the id belongs to multiple repos, with -f just delete all 80 repoName = parsedRepo 81 if parsedTag != "" { 82 repoAndTags[repoName] = append(repoAndTags[repoName], parsedTag) 83 } 84 } 85 } 86 } else { 87 repoAndTags[repoName] = append(repoAndTags[repoName], tag) 88 } 89 90 if !first && len(repoAndTags) > 0 { 91 return nil 92 } 93 94 if len(repos) <= 1 { 95 if err := daemon.canDeleteImage(img.ID, force); err != nil { 96 return err 97 } 98 } 99 100 // Untag the current image 101 for repoName, tags := range repoAndTags { 102 for _, tag := range tags { 103 tagDeleted, err := daemon.Repositories().Delete(repoName, tag) 104 if err != nil { 105 return err 106 } 107 if tagDeleted { 108 *list = append(*list, types.ImageDelete{ 109 Untagged: utils.ImageReference(repoName, tag), 110 }) 111 daemon.EventsService.Log("untag", img.ID, "") 112 } 113 } 114 } 115 tags = daemon.Repositories().ByID()[img.ID] 116 if (len(tags) <= 1 && repoName == "") || len(tags) == 0 { 117 if len(byParents[img.ID]) == 0 { 118 if err := daemon.Repositories().DeleteAll(img.ID); err != nil { 119 return err 120 } 121 if err := daemon.Graph().Delete(img.ID); err != nil { 122 return err 123 } 124 *list = append(*list, types.ImageDelete{ 125 Deleted: img.ID, 126 }) 127 daemon.EventsService.Log("delete", img.ID, "") 128 if img.Parent != "" && !noprune { 129 err := daemon.imgDeleteHelper(img.Parent, list, false, force, noprune) 130 if first { 131 return err 132 } 133 134 } 135 136 } 137 } 138 return nil 139 } 140 141 func (daemon *Daemon) canDeleteImage(imgID string, force bool) error { 142 for _, container := range daemon.List() { 143 parent, err := daemon.Repositories().LookupImage(container.ImageID) 144 if err != nil { 145 if daemon.Graph().IsNotExist(err, container.ImageID) { 146 return nil 147 } 148 return err 149 } 150 151 if err := parent.WalkHistory(func(p *image.Image) error { 152 if imgID == p.ID { 153 if container.IsRunning() { 154 if force { 155 return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it, stop it and retry", stringid.TruncateID(imgID), stringid.TruncateID(container.ID)) 156 } 157 return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it, stop it and use -f to force", stringid.TruncateID(imgID), stringid.TruncateID(container.ID)) 158 } else if !force { 159 return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it, use -f to force", stringid.TruncateID(imgID), stringid.TruncateID(container.ID)) 160 } 161 } 162 return nil 163 }); err != nil { 164 return err 165 } 166 } 167 return nil 168 }