From 22e7e107addc4703ee6ef36981dfafe985be695d Mon Sep 17 00:00:00 2001 From: unclejack Date: Thu, 15 Aug 2013 21:48:05 +0300 Subject: [PATCH] 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. --- commands_test.go | 37 ++++++++++++++++++++++++ container.go | 16 ++++++++-- docs/sources/commandline/command/run.rst | 1 + runtime.go | 3 +- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/commands_test.go b/commands_test.go index dbd85be0c7..be5e80787c 100644 --- a/commands_test.go +++ b/commands_test.go @@ -542,3 +542,40 @@ func TestAttachDisconnect(t *testing.T) { cStdin.Close() 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) + } +} diff --git a/container.go b/container.go index 6bca3dfbf2..3dd0b4a1e0 100644 --- a/container.go +++ b/container.go @@ -90,6 +90,7 @@ type HostConfig struct { Binds []string ContainerIDFile string LxcConf []KeyValuePair + AutoRemove bool } 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") flNetwork := cmd.Bool("n", true, "Enable networking for 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 { //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 // add any bind targets to the list of container volumes @@ -242,6 +248,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Binds: binds, ContainerIDFile: *flContainerIDFile, LxcConf: lxcConf, + AutoRemove: *flAutoRemove, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -818,7 +825,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.ToDisk() container.SaveHostConfig(hostConfig) - go container.monitor() + go container.monitor(hostConfig) 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 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? //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 { diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst index d29e70dd50..41aa09d34e 100644 --- a/docs/sources/commandline/command/run.rst +++ b/docs/sources/commandline/command/run.rst @@ -23,6 +23,7 @@ -m=0: Memory limit (in bytes) -n=true: Enable networking for this 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 -u="": Username or UID -dns=[]: Set custom dns servers for the container diff --git a/runtime.go b/runtime.go index aff1773fdf..d77ecca722 100644 --- a/runtime.go +++ b/runtime.go @@ -174,7 +174,8 @@ func (runtime *Runtime) Register(container *Container) error { close(container.waitLock) } else if !nomonitor { container.allocateNetwork() - go container.monitor() + // hostConfig isn't needed here and can be nil + go container.monitor(nil) } return nil }