Browse Source

automatically remove container via -rm

add AutoRemove to HostConfig
add -rm flag to docker run
add TestRunAutoRemove to test -rm
docs: add -rm to commandline/command/run
add hostConfig to container monitor
make monitor destroy the container via -rm

This adds support for automatically removing a container after it
exits. The removal of the container is handled on the server side.
unclejack 12 years ago
parent
commit
22e7e107ad
4 changed files with 54 additions and 3 deletions
  1. 37 0
      commands_test.go
  2. 14 2
      container.go
  3. 1 0
      docs/sources/commandline/command/run.rst
  4. 2 1
      runtime.go

+ 37 - 0
commands_test.go

@@ -542,3 +542,40 @@ func TestAttachDisconnect(t *testing.T) {
 	cStdin.Close()
 	cStdin.Close()
 	container.Wait()
 	container.Wait()
 }
 }
+
+// Expected behaviour: container gets deleted automatically after exit
+func TestRunAutoRemove(t *testing.T) {
+	stdout, stdoutPipe := io.Pipe()
+	cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
+	defer cleanup(globalRuntime)
+
+	c := make(chan struct{})
+	go func() {
+		defer close(c)
+		if err := cli.CmdRun("-rm", unitTestImageID, "hostname"); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	var temporaryContainerID string
+	setTimeout(t, "Reading command output time out", 2*time.Second, func() {
+		cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
+		if err != nil {
+			t.Fatal(err)
+		}
+		temporaryContainerID = cmdOutput
+		if err := closeWrap(stdout, stdoutPipe); err != nil {
+			t.Fatal(err)
+		}
+	})
+
+	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
+		<-c
+	})
+
+	time.Sleep(500 * time.Millisecond)
+
+	if len(globalRuntime.List()) > 0 {
+		t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
+	}
+}

+ 14 - 2
container.go

@@ -90,6 +90,7 @@ type HostConfig struct {
 	Binds           []string
 	Binds           []string
 	ContainerIDFile string
 	ContainerIDFile string
 	LxcConf         []KeyValuePair
 	LxcConf         []KeyValuePair
+	AutoRemove      bool
 }
 }
 
 
 type BindMap struct {
 type BindMap struct {
@@ -126,6 +127,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file")
 	flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file")
 	flNetwork := cmd.Bool("n", true, "Enable networking for this container")
 	flNetwork := cmd.Bool("n", true, "Enable networking for this container")
 	flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
 	flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
+	flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
 
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
@@ -174,6 +176,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		}
 		}
 	}
 	}
 
 
+	if *flDetach && *flAutoRemove {
+		return nil, nil, cmd, fmt.Errorf("Conflicting options: -rm and -d")
+	}
+
 	var binds []string
 	var binds []string
 
 
 	// add any bind targets to the list of container volumes
 	// add any bind targets to the list of container volumes
@@ -242,6 +248,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		Binds:           binds,
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
 		ContainerIDFile: *flContainerIDFile,
 		LxcConf:         lxcConf,
 		LxcConf:         lxcConf,
+		AutoRemove:      *flAutoRemove,
 	}
 	}
 
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
@@ -818,7 +825,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
 
 
 	container.ToDisk()
 	container.ToDisk()
 	container.SaveHostConfig(hostConfig)
 	container.SaveHostConfig(hostConfig)
-	go container.monitor()
+	go container.monitor(hostConfig)
 	return nil
 	return nil
 }
 }
 
 
@@ -948,7 +955,7 @@ func (container *Container) waitLxc() error {
 	}
 	}
 }
 }
 
 
-func (container *Container) monitor() {
+func (container *Container) monitor(hostConfig *HostConfig) {
 	// Wait for the program to exit
 	// Wait for the program to exit
 	utils.Debugf("Waiting for process")
 	utils.Debugf("Waiting for process")
 
 
@@ -1018,6 +1025,11 @@ func (container *Container) monitor() {
 		// FIXME: why are we serializing running state to disk in the first place?
 		// FIXME: why are we serializing running state to disk in the first place?
 		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
 		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
 	}
 	}
+	if hostConfig != nil {
+		if hostConfig.AutoRemove {
+			container.runtime.Destroy(container)
+		}
+	}
 }
 }
 
 
 func (container *Container) kill() error {
 func (container *Container) kill() error {

+ 1 - 0
docs/sources/commandline/command/run.rst

@@ -23,6 +23,7 @@
       -m=0: Memory limit (in bytes)
       -m=0: Memory limit (in bytes)
       -n=true: Enable networking for this container
       -n=true: Enable networking for this container
       -p=[]: Map a network port to the container
       -p=[]: Map a network port to the container
+      -rm=false: Automatically remove the container when it exits (incompatible with -d)
       -t=false: Allocate a pseudo-tty
       -t=false: Allocate a pseudo-tty
       -u="": Username or UID
       -u="": Username or UID
       -dns=[]: Set custom dns servers for the container
       -dns=[]: Set custom dns servers for the container

+ 2 - 1
runtime.go

@@ -174,7 +174,8 @@ func (runtime *Runtime) Register(container *Container) error {
 		close(container.waitLock)
 		close(container.waitLock)
 	} else if !nomonitor {
 	} else if !nomonitor {
 		container.allocateNetwork()
 		container.allocateNetwork()
-		go container.monitor()
+		// hostConfig isn't needed here and can be nil
+		go container.monitor(nil)
 	}
 	}
 	return nil
 	return nil
 }
 }