From 9c90d3e5208c972da0351382d1b83376728ce2b3 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 6 May 2015 10:18:01 -0400 Subject: [PATCH] Fix LXC stop signals `lxc-stop` does not support sending arbitrary signals. By default, `lxc-stop -n ` would send `SIGPWR`. The lxc driver was always sending `lxc-stop -n -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 Upstream-commit: d2c4ee37c6a4114b33a915b7dae6de70e27e7965 Component: engine --- .../engine/daemon/execdriver/lxc/driver.go | 15 +++++++--- .../integration-cli/docker_cli_wait_test.go | 28 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/components/engine/daemon/execdriver/lxc/driver.go b/components/engine/daemon/execdriver/lxc/driver.go index 32e5b43ebc..49db608743 100644 --- a/components/engine/daemon/execdriver/lxc/driver.go +++ b/components/engine/daemon/execdriver/lxc/driver.go @@ -127,6 +127,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba "lxc-start", "-n", c.ID, "-f", configPath, + "-q", } // 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 oomKillNotification, err := notifyOnOOM(cgroupPaths) <-waitLock + exitCode := getExitCode(c) if err == nil { _, oomKill = <-oomKillNotification - logrus.Debugf("oomKill error %s waitErr %s", oomKill, waitErr) + logrus.Debugf("oomKill error: %v, waitErr: %v", oomKill, waitErr) } else { logrus.Warnf("Your kernel does not support OOM notifications: %s", err) } // check oom error - exitCode := getExitCode(c) if oomKill { exitCode = 137 } + return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr } @@ -468,7 +470,11 @@ func getExitCode(c *execdriver.Command) int { } func (d *driver) Kill(c *execdriver.Command, sig int) error { - return KillLxc(c.ID, sig) + if sig == 9 || c.ProcessConfig.Process == nil { + return KillLxc(c.ID, sig) + } + + return c.ProcessConfig.Process.Signal(syscall.Signal(sig)) } func (d *driver) Pause(c *execdriver.Command) error { @@ -528,7 +534,8 @@ func KillLxc(id string, sig int) error { if err == nil { output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput() } else { - output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput() + // lxc-stop does not take arbitrary signals like lxc-kill does + output, err = exec.Command("lxc-stop", "-k", "-n", id).CombinedOutput() } if err != nil { return fmt.Errorf("Err: %s Output: %s", err, output) diff --git a/components/engine/integration-cli/docker_cli_wait_test.go b/components/engine/integration-cli/docker_cli_wait_test.go index 21f04faf0f..b7fb3fe958 100644 --- a/components/engine/integration-cli/docker_cli_wait_test.go +++ b/components/engine/integration-cli/docker_cli_wait_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "os/exec" "strings" "time" @@ -44,7 +45,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitZero(c *check.C) { // blocking wait with 0 exit code func (s *DockerSuite) TestWaitBlockedExitZero(c *check.C) { - out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' SIGTERM; while true; do sleep 0.01; done") + out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' TERM; while true; do sleep 0.01; done") containerID := strings.TrimSpace(out) if err := waitRun(containerID); err != nil { @@ -107,7 +108,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitRandom(c *check.C) { // blocking wait with random exit code func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) { - out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "trap 'exit 99' SIGTERM; while true; do sleep 0.01; done") + out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 99' TERM; while true; do sleep 0.01; done") containerID := strings.TrimSpace(out) if err := waitRun(containerID); err != nil { c.Fatal(err) @@ -116,21 +117,34 @@ func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) { c.Fatal(err) } - chWait := make(chan string) + chWait := make(chan error) + waitCmd := exec.Command(dockerBinary, "wait", containerID) + waitCmdOut := bytes.NewBuffer(nil) + waitCmd.Stdout = waitCmdOut + if err := waitCmd.Start(); err != nil { + c.Fatal(err) + } + go func() { - out, _, _ := runCommandWithOutput(exec.Command(dockerBinary, "wait", containerID)) - chWait <- out + chWait <- waitCmd.Wait() }() - time.Sleep(100 * time.Millisecond) dockerCmd(c, "stop", containerID) select { - case status := <-chWait: + case err := <-chWait: + if err != nil { + c.Fatal(err) + } + status, err := waitCmdOut.ReadString('\n') + if err != nil { + c.Fatal(err) + } if strings.TrimSpace(status) != "99" { c.Fatalf("expected exit 99, got %s", status) } case <-time.After(2 * time.Second): + waitCmd.Process.Kill() c.Fatal("timeout waiting for `docker wait` to exit") } }