Browse code

Fix LXC stop signals

`lxc-stop` does not support sending arbitrary signals.
By default, `lxc-stop -n <id>` would send `SIGPWR`.
The lxc driver was always sending `lxc-stop -n <id> -k`, which always
sends `SIGKILL`. In this case `lxc-start` returns an exit code of `0`,
regardless of what the container actually exited with.
Because of this we must send signals directly to the process when we
can.

Also need to set quiet mode on `lxc-start` otherwise it reports an error
on `stderr` when the container exits cleanly (ie, we didn't SIGKILL it),
this error is picked up in the container logs... and isn't really an
error.

Also cleaned up some potential races for waitblocked test.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2015/05/06 23:18:01
Showing 2 changed files
... ...
@@ -127,6 +127,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
127 127
 		"lxc-start",
128 128
 		"-n", c.ID,
129 129
 		"-f", configPath,
130
+		"-q",
130 131
 	}
131 132
 
132 133
 	// From lxc>=1.1 the default behavior is to daemonize containers after start
... ...
@@ -278,19 +279,20 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
278 278
 	oomKillNotification, err := notifyOnOOM(cgroupPaths)
279 279
 
280 280
 	<-waitLock
281
+	exitCode := getExitCode(c)
281 282
 
282 283
 	if err == nil {
283 284
 		_, oomKill = <-oomKillNotification
284
-		logrus.Debugf("oomKill error %s waitErr %s", oomKill, waitErr)
285
+		logrus.Debugf("oomKill error: %v, waitErr: %v", oomKill, waitErr)
285 286
 	} else {
286 287
 		logrus.Warnf("Your kernel does not support OOM notifications: %s", err)
287 288
 	}
288 289
 
289 290
 	// check oom error
290
-	exitCode := getExitCode(c)
291 291
 	if oomKill {
292 292
 		exitCode = 137
293 293
 	}
294
+
294 295
 	return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr
295 296
 }
296 297
 
... ...
@@ -468,7 +470,11 @@ func getExitCode(c *execdriver.Command) int {
468 468
 }
469 469
 
470 470
 func (d *driver) Kill(c *execdriver.Command, sig int) error {
471
-	return KillLxc(c.ID, sig)
471
+	if sig == 9 || c.ProcessConfig.Process == nil {
472
+		return KillLxc(c.ID, sig)
473
+	}
474
+
475
+	return c.ProcessConfig.Process.Signal(syscall.Signal(sig))
472 476
 }
473 477
 
474 478
 func (d *driver) Pause(c *execdriver.Command) error {
... ...
@@ -528,7 +534,8 @@ func KillLxc(id string, sig int) error {
528 528
 	if err == nil {
529 529
 		output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
530 530
 	} else {
531
-		output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
531
+		// lxc-stop does not take arbitrary signals like lxc-kill does
532
+		output, err = exec.Command("lxc-stop", "-k", "-n", id).CombinedOutput()
532 533
 	}
533 534
 	if err != nil {
534 535
 		return fmt.Errorf("Err: %s Output: %s", err, output)
... ...
@@ -1,6 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"bytes"
4 5
 	"os/exec"
5 6
 	"strings"
6 7
 	"time"
... ...
@@ -44,7 +45,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitZero(c *check.C) {
44 44
 
45 45
 // blocking wait with 0 exit code
46 46
 func (s *DockerSuite) TestWaitBlockedExitZero(c *check.C) {
47
-	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' SIGTERM; while true; do sleep 0.01; done")
47
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' TERM; while true; do sleep 0.01; done")
48 48
 	containerID := strings.TrimSpace(out)
49 49
 
50 50
 	if err := waitRun(containerID); err != nil {
... ...
@@ -107,7 +108,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitRandom(c *check.C) {
107 107
 
108 108
 // blocking wait with random exit code
109 109
 func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) {
110
-	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "trap 'exit 99' SIGTERM; while true; do sleep 0.01; done")
110
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 99' TERM; while true; do sleep 0.01; done")
111 111
 	containerID := strings.TrimSpace(out)
112 112
 	if err := waitRun(containerID); err != nil {
113 113
 		c.Fatal(err)
... ...
@@ -116,21 +117,34 @@ func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) {
116 116
 		c.Fatal(err)
117 117
 	}
118 118
 
119
-	chWait := make(chan string)
119
+	chWait := make(chan error)
120
+	waitCmd := exec.Command(dockerBinary, "wait", containerID)
121
+	waitCmdOut := bytes.NewBuffer(nil)
122
+	waitCmd.Stdout = waitCmdOut
123
+	if err := waitCmd.Start(); err != nil {
124
+		c.Fatal(err)
125
+	}
126
+
120 127
 	go func() {
121
-		out, _, _ := runCommandWithOutput(exec.Command(dockerBinary, "wait", containerID))
122
-		chWait <- out
128
+		chWait <- waitCmd.Wait()
123 129
 	}()
124 130
 
125
-	time.Sleep(100 * time.Millisecond)
126 131
 	dockerCmd(c, "stop", containerID)
127 132
 
128 133
 	select {
129
-	case status := <-chWait:
134
+	case err := <-chWait:
135
+		if err != nil {
136
+			c.Fatal(err)
137
+		}
138
+		status, err := waitCmdOut.ReadString('\n')
139
+		if err != nil {
140
+			c.Fatal(err)
141
+		}
130 142
 		if strings.TrimSpace(status) != "99" {
131 143
 			c.Fatalf("expected exit 99, got %s", status)
132 144
 		}
133 145
 	case <-time.After(2 * time.Second):
146
+		waitCmd.Process.Kill()
134 147
 		c.Fatal("timeout waiting for `docker wait` to exit")
135 148
 	}
136 149
 }