diff options
author | Fabricio Voznika <fvoznika@google.com> | 2019-08-02 13:46:42 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2019-08-02 13:47:55 -0700 |
commit | b461be88a8036ca0455aceb8b6c723d6b6fded4f (patch) | |
tree | c4b7b4ce6b9163ef5fd326ed4245cce04aa4621b /runsc/container | |
parent | 2906dffcdbd4398361d5d7f718faa4f24fdc3b8e (diff) |
Stops container if gofer is killed
Each gofer now has a goroutine that polls on the FDs used
to communicate with the sandbox. The respective gofer is
destroyed if any of the FDs is closed.
Closes #601
PiperOrigin-RevId: 261383725
Diffstat (limited to 'runsc/container')
-rw-r--r-- | runsc/container/BUILD | 1 | ||||
-rw-r--r-- | runsc/container/container_test.go | 2 | ||||
-rw-r--r-- | runsc/container/multi_container_test.go | 86 |
3 files changed, 84 insertions, 5 deletions
diff --git a/runsc/container/BUILD b/runsc/container/BUILD index e246c38ae..de8202bb1 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -49,6 +49,7 @@ go_test( "//pkg/abi/linux", "//pkg/log", "//pkg/sentry/control", + "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/unet", "//pkg/urpc", diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index ff68c586e..af128bf1c 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -76,7 +76,7 @@ func waitForProcessCount(cont *Container, want int) error { } func blockUntilWaitable(pid int) error { - _, _, err := testutil.RetryEintr(func() (uintptr, uintptr, error) { + _, _, err := specutils.RetryEintr(func() (uintptr, uintptr, error) { var err error _, _, err1 := syscall.Syscall6(syscall.SYS_WAITID, 1, uintptr(pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0) if err1 != 0 { diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index 978a422f5..2ef065a15 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -29,6 +29,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/sentry/control" + "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/specutils" "gvisor.dev/gvisor/runsc/test/testutil" @@ -488,7 +489,7 @@ func TestMultiContainerSignal(t *testing.T) { if err := containers[1].Destroy(); err != nil { t.Errorf("failed to destroy container: %v", err) } - _, _, err = testutil.RetryEintr(func() (uintptr, uintptr, error) { + _, _, err = specutils.RetryEintr(func() (uintptr, uintptr, error) { cpid, err := syscall.Wait4(goferPid, nil, 0, nil) return uintptr(cpid), 0, err }) @@ -905,9 +906,9 @@ func TestMultiContainerDifferentFilesystems(t *testing.T) { } } -// TestMultiContainerGoferStop tests that IO operations continue to work after -// containers have been stopped and gofers killed. -func TestMultiContainerGoferStop(t *testing.T) { +// TestMultiContainerContainerDestroyStress tests that IO operations continue +// to work after containers have been stopped and gofers killed. +func TestMultiContainerContainerDestroyStress(t *testing.T) { app, err := testutil.FindFile("runsc/container/test_app/test_app") if err != nil { t.Fatal("error finding test_app:", err) @@ -1345,3 +1346,80 @@ func TestMultiContainerMultiRootCanHandleFDs(t *testing.T) { } } } + +// Test that container is destroyed when Gofer is killed. +func TestMultiContainerGoferKilled(t *testing.T) { + sleep := []string{"sleep", "100"} + specs, ids := createSpecs(sleep, sleep, sleep) + conf := testutil.TestConfig() + containers, cleanup, err := startContainers(conf, specs, ids) + if err != nil { + t.Fatalf("error starting containers: %v", err) + } + defer cleanup() + + // Ensure container is running + c := containers[2] + expectedPL := []*control.Process{ + {PID: 3, Cmd: "sleep"}, + } + if err := waitForProcessList(c, expectedPL); err != nil { + t.Errorf("failed to wait for sleep to start: %v", err) + } + + // Kill container's gofer. + if err := syscall.Kill(c.GoferPid, syscall.SIGKILL); err != nil { + t.Fatalf("syscall.Kill(%d, SIGKILL)=%v", c.GoferPid, err) + } + + // Wait until container stops. + if err := waitForProcessList(c, nil); err != nil { + t.Errorf("Container %q was not stopped after gofer death: %v", c.ID, err) + } + + // Check that container isn't running anymore. + args := &control.ExecArgs{Argv: []string{"/bin/true"}} + if _, err := c.executeSync(args); err == nil { + t.Fatalf("Container %q was not stopped after gofer death", c.ID) + } + + // Check that other containers are unaffected. + for i, c := range containers { + if i == 2 { + continue // container[2] has been killed. + } + pl := []*control.Process{ + {PID: kernel.ThreadID(i + 1), Cmd: "sleep"}, + } + if err := waitForProcessList(c, pl); err != nil { + t.Errorf("Container %q was affected by another container: %v", c.ID, err) + } + args := &control.ExecArgs{Argv: []string{"/bin/true"}} + if _, err := c.executeSync(args); err != nil { + t.Fatalf("Container %q was affected by another container: %v", c.ID, err) + } + } + + // Kill root container's gofer to bring entire sandbox down. + c = containers[0] + if err := syscall.Kill(c.GoferPid, syscall.SIGKILL); err != nil { + t.Fatalf("syscall.Kill(%d, SIGKILL)=%v", c.GoferPid, err) + } + + // Wait until sandbox stops. waitForProcessList will loop until sandbox exits + // and RPC errors out. + impossiblePL := []*control.Process{ + {PID: 100, Cmd: "non-existent-process"}, + } + if err := waitForProcessList(c, impossiblePL); err == nil { + t.Fatalf("Sandbox was not killed after gofer death") + } + + // Check that entire sandbox isn't running anymore. + for _, c := range containers { + args := &control.ExecArgs{Argv: []string{"/bin/true"}} + if _, err := c.executeSync(args); err == nil { + t.Fatalf("Container %q was not stopped after gofer death", c.ID) + } + } +} |