summaryrefslogtreecommitdiffhomepage
path: root/runsc/container
diff options
context:
space:
mode:
authorNicolas Lacasse <nlacasse@google.com>2018-11-15 15:34:38 -0800
committerShentubot <shentubot@google.com>2018-11-15 15:35:41 -0800
commitadf8138e069a99aa7f3fe190a9a7c17a4e88b99a (patch)
tree349dd842162121546b957702aa8abebdfc82cc7b /runsc/container
parentf7aa9371247a3e7d8c490ac0fd4c4f3ff6de2017 (diff)
Allow sandbox.Wait to be called after the sandbox has exited.
sandbox.Wait is racey, as the sandbox may have exited before it is called, or even during. We already had code to handle the case that the sandbox exits during the Wait call, but we were not properly handling the case where the sandbox has exited before the call. The best we can do in such cases is return the sandbox exit code as the application exit code. PiperOrigin-RevId: 221702517 Change-Id: I290d0333cc094c7c1c3b4ce0f17f61a3e908d787
Diffstat (limited to 'runsc/container')
-rw-r--r--runsc/container/container.go3
-rw-r--r--runsc/container/container_test.go52
2 files changed, 31 insertions, 24 deletions
diff --git a/runsc/container/container.go b/runsc/container/container.go
index 11c440f09..80a27df4a 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -476,9 +476,6 @@ func (c *Container) SandboxPid() int {
// and wait returns immediately.
func (c *Container) Wait() (syscall.WaitStatus, error) {
log.Debugf("Wait on container %q", c.ID)
- if !c.isSandboxRunning() {
- return 0, fmt.Errorf("sandbox is not running")
- }
return c.Sandbox.Wait(c.ID)
}
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
index 598b96a08..45a36e583 100644
--- a/runsc/container/container_test.go
+++ b/runsc/container/container_test.go
@@ -39,12 +39,8 @@ import (
"gvisor.googlesource.com/gvisor/runsc/test/testutil"
)
-func init() {
- log.SetLevel(log.Debug)
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
- }
-}
+// childReaper reaps child processes.
+var childReaper *testutil.Reaper
// waitForProcessList waits for the given process list to show up in the container.
func waitForProcessList(cont *Container, want []*control.Process) error {
@@ -1580,12 +1576,17 @@ func TestUserLog(t *testing.T) {
}
func TestWaitOnExitedSandbox(t *testing.T) {
+ // Disable the childReaper for this test.
+ childReaper.Stop()
+ defer childReaper.Start()
+
for _, conf := range configs(all...) {
t.Logf("Running test with conf: %+v", conf)
- // Run a shell that exits immediately with a non-zero code.
+ // Run a shell that sleeps for 1 second and then exits with a
+ // non-zero code.
const wantExit = 17
- cmd := fmt.Sprintf("exit %d", wantExit)
+ cmd := fmt.Sprintf("sleep 1; exit %d", wantExit)
spec := testutil.NewSpecWithArgs("/bin/sh", "-c", cmd)
rootDir, bundleDir, err := testutil.SetupContainer(spec, conf)
if err != nil {
@@ -1604,22 +1605,23 @@ func TestWaitOnExitedSandbox(t *testing.T) {
t.Fatalf("error starting container: %v", err)
}
- // Wait for the sandbox to stop running.
- if err := testutil.Poll(func() error {
- if c.Sandbox.IsRunning() {
- return nil
- }
- return fmt.Errorf("sandbox still running")
- }, 10*time.Second); err != nil {
- t.Fatalf("error waiting for sandbox to exit: %v", err)
- }
-
- // Now call Wait.
+ // Wait on the sandbox. This will make an RPC to the sandbox
+ // and get the actual exit status of the application.
ws, err := c.Wait()
if err != nil {
t.Fatalf("error waiting on container: %v", err)
}
+ if got := ws.ExitStatus(); got != wantExit {
+ t.Errorf("got exit status %d, want %d", got, wantExit)
+ }
+ // Now the sandbox has exited, but the zombie sandbox process
+ // still exists. Calling Wait() now will return the sandbox
+ // exit status.
+ ws, err = c.Wait()
+ if err != nil {
+ t.Fatalf("error waiting on container: %v", err)
+ }
if got := ws.ExitStatus(); got != wantExit {
t.Errorf("got exit status %d, want %d", got, wantExit)
}
@@ -1704,8 +1706,16 @@ func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus,
}
func TestMain(m *testing.M) {
+ log.SetLevel(log.Debug)
+ if err := testutil.ConfigureExePath(); err != nil {
+ panic(err.Error())
+ }
testutil.RunAsRoot()
- stop := testutil.StartReaper()
- defer stop()
+
+ // Start the child reaper.
+ childReaper = &testutil.Reaper{}
+ childReaper.Start()
+ defer childReaper.Stop()
+
os.Exit(m.Run())
}