docker rmi -f works with stopped containers + revamped error messages
| ... | ... |
@@ -24,19 +24,6 @@ package server |
| 24 | 24 |
import ( |
| 25 | 25 |
"encoding/json" |
| 26 | 26 |
"fmt" |
| 27 |
- "github.com/dotcloud/docker/api" |
|
| 28 |
- "github.com/dotcloud/docker/archive" |
|
| 29 |
- "github.com/dotcloud/docker/daemon" |
|
| 30 |
- "github.com/dotcloud/docker/daemonconfig" |
|
| 31 |
- "github.com/dotcloud/docker/dockerversion" |
|
| 32 |
- "github.com/dotcloud/docker/engine" |
|
| 33 |
- "github.com/dotcloud/docker/graph" |
|
| 34 |
- "github.com/dotcloud/docker/image" |
|
| 35 |
- "github.com/dotcloud/docker/pkg/graphdb" |
|
| 36 |
- "github.com/dotcloud/docker/pkg/signal" |
|
| 37 |
- "github.com/dotcloud/docker/registry" |
|
| 38 |
- "github.com/dotcloud/docker/runconfig" |
|
| 39 |
- "github.com/dotcloud/docker/utils" |
|
| 40 | 27 |
"io" |
| 41 | 28 |
"io/ioutil" |
| 42 | 29 |
"log" |
| ... | ... |
@@ -53,6 +40,20 @@ import ( |
| 53 | 53 |
"sync" |
| 54 | 54 |
"syscall" |
| 55 | 55 |
"time" |
| 56 |
+ |
|
| 57 |
+ "github.com/dotcloud/docker/api" |
|
| 58 |
+ "github.com/dotcloud/docker/archive" |
|
| 59 |
+ "github.com/dotcloud/docker/daemon" |
|
| 60 |
+ "github.com/dotcloud/docker/daemonconfig" |
|
| 61 |
+ "github.com/dotcloud/docker/dockerversion" |
|
| 62 |
+ "github.com/dotcloud/docker/engine" |
|
| 63 |
+ "github.com/dotcloud/docker/graph" |
|
| 64 |
+ "github.com/dotcloud/docker/image" |
|
| 65 |
+ "github.com/dotcloud/docker/pkg/graphdb" |
|
| 66 |
+ "github.com/dotcloud/docker/pkg/signal" |
|
| 67 |
+ "github.com/dotcloud/docker/registry" |
|
| 68 |
+ "github.com/dotcloud/docker/runconfig" |
|
| 69 |
+ "github.com/dotcloud/docker/utils" |
|
| 56 | 70 |
) |
| 57 | 71 |
|
| 58 | 72 |
// jobInitApi runs the remote api server `srv` as a daemon, |
| ... | ... |
@@ -1953,6 +1954,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, no |
| 1953 | 1953 |
var ( |
| 1954 | 1954 |
repoName, tag string |
| 1955 | 1955 |
tags = []string{}
|
| 1956 |
+ tagDeleted bool |
|
| 1956 | 1957 |
) |
| 1957 | 1958 |
|
| 1958 | 1959 |
repoName, tag = utils.ParseRepositoryTag(name) |
| ... | ... |
@@ -2003,7 +2005,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, no |
| 2003 | 2003 |
|
| 2004 | 2004 |
//Untag the current image |
| 2005 | 2005 |
for _, tag := range tags {
|
| 2006 |
- tagDeleted, err := srv.daemon.Repositories().Delete(repoName, tag) |
|
| 2006 |
+ tagDeleted, err = srv.daemon.Repositories().Delete(repoName, tag) |
|
| 2007 | 2007 |
if err != nil {
|
| 2008 | 2008 |
return err |
| 2009 | 2009 |
} |
| ... | ... |
@@ -2017,7 +2019,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, no |
| 2017 | 2017 |
tags = srv.daemon.Repositories().ByID()[img.ID] |
| 2018 | 2018 |
if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
|
| 2019 | 2019 |
if len(byParents[img.ID]) == 0 {
|
| 2020 |
- if err := srv.canDeleteImage(img.ID); err != nil {
|
|
| 2020 |
+ if err := srv.canDeleteImage(img.ID, force, tagDeleted); err != nil {
|
|
| 2021 | 2021 |
return err |
| 2022 | 2022 |
} |
| 2023 | 2023 |
if err := srv.daemon.Repositories().DeleteAll(img.ID); err != nil {
|
| ... | ... |
@@ -2060,7 +2062,11 @@ func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
| 2060 | 2060 |
return engine.StatusOK |
| 2061 | 2061 |
} |
| 2062 | 2062 |
|
| 2063 |
-func (srv *Server) canDeleteImage(imgID string) error {
|
|
| 2063 |
+func (srv *Server) canDeleteImage(imgID string, force, untagged bool) error {
|
|
| 2064 |
+ var message string |
|
| 2065 |
+ if untagged {
|
|
| 2066 |
+ message = " (docker untagged the image)" |
|
| 2067 |
+ } |
|
| 2064 | 2068 |
for _, container := range srv.daemon.List() {
|
| 2065 | 2069 |
parent, err := srv.daemon.Repositories().LookupImage(container.Image) |
| 2066 | 2070 |
if err != nil {
|
| ... | ... |
@@ -2069,7 +2075,14 @@ func (srv *Server) canDeleteImage(imgID string) error {
|
| 2069 | 2069 |
|
| 2070 | 2070 |
if err := parent.WalkHistory(func(p *image.Image) error {
|
| 2071 | 2071 |
if imgID == p.ID {
|
| 2072 |
- return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it", utils.TruncateID(imgID), utils.TruncateID(container.ID))
|
|
| 2072 |
+ if container.State.IsRunning() {
|
|
| 2073 |
+ if force {
|
|
| 2074 |
+ return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it%s, stop it and retry", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
| 2075 |
+ } |
|
| 2076 |
+ return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it%s, stop it and use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
| 2077 |
+ } else if !force {
|
|
| 2078 |
+ return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it%s, use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
| 2079 |
+ } |
|
| 2073 | 2080 |
} |
| 2074 | 2081 |
return nil |
| 2075 | 2082 |
}); err != nil {
|