summaryrefslogtreecommitdiffhomepage
path: root/runsc/test
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/test')
-rw-r--r--runsc/test/integration/exec_test.go2
-rw-r--r--runsc/test/integration/integration_test.go48
-rw-r--r--runsc/test/testutil/docker.go39
3 files changed, 88 insertions, 1 deletions
diff --git a/runsc/test/integration/exec_test.go b/runsc/test/integration/exec_test.go
index d08140ad3..3cac674d0 100644
--- a/runsc/test/integration/exec_test.go
+++ b/runsc/test/integration/exec_test.go
@@ -66,7 +66,7 @@ func TestExecJobControl(t *testing.T) {
if err := testutil.Pull("alpine"); err != nil {
t.Fatalf("docker pull failed: %v", err)
}
- d := testutil.MakeDocker("exec-test")
+ d := testutil.MakeDocker("exec-job-control-test")
// Start the container.
if err := d.Run("alpine", "sleep", "1000"); err != nil {
diff --git a/runsc/test/integration/integration_test.go b/runsc/test/integration/integration_test.go
index b7d07309d..536bb17e0 100644
--- a/runsc/test/integration/integration_test.go
+++ b/runsc/test/integration/integration_test.go
@@ -28,6 +28,7 @@ import (
"os"
"strconv"
"strings"
+ "syscall"
"testing"
"time"
@@ -231,6 +232,53 @@ func TestNumCPU(t *testing.T) {
}
}
+// TestJobControl tests that job control characters are handled properly.
+func TestJobControl(t *testing.T) {
+ if err := testutil.Pull("alpine"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := testutil.MakeDocker("job-control-test")
+
+ // Start the container with an attached PTY.
+ _, ptmx, err := d.RunWithPty("alpine", "sh")
+ if err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer ptmx.Close()
+ defer d.CleanUp()
+
+ // Call "sleep 100" in the shell.
+ if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // Give shell a few seconds to start executing the sleep.
+ time.Sleep(2 * time.Second)
+
+ // Send a ^C to the pty, which should kill sleep, but not the shell.
+ // \x03 is ASCII "end of text", which is the same as ^C.
+ if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // The shell should still be alive at this point. Sleep should have
+ // exited with code 2+128=130. We'll exit with 10 plus that number, so
+ // that we can be sure that the shell did not get signalled.
+ if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // Wait for the container to exit.
+ got, err := d.Wait(5 * time.Second)
+ if err != nil {
+ t.Fatalf("error getting exit code: %v", err)
+ }
+ // Container should exit with code 10+130=140.
+ if want := syscall.WaitStatus(140); got != want {
+ t.Errorf("container exited with code %d want %d", got, want)
+ }
+}
+
func TestMain(m *testing.M) {
testutil.EnsureSupportedDockerVersion()
os.Exit(m.Run())
diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go
index 8a51d3eed..4e48817cf 100644
--- a/runsc/test/testutil/docker.go
+++ b/runsc/test/testutil/docker.go
@@ -25,6 +25,7 @@ import (
"regexp"
"strconv"
"strings"
+ "syscall"
"time"
"github.com/kr/pty"
@@ -198,6 +199,13 @@ func (d *Docker) Run(args ...string) error {
return err
}
+// RunWithPty is like Run but with an attached pty.
+func (d *Docker) RunWithPty(args ...string) (*exec.Cmd, *os.File, error) {
+ a := []string{"run", "--runtime", d.Runtime, "--name", d.Name, "-it"}
+ a = append(a, args...)
+ return doWithPty(a...)
+}
+
// RunFg calls 'docker run' with the arguments provided in the foreground. It
// blocks until the container exits and returns the output.
func (d *Docker) RunFg(args ...string) (string, error) {
@@ -307,6 +315,37 @@ func (d *Docker) ID() (string, error) {
return strings.TrimSpace(string(out)), nil
}
+// Wait waits for container to exit, up to the given timeout. Returns error if
+// wait fails or timeout is hit. Returns the application return code otherwise.
+// Note that the application may have failed even if err == nil, always check
+// the exit code.
+func (d *Docker) Wait(timeout time.Duration) (syscall.WaitStatus, error) {
+ timeoutChan := time.After(timeout)
+ waitChan := make(chan (syscall.WaitStatus))
+ errChan := make(chan (error))
+
+ go func() {
+ out, err := do("wait", d.Name)
+ if err != nil {
+ errChan <- fmt.Errorf("error waiting for container %q: %v", d.Name, err)
+ }
+ exit, err := strconv.Atoi(strings.TrimSuffix(string(out), "\n"))
+ if err != nil {
+ errChan <- fmt.Errorf("error parsing exit code %q: %v", out, err)
+ }
+ waitChan <- syscall.WaitStatus(uint32(exit))
+ }()
+
+ select {
+ case ws := <-waitChan:
+ return ws, nil
+ case err := <-errChan:
+ return syscall.WaitStatus(1), err
+ case <-timeoutChan:
+ return syscall.WaitStatus(1), fmt.Errorf("timeout waiting for container %q", d.Name)
+ }
+}
+
// WaitForOutput calls 'docker logs' to retrieve containers output and searches
// for the given pattern.
func (d *Docker) WaitForOutput(pattern string, timeout time.Duration) (string, error) {