diff --git a/container.go b/container.go index 77fd7c0fec..d89f99e474 100644 --- a/container.go +++ b/container.go @@ -5,10 +5,12 @@ import ( "errors" "io" "io/ioutil" + "log" "os" "os/exec" "path" "syscall" + "time" ) type Container struct { @@ -176,22 +178,57 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { func (container *Container) monitor() { container.cmd.Wait() + exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() + + // Cleanup container container.stdout.Close() container.stderr.Close() - container.State.setStopped(container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()) + if err := container.Filesystem.Umount(); err != nil { + log.Printf("%v: Failed to umount filesystem: %v", container.Name, err) + } + + // Report status back + container.State.setStopped(exitCode) +} + +func (container *Container) kill() error { + // This will cause the main container process to receive a SIGKILL + if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Name).Run(); err != nil { + return err + } + + // Wait for the container to be actually stopped + if err := exec.Command("/usr/bin/lxc-wait", "-n", container.Name, "-s", "STOPPED").Run(); err != nil { + return err + } + return nil +} + +func (container *Container) Kill() error { + if !container.State.Running { + return nil + } + return container.kill() } func (container *Container) Stop() error { - if container.State.Running { - if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Name).Run(); err != nil { - return err - } - //FIXME: We should lxc-wait for the container to stop + if !container.State.Running { + return nil } - if err := container.Filesystem.Umount(); err != nil { - // FIXME: Do not abort, probably already umounted? - return nil + // 1. Send a SIGTERM + if err := exec.Command("/usr/bin/lxc-kill", "-n", container.Name, "15").Run(); err != nil { + return err + } + + // 2. Wait for the process to exit on its own + if err := container.WaitTimeout(10 * time.Second); err != nil { + log.Printf("Container %v failed to exit within 10 seconds of SIGTERM", container.Name) + } + + // 3. Force kill + if err := container.kill(); err != nil { + return err } return nil } @@ -201,3 +238,19 @@ func (container *Container) Wait() { container.State.wait() } } + +func (container *Container) WaitTimeout(timeout time.Duration) error { + done := make(chan bool) + go func() { + container.Wait() + done <- true + }() + + select { + case <-time.After(timeout): + return errors.New("Timed Out") + case <-done: + return nil + } + return nil +} diff --git a/container_test.go b/container_test.go index 650d2a07f9..e348a2b4b0 100644 --- a/container_test.go +++ b/container_test.go @@ -98,15 +98,15 @@ func TestOutput(t *testing.T) { } } -func TestStop(t *testing.T) { +func TestKill(t *testing.T) { docker, err := newTestDocker() if err != nil { t.Fatal(err) } container, err := docker.Create( "stop_test", - "sleep", - []string{"300"}, + "cat", + []string{"/dev/zero"}, []string{"/var/lib/docker/images/ubuntu"}, &Config{}, ) @@ -124,7 +124,7 @@ func TestStop(t *testing.T) { if !container.State.Running { t.Errorf("Container should be running") } - if err := container.Stop(); err != nil { + if err := container.Kill(); err != nil { t.Fatal(err) } if container.State.Running { @@ -135,7 +135,7 @@ func TestStop(t *testing.T) { t.Errorf("Container shouldn't be running") } // Try stopping twice - if err := container.Stop(); err != nil { + if err := container.Kill(); err != nil { t.Fatal(err) } }