diff options
Diffstat (limited to 'runsc/container')
-rw-r--r-- | runsc/container/container.go | 8 | ||||
-rw-r--r-- | runsc/container/container_test.go | 4 | ||||
-rw-r--r-- | runsc/container/multi_container_test.go | 94 |
3 files changed, 96 insertions, 10 deletions
diff --git a/runsc/container/container.go b/runsc/container/container.go index 792b7967b..a24c6cc31 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -398,22 +398,22 @@ func (c *Container) Wait() (syscall.WaitStatus, error) { // WaitRootPID waits for process 'pid' in the sandbox's PID namespace and // returns its WaitStatus. -func (c *Container) WaitRootPID(pid int32) (syscall.WaitStatus, error) { +func (c *Container) WaitRootPID(pid int32, clearStatus bool) (syscall.WaitStatus, error) { log.Debugf("Wait on pid %d in sandbox %q", pid, c.Sandbox.ID) if c.Sandbox == nil || !c.Sandbox.IsRunning() { return 0, fmt.Errorf("container sandbox is not running") } - return c.Sandbox.WaitPID(pid, c.Sandbox.ID) + return c.Sandbox.WaitPID(c.Sandbox.ID, pid, clearStatus) } // WaitPID waits for process 'pid' in the container's PID namespace and returns // its WaitStatus. -func (c *Container) WaitPID(pid int32) (syscall.WaitStatus, error) { +func (c *Container) WaitPID(pid int32, clearStatus bool) (syscall.WaitStatus, error) { log.Debugf("Wait on pid %d in container %q", pid, c.ID) if c.Sandbox == nil || !c.Sandbox.IsRunning() { return 0, fmt.Errorf("container sandbox is not running") } - return c.Sandbox.WaitPID(pid, c.ID) + return c.Sandbox.WaitPID(c.ID, pid, clearStatus) } // Signal sends the signal to the container. diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index ab1823f1c..5fe80f20f 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -551,7 +551,7 @@ func TestExec(t *testing.T) { args := &control.ExecArgs{ Filename: "/bin/sleep", - Argv: []string{"sleep", "5"}, + Argv: []string{"/bin/sleep", "5"}, WorkingDirectory: "/", KUID: uid, } @@ -1598,7 +1598,7 @@ func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus, if err != nil { return 0, fmt.Errorf("error executing: %v", err) } - ws, err := cont.WaitPID(pid) + ws, err := cont.WaitPID(pid, true /* clearStatus */) if err != nil { return 0, fmt.Errorf("error waiting: %v", err) } diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index 84e0ec080..09888cb86 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -163,16 +163,15 @@ func TestMultiContainerWait(t *testing.T) { go func(c *Container) { defer wg.Done() const pid = 2 - if ws, err := c.WaitPID(pid); err != nil { + if ws, err := c.WaitPID(pid, true /* clearStatus */); err != nil { t.Errorf("failed to wait for PID %d: %v", pid, err) } else if es := ws.ExitStatus(); es != 0 { t.Errorf("PID %d exited with non-zero status %d", pid, es) } - if _, err := c.WaitPID(pid); err == nil { + if _, err := c.WaitPID(pid, true /* clearStatus */); err == nil { t.Errorf("wait for stopped PID %d should fail", pid) } - // TODO: use 'container[1]' when PID namespace is supported. - }(containers[0]) + }(containers[1]) } wg.Wait() @@ -184,6 +183,93 @@ func TestMultiContainerWait(t *testing.T) { } } +// TestExecWait ensures what we can wait containers and individual processes in the +// sandbox that have already exited. +func TestExecWait(t *testing.T) { + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("error creating root dir: %v", err) + } + defer os.RemoveAll(rootDir) + + // The first container should run the entire duration of the test. + cmd1 := []string{"sleep", "100"} + // We'll wait on the second container, which is much shorter lived. + cmd2 := []string{"sleep", "1"} + specs, ids := createSpecs(cmd1, cmd2) + + // Setup the containers. + var containers []*Container + for i, spec := range specs { + conf := testutil.TestConfig() + bundleDir, err := testutil.SetupContainerInRoot(rootDir, spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(bundleDir) + cont, err := Create(ids[i], spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer cont.Destroy() + if err := cont.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + containers = append(containers, cont) + } + + // Check via ps that multiple processes are running. + expectedPL := []*control.Process{ + {PID: 1, Cmd: "sleep"}, + {PID: 2, Cmd: "sleep"}, + } + if err := waitForProcessList(containers[0], expectedPL); err != nil { + t.Fatalf("failed to wait for sleep to start: %v", err) + } + + // Wait for the second container to finish. + if err := waitForProcessList(containers[0], expectedPL[:1]); err != nil { + t.Fatalf("failed to wait for second container to stop: %v", err) + } + + // Get the second container exit status. + if ws, err := containers[1].Wait(); err != nil { + t.Fatalf("failed to wait for process %s: %v", containers[1].Spec.Process.Args, err) + } else if es := ws.ExitStatus(); es != 0 { + t.Fatalf("process %s exited with non-zero status %d", containers[1].Spec.Process.Args, es) + } + if _, err := containers[1].Wait(); err == nil { + t.Fatalf("wait for stopped process %s should fail", containers[1].Spec.Process.Args) + } + + // Execute another process in the first container. + args := &control.ExecArgs{ + Filename: "/bin/sleep", + Argv: []string{"/bin/sleep", "1"}, + WorkingDirectory: "/", + KUID: 0, + } + pid, err := containers[0].Execute(args) + if err != nil { + t.Fatalf("error executing: %v", err) + } + + // Wait for the exec'd process to exit. + if err := waitForProcessList(containers[0], expectedPL[:1]); err != nil { + t.Fatalf("failed to wait for second container to stop: %v", err) + } + + // Get the exit status from the exec'd process. + if ws, err := containers[0].WaitPID(pid, true /* clearStatus */); err != nil { + t.Fatalf("failed to wait for process %+v with pid %d: %v", args, pid, err) + } else if es := ws.ExitStatus(); es != 0 { + t.Fatalf("process %+v exited with non-zero status %d", args, es) + } + if _, err := containers[0].WaitPID(pid, true /* clearStatus */); err == nil { + t.Fatalf("wait for stopped process %+v should fail", args) + } +} + // TestMultiContainerMount tests that bind mounts can be used with multiple // containers. func TestMultiContainerMount(t *testing.T) { |