Add ability to force removal of running container via docker rm -f
| ... | ... |
@@ -890,6 +890,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
| 890 | 890 |
cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
|
| 891 | 891 |
v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
|
| 892 | 892 |
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
|
| 893 |
+ force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
|
|
| 893 | 894 |
|
| 894 | 895 |
if err := cmd.Parse(args); err != nil {
|
| 895 | 896 |
return nil |
| ... | ... |
@@ -905,6 +906,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
| 905 | 905 |
if *link {
|
| 906 | 906 |
val.Set("link", "1")
|
| 907 | 907 |
} |
| 908 |
+ if *force {
|
|
| 909 |
+ val.Set("force", "1")
|
|
| 910 |
+ } |
|
| 908 | 911 |
|
| 909 | 912 |
var encounteredError error |
| 910 | 913 |
for _, name := range cmd.Args() {
|
| ... | ... |
@@ -606,6 +606,7 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon |
| 606 | 606 |
job := eng.Job("container_delete", vars["name"])
|
| 607 | 607 |
job.Setenv("removeVolume", r.Form.Get("v"))
|
| 608 | 608 |
job.Setenv("removeLink", r.Form.Get("link"))
|
| 609 |
+ job.Setenv("forceRemove", r.Form.Get("force"))
|
|
| 609 | 610 |
if err := job.Run(); err != nil {
|
| 610 | 611 |
return err |
| 611 | 612 |
} |
| ... | ... |
@@ -51,6 +51,11 @@ What's new |
| 51 | 51 |
**New!** You can now use the force parameter to force delete of an image, even if it's |
| 52 | 52 |
tagged in multiple repositories. |
| 53 | 53 |
|
| 54 |
+.. http:delete:: /containers/(id) |
|
| 55 |
+ |
|
| 56 |
+ **New!** You can now use the force paramter to force delete a container, even if |
|
| 57 |
+ it is currently running |
|
| 58 |
+ |
|
| 54 | 59 |
v1.9 |
| 55 | 60 |
**** |
| 56 | 61 |
|
| ... | ... |
@@ -597,6 +597,7 @@ Remove a container |
| 597 | 597 |
HTTP/1.1 204 OK |
| 598 | 598 |
|
| 599 | 599 |
:query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false |
| 600 |
+ :query force: 1/True/true or 0/False/false, Removes the container even if it was running. Default false |
|
| 600 | 601 |
:statuscode 204: no error |
| 601 | 602 |
:statuscode 400: bad parameter |
| 602 | 603 |
:statuscode 404: no such container |
| ... | ... |
@@ -995,7 +995,8 @@ The last container is marked as a ``Ghost`` container. It is a container that wa |
| 995 | 995 |
Usage: docker rm [OPTIONS] CONTAINER |
| 996 | 996 |
|
| 997 | 997 |
Remove one or more containers |
| 998 |
- --link="": Remove the link instead of the actual container |
|
| 998 |
+ -l, --link="": Remove the link instead of the actual container |
|
| 999 |
+ -f, --force=false: Force removal of running container |
|
| 999 | 1000 |
|
| 1000 | 1001 |
Known Issues (rm) |
| 1001 | 1002 |
~~~~~~~~~~~~~~~~~ |
| ... | ... |
@@ -199,6 +199,68 @@ func TestCreateRmVolumes(t *testing.T) {
|
| 199 | 199 |
} |
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
+func TestCreateRmRunning(t *testing.T) {
|
|
| 203 |
+ eng := NewTestEngine(t) |
|
| 204 |
+ defer mkRuntimeFromEngine(eng, t).Nuke() |
|
| 205 |
+ |
|
| 206 |
+ config, hostConfig, _, err := runconfig.Parse([]string{"-name", "foo", unitTestImageID, "sleep 300"}, nil)
|
|
| 207 |
+ if err != nil {
|
|
| 208 |
+ t.Fatal(err) |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ id := createTestContainer(eng, config, t) |
|
| 212 |
+ |
|
| 213 |
+ job := eng.Job("containers")
|
|
| 214 |
+ job.SetenvBool("all", true)
|
|
| 215 |
+ outs, err := job.Stdout.AddListTable() |
|
| 216 |
+ if err != nil {
|
|
| 217 |
+ t.Fatal(err) |
|
| 218 |
+ } |
|
| 219 |
+ if err := job.Run(); err != nil {
|
|
| 220 |
+ t.Fatal(err) |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ if len(outs.Data) != 1 {
|
|
| 224 |
+ t.Errorf("Expected 1 container, %v found", len(outs.Data))
|
|
| 225 |
+ } |
|
| 226 |
+ |
|
| 227 |
+ job = eng.Job("start", id)
|
|
| 228 |
+ if err := job.ImportEnv(hostConfig); err != nil {
|
|
| 229 |
+ t.Fatal(err) |
|
| 230 |
+ } |
|
| 231 |
+ if err := job.Run(); err != nil {
|
|
| 232 |
+ t.Fatal(err) |
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 235 |
+ // Test cannot remove running container |
|
| 236 |
+ job = eng.Job("container_delete", id)
|
|
| 237 |
+ job.SetenvBool("forceRemove", false)
|
|
| 238 |
+ if err := job.Run(); err == nil {
|
|
| 239 |
+ t.Fatal("Expected container delete to fail")
|
|
| 240 |
+ } |
|
| 241 |
+ |
|
| 242 |
+ // Test can force removal of running container |
|
| 243 |
+ job = eng.Job("container_delete", id)
|
|
| 244 |
+ job.SetenvBool("forceRemove", true)
|
|
| 245 |
+ if err := job.Run(); err != nil {
|
|
| 246 |
+ t.Fatal(err) |
|
| 247 |
+ } |
|
| 248 |
+ |
|
| 249 |
+ job = eng.Job("containers")
|
|
| 250 |
+ job.SetenvBool("all", true)
|
|
| 251 |
+ outs, err = job.Stdout.AddListTable() |
|
| 252 |
+ if err != nil {
|
|
| 253 |
+ t.Fatal(err) |
|
| 254 |
+ } |
|
| 255 |
+ if err := job.Run(); err != nil {
|
|
| 256 |
+ t.Fatal(err) |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ if len(outs.Data) != 0 {
|
|
| 260 |
+ t.Errorf("Expected 0 container, %v found", len(outs.Data))
|
|
| 261 |
+ } |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 202 | 264 |
func TestCommit(t *testing.T) {
|
| 203 | 265 |
eng := NewTestEngine(t) |
| 204 | 266 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| ... | ... |
@@ -1713,6 +1713,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1713 | 1713 |
name := job.Args[0] |
| 1714 | 1714 |
removeVolume := job.GetenvBool("removeVolume")
|
| 1715 | 1715 |
removeLink := job.GetenvBool("removeLink")
|
| 1716 |
+ forceRemove := job.GetenvBool("forceRemove")
|
|
| 1716 | 1717 |
|
| 1717 | 1718 |
container := srv.runtime.Get(name) |
| 1718 | 1719 |
|
| ... | ... |
@@ -1750,7 +1751,13 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1750 | 1750 |
|
| 1751 | 1751 |
if container != nil {
|
| 1752 | 1752 |
if container.State.IsRunning() {
|
| 1753 |
- return job.Errorf("Impossible to remove a running container, please stop it first")
|
|
| 1753 |
+ if forceRemove {
|
|
| 1754 |
+ if err := container.Stop(5); err != nil {
|
|
| 1755 |
+ return job.Errorf("Could not stop running container, cannot remove - %v", err)
|
|
| 1756 |
+ } |
|
| 1757 |
+ } else {
|
|
| 1758 |
+ return job.Errorf("Impossible to remove a running container, please stop it first or use -f")
|
|
| 1759 |
+ } |
|
| 1754 | 1760 |
} |
| 1755 | 1761 |
if err := srv.runtime.Destroy(container); err != nil {
|
| 1756 | 1762 |
return job.Errorf("Cannot destroy container %s: %s", name, err)
|