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.
| ... | ... |
@@ -542,3 +542,40 @@ func TestAttachDisconnect(t *testing.T) {
|
| 542 | 542 |
cStdin.Close() |
| 543 | 543 |
container.Wait() |
| 544 | 544 |
} |
| 545 |
+ |
|
| 546 |
+// Expected behaviour: container gets deleted automatically after exit |
|
| 547 |
+func TestRunAutoRemove(t *testing.T) {
|
|
| 548 |
+ stdout, stdoutPipe := io.Pipe() |
|
| 549 |
+ cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) |
|
| 550 |
+ defer cleanup(globalRuntime) |
|
| 551 |
+ |
|
| 552 |
+ c := make(chan struct{})
|
|
| 553 |
+ go func() {
|
|
| 554 |
+ defer close(c) |
|
| 555 |
+ if err := cli.CmdRun("-rm", unitTestImageID, "hostname"); err != nil {
|
|
| 556 |
+ t.Fatal(err) |
|
| 557 |
+ } |
|
| 558 |
+ }() |
|
| 559 |
+ |
|
| 560 |
+ var temporaryContainerID string |
|
| 561 |
+ setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
|
| 562 |
+ cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
|
| 563 |
+ if err != nil {
|
|
| 564 |
+ t.Fatal(err) |
|
| 565 |
+ } |
|
| 566 |
+ temporaryContainerID = cmdOutput |
|
| 567 |
+ if err := closeWrap(stdout, stdoutPipe); err != nil {
|
|
| 568 |
+ t.Fatal(err) |
|
| 569 |
+ } |
|
| 570 |
+ }) |
|
| 571 |
+ |
|
| 572 |
+ setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
|
| 573 |
+ <-c |
|
| 574 |
+ }) |
|
| 575 |
+ |
|
| 576 |
+ time.Sleep(500 * time.Millisecond) |
|
| 577 |
+ |
|
| 578 |
+ if len(globalRuntime.List()) > 0 {
|
|
| 579 |
+ t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
|
|
| 580 |
+ } |
|
| 581 |
+} |
| ... | ... |
@@ -90,6 +90,7 @@ type HostConfig struct {
|
| 90 | 90 |
Binds []string |
| 91 | 91 |
ContainerIDFile string |
| 92 | 92 |
LxcConf []KeyValuePair |
| 93 |
+ AutoRemove bool |
|
| 93 | 94 |
} |
| 94 | 95 |
|
| 95 | 96 |
type BindMap struct {
|
| ... | ... |
@@ -126,6 +127,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 126 | 126 |
flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file")
|
| 127 | 127 |
flNetwork := cmd.Bool("n", true, "Enable networking for this container")
|
| 128 | 128 |
flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
|
| 129 |
+ flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
|
|
| 129 | 130 |
|
| 130 | 131 |
if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
|
| 131 | 132 |
//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, |
| 174 | 174 |
} |
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 |
+ if *flDetach && *flAutoRemove {
|
|
| 178 |
+ return nil, nil, cmd, fmt.Errorf("Conflicting options: -rm and -d")
|
|
| 179 |
+ } |
|
| 180 |
+ |
|
| 177 | 181 |
var binds []string |
| 178 | 182 |
|
| 179 | 183 |
// add any bind targets to the list of container volumes |
| ... | ... |
@@ -242,6 +248,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 242 | 242 |
Binds: binds, |
| 243 | 243 |
ContainerIDFile: *flContainerIDFile, |
| 244 | 244 |
LxcConf: lxcConf, |
| 245 |
+ AutoRemove: *flAutoRemove, |
|
| 245 | 246 |
} |
| 246 | 247 |
|
| 247 | 248 |
if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
|
| ... | ... |
@@ -818,7 +825,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
| 818 | 818 |
|
| 819 | 819 |
container.ToDisk() |
| 820 | 820 |
container.SaveHostConfig(hostConfig) |
| 821 |
- go container.monitor() |
|
| 821 |
+ go container.monitor(hostConfig) |
|
| 822 | 822 |
return nil |
| 823 | 823 |
} |
| 824 | 824 |
|
| ... | ... |
@@ -948,7 +955,7 @@ func (container *Container) waitLxc() error {
|
| 948 | 948 |
} |
| 949 | 949 |
} |
| 950 | 950 |
|
| 951 |
-func (container *Container) monitor() {
|
|
| 951 |
+func (container *Container) monitor(hostConfig *HostConfig) {
|
|
| 952 | 952 |
// Wait for the program to exit |
| 953 | 953 |
utils.Debugf("Waiting for process")
|
| 954 | 954 |
|
| ... | ... |
@@ -1018,6 +1025,11 @@ func (container *Container) monitor() {
|
| 1018 | 1018 |
// FIXME: why are we serializing running state to disk in the first place? |
| 1019 | 1019 |
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
| 1020 | 1020 |
} |
| 1021 |
+ if hostConfig != nil {
|
|
| 1022 |
+ if hostConfig.AutoRemove {
|
|
| 1023 |
+ container.runtime.Destroy(container) |
|
| 1024 |
+ } |
|
| 1025 |
+ } |
|
| 1021 | 1026 |
} |
| 1022 | 1027 |
|
| 1023 | 1028 |
func (container *Container) kill() error {
|
| ... | ... |
@@ -23,6 +23,7 @@ |
| 23 | 23 |
-m=0: Memory limit (in bytes) |
| 24 | 24 |
-n=true: Enable networking for this container |
| 25 | 25 |
-p=[]: Map a network port to the container |
| 26 |
+ -rm=false: Automatically remove the container when it exits (incompatible with -d) |
|
| 26 | 27 |
-t=false: Allocate a pseudo-tty |
| 27 | 28 |
-u="": Username or UID |
| 28 | 29 |
-dns=[]: Set custom dns servers for the container |
| ... | ... |
@@ -174,7 +174,8 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 174 | 174 |
close(container.waitLock) |
| 175 | 175 |
} else if !nomonitor {
|
| 176 | 176 |
container.allocateNetwork() |
| 177 |
- go container.monitor() |
|
| 177 |
+ // hostConfig isn't needed here and can be nil |
|
| 178 |
+ go container.monitor(nil) |
|
| 178 | 179 |
} |
| 179 | 180 |
return nil |
| 180 | 181 |
} |