Quellcode durchsuchen

Docker: Clean shutdown of containers.
Stop() will send a SIGTERM to the init process of the container and give it 10 seconds to shutdown properly.
After the timeout is reached, the process will be force killed (SIGKILL).
Also available is Kill() which kills the process on the spot.

Andrea Luzzardi vor 12 Jahren
Ursprung
Commit
24dac22892
2 geänderte Dateien mit 67 neuen und 14 gelöschten Zeilen
  1. 62 9
      container.go
  2. 5 5
      container_test.go

+ 62 - 9
container.go

@@ -5,10 +5,12 @@ import (
 	"errors"
 	"errors"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
+	"log"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path"
 	"path"
 	"syscall"
 	"syscall"
+	"time"
 )
 )
 
 
 type Container struct {
 type Container struct {
@@ -176,23 +178,58 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
 
 
 func (container *Container) monitor() {
 func (container *Container) monitor() {
 	container.cmd.Wait()
 	container.cmd.Wait()
+	exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
+
+	// Cleanup container
 	container.stdout.Close()
 	container.stdout.Close()
 	container.stderr.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) 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
+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
 	}
 	}
 
 
-	if err := container.Filesystem.Umount(); err != nil {
-		// FIXME: Do not abort, probably already umounted?
+	// 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 {
 		return nil
 		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
 	return nil
 }
 }
 
 
@@ -201,3 +238,19 @@ func (container *Container) Wait() {
 		container.State.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
+}

+ 5 - 5
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()
 	docker, err := newTestDocker()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	container, err := docker.Create(
 	container, err := docker.Create(
 		"stop_test",
 		"stop_test",
-		"sleep",
-		[]string{"300"},
+		"cat",
+		[]string{"/dev/zero"},
 		[]string{"/var/lib/docker/images/ubuntu"},
 		[]string{"/var/lib/docker/images/ubuntu"},
 		&Config{},
 		&Config{},
 	)
 	)
@@ -124,7 +124,7 @@ func TestStop(t *testing.T) {
 	if !container.State.Running {
 	if !container.State.Running {
 		t.Errorf("Container should be running")
 		t.Errorf("Container should be running")
 	}
 	}
-	if err := container.Stop(); err != nil {
+	if err := container.Kill(); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if container.State.Running {
 	if container.State.Running {
@@ -135,7 +135,7 @@ func TestStop(t *testing.T) {
 		t.Errorf("Container shouldn't be running")
 		t.Errorf("Container shouldn't be running")
 	}
 	}
 	// Try stopping twice
 	// Try stopping twice
-	if err := container.Stop(); err != nil {
+	if err := container.Kill(); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 }
 }