Procházet zdrojové kódy

Merge pull request #3959 from cpuguy83/3958_add_ability_to_remove_running_container_in_single_command

Add ability to force removal of running container via docker rm -f
unclejack před 11 roky
rodič
revize
e388b6aba5

+ 4 - 0
api/client.go

@@ -890,6 +890,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
 	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
 	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
+	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
 
 	if err := cmd.Parse(args); err != nil {
 		return nil
@@ -905,6 +906,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	if *link {
 		val.Set("link", "1")
 	}
+	if *force {
+		val.Set("force", "1")
+	}
 
 	var encounteredError error
 	for _, name := range cmd.Args() {

+ 1 - 0
api/server.go

@@ -606,6 +606,7 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon
 	job := eng.Job("container_delete", vars["name"])
 	job.Setenv("removeVolume", r.Form.Get("v"))
 	job.Setenv("removeLink", r.Form.Get("link"))
+	job.Setenv("forceRemove", r.Form.Get("force"))
 	if err := job.Run(); err != nil {
 		return err
 	}

+ 5 - 0
docs/sources/reference/api/docker_remote_api.rst

@@ -51,6 +51,11 @@ What's new
    **New!** You can now use the force parameter to force delete of an image, even if it's
    tagged in multiple repositories.
 
+.. http:delete:: /containers/(id)
+
+  **New!** You can now use the force paramter to force delete a container, even if
+  it is currently running
+
 v1.9
 ****
 

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.10.rst

@@ -597,6 +597,7 @@ Remove a container
            HTTP/1.1 204 OK
 
         :query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false
+        :query force: 1/True/true or 0/False/false, Removes the container even if it was running. Default false
         :statuscode 204: no error
         :statuscode 400: bad parameter
         :statuscode 404: no such container

+ 2 - 1
docs/sources/reference/commandline/cli.rst

@@ -995,7 +995,8 @@ The last container is marked as a ``Ghost`` container. It is a container that wa
     Usage: docker rm [OPTIONS] CONTAINER
 
     Remove one or more containers
-        --link="": Remove the link instead of the actual container
+        -l, --link="": Remove the link instead of the actual container
+        -f, --force=false: Force removal of running container
 
 Known Issues (rm)
 ~~~~~~~~~~~~~~~~~

+ 62 - 0
integration/server_test.go

@@ -199,6 +199,68 @@ func TestCreateRmVolumes(t *testing.T) {
 	}
 }
 
+func TestCreateRmRunning(t *testing.T) {
+	eng := NewTestEngine(t)
+	defer mkRuntimeFromEngine(eng, t).Nuke()
+
+	config, hostConfig, _, err := runconfig.Parse([]string{"-name", "foo", unitTestImageID, "sleep 300"}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	id := createTestContainer(eng, config, t)
+
+	job := eng.Job("containers")
+	job.SetenvBool("all", true)
+	outs, err := job.Stdout.AddListTable()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(outs.Data) != 1 {
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
+	}
+
+	job = eng.Job("start", id)
+	if err := job.ImportEnv(hostConfig); err != nil {
+		t.Fatal(err)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test cannot remove running container
+	job = eng.Job("container_delete", id)
+	job.SetenvBool("forceRemove", false)
+	if err := job.Run(); err == nil {
+		t.Fatal("Expected container delete to fail")
+	}
+
+	// Test can force removal of running container
+	job = eng.Job("container_delete", id)
+	job.SetenvBool("forceRemove", true)
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+
+	job = eng.Job("containers")
+	job.SetenvBool("all", true)
+	outs, err = job.Stdout.AddListTable()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(outs.Data) != 0 {
+		t.Errorf("Expected 0 container, %v found", len(outs.Data))
+	}
+}
+
 func TestCommit(t *testing.T) {
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()

+ 8 - 1
server.go

@@ -1713,6 +1713,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
 	name := job.Args[0]
 	removeVolume := job.GetenvBool("removeVolume")
 	removeLink := job.GetenvBool("removeLink")
+	forceRemove := job.GetenvBool("forceRemove")
 
 	container := srv.runtime.Get(name)
 
@@ -1750,7 +1751,13 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
 
 	if container != nil {
 		if container.State.IsRunning() {
-			return job.Errorf("Impossible to remove a running container, please stop it first")
+			if forceRemove {
+				if err := container.Stop(5); err != nil {
+					return job.Errorf("Could not stop running container, cannot remove - %v", err)
+				}
+			} else {
+				return job.Errorf("Impossible to remove a running container, please stop it first or use -f")
+			}
 		}
 		if err := srv.runtime.Destroy(container); err != nil {
 			return job.Errorf("Cannot destroy container %s: %s", name, err)