Combine executable lookup code

Run vs. exec, VFS1 vs. VFS2 were executable lookup were
slightly different from each other. Combine them all
into the same logic.

PiperOrigin-RevId: 315426443
This commit is contained in:
Fabricio Voznika
2020-06-08 23:08:23 -07:00
committed by gVisor bot
parent 12f9094761
commit 4e96b94915
8 changed files with 285 additions and 202 deletions
-4
View File
@@ -16,15 +16,12 @@ go_library(
],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/fd",
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/fdimport",
"//pkg/sentry/fs",
"//pkg/sentry/fs/host",
"//pkg/sentry/fs/user",
"//pkg/sentry/fsbridge",
"//pkg/sentry/fsimpl/host",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
@@ -36,7 +33,6 @@ go_library(
"//pkg/sentry/vfs",
"//pkg/sentry/watchdog",
"//pkg/sync",
"//pkg/syserror",
"//pkg/tcpip/link/sniffer",
"//pkg/urpc",
"@org_golang_x_sys//unix:go_default_library",
+24 -60
View File
@@ -25,13 +25,10 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fdimport"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/host"
"gvisor.dev/gvisor/pkg/sentry/fs/user"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
hostvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -39,7 +36,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/urpc"
)
@@ -107,6 +103,9 @@ type ExecArgs struct {
// String prints the arguments as a string.
func (args ExecArgs) String() string {
if len(args.Argv) == 0 {
return args.Filename
}
a := make([]string, len(args.Argv))
copy(a, args.Argv)
if args.Filename != "" {
@@ -179,37 +178,30 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
}
ctx := initArgs.NewContext(proc.Kernel)
if initArgs.Filename == "" {
if kernel.VFS2Enabled {
// Get the full path to the filename from the PATH env variable.
if initArgs.MountNamespaceVFS2 == nil {
// Set initArgs so that 'ctx' returns the namespace.
//
// MountNamespaceVFS2 adds a reference to the namespace, which is
// transferred to the new process.
initArgs.MountNamespaceVFS2 = proc.Kernel.GlobalInit().Leader().MountNamespaceVFS2()
}
file, err := getExecutableFD(ctx, creds, proc.Kernel.VFS(), initArgs.MountNamespaceVFS2, initArgs.Envv, initArgs.WorkingDirectory, initArgs.Argv[0])
if err != nil {
return nil, 0, nil, nil, fmt.Errorf("error finding executable %q in environment %v: %v", initArgs.Argv[0], initArgs.Envv, err)
}
initArgs.File = fsbridge.NewVFSFile(file)
} else {
if initArgs.MountNamespace == nil {
// Set initArgs so that 'ctx' returns the namespace.
initArgs.MountNamespace = proc.Kernel.GlobalInit().Leader().MountNamespace()
if kernel.VFS2Enabled {
// Get the full path to the filename from the PATH env variable.
if initArgs.MountNamespaceVFS2 == nil {
// Set initArgs so that 'ctx' returns the namespace.
//
// MountNamespaceVFS2 adds a reference to the namespace, which is
// transferred to the new process.
initArgs.MountNamespaceVFS2 = proc.Kernel.GlobalInit().Leader().MountNamespaceVFS2()
}
} else {
if initArgs.MountNamespace == nil {
// Set initArgs so that 'ctx' returns the namespace.
initArgs.MountNamespace = proc.Kernel.GlobalInit().Leader().MountNamespace()
// initArgs must hold a reference on MountNamespace, which will
// be donated to the new process in CreateProcess.
initArgs.MountNamespace.IncRef()
}
f, err := user.ResolveExecutablePath(ctx, creds, initArgs.MountNamespace, initArgs.Envv, initArgs.WorkingDirectory, initArgs.Argv[0])
if err != nil {
return nil, 0, nil, nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], initArgs.Envv, err)
}
initArgs.Filename = f
// initArgs must hold a reference on MountNamespace, which will
// be donated to the new process in CreateProcess.
initArgs.MountNamespace.IncRef()
}
}
resolved, err := user.ResolveExecutablePath(ctx, &initArgs)
if err != nil {
return nil, 0, nil, nil, err
}
initArgs.Filename = resolved
fds := make([]int, len(args.FilePayload.Files))
for i, file := range args.FilePayload.Files {
@@ -422,31 +414,3 @@ func ttyName(tty *kernel.TTY) string {
}
return fmt.Sprintf("pts/%d", tty.Index)
}
// getExecutableFD resolves the given executable name and returns a
// vfs.FileDescription for the executable file.
func getExecutableFD(ctx context.Context, creds *auth.Credentials, vfsObj *vfs.VirtualFilesystem, mns *vfs.MountNamespace, envv []string, wd, name string) (*vfs.FileDescription, error) {
path, err := user.ResolveExecutablePathVFS2(ctx, creds, mns, envv, wd, name)
if err != nil {
return nil, err
}
root := vfs.RootFromContext(ctx)
defer root.DecRef()
pop := vfs.PathOperation{
Root: root,
Start: root, // binPath is absolute, Start can be anything.
Path: fspath.Parse(path),
FollowFinalSymlink: true,
}
opts := &vfs.OpenOptions{
Flags: linux.O_RDONLY,
FileExec: true,
}
f, err := vfsObj.OpenAt(ctx, creds, &pop, opts)
if err == syserror.ENOENT || err == syserror.EACCES {
return nil, nil
}
return f, err
}
+1
View File
@@ -15,6 +15,7 @@ go_library(
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/fs",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
"//pkg/syserror",
+29 -28
View File
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
@@ -31,7 +32,15 @@ import (
// ResolveExecutablePath resolves the given executable name given the working
// dir and environment.
func ResolveExecutablePath(ctx context.Context, creds *auth.Credentials, mns *fs.MountNamespace, envv []string, wd, name string) (string, error) {
func ResolveExecutablePath(ctx context.Context, args *kernel.CreateProcessArgs) (string, error) {
name := args.Filename
if len(name) == 0 {
if len(args.Argv) == 0 {
return "", fmt.Errorf("no filename or command provided")
}
name = args.Argv[0]
}
// Absolute paths can be used directly.
if path.IsAbs(name) {
return name, nil
@@ -40,6 +49,7 @@ func ResolveExecutablePath(ctx context.Context, creds *auth.Credentials, mns *fs
// Paths with '/' in them should be joined to the working directory, or
// to the root if working directory is not set.
if strings.IndexByte(name, '/') > 0 {
wd := args.WorkingDirectory
if wd == "" {
wd = "/"
}
@@ -49,10 +59,24 @@ func ResolveExecutablePath(ctx context.Context, creds *auth.Credentials, mns *fs
return path.Join(wd, name), nil
}
// Otherwise, We must lookup the name in the paths, starting from the
// calling context's root directory.
paths := getPath(envv)
// Otherwise, We must lookup the name in the paths.
paths := getPath(args.Envv)
if kernel.VFS2Enabled {
f, err := resolveVFS2(ctx, args.Credentials, args.MountNamespaceVFS2, paths, name)
if err != nil {
return "", fmt.Errorf("error finding executable %q in PATH %v: %v", name, paths, err)
}
return f, nil
}
f, err := resolve(ctx, args.MountNamespace, paths, name)
if err != nil {
return "", fmt.Errorf("error finding executable %q in PATH %v: %v", name, paths, err)
}
return f, nil
}
func resolve(ctx context.Context, mns *fs.MountNamespace, paths []string, name string) (string, error) {
root := fs.RootFromContext(ctx)
if root == nil {
// Caller has no root. Don't bother traversing anything.
@@ -95,30 +119,7 @@ func ResolveExecutablePath(ctx context.Context, creds *auth.Credentials, mns *fs
return "", syserror.ENOENT
}
// ResolveExecutablePathVFS2 resolves the given executable name given the
// working dir and environment.
func ResolveExecutablePathVFS2(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, envv []string, wd, name string) (string, error) {
// Absolute paths can be used directly.
if path.IsAbs(name) {
return name, nil
}
// Paths with '/' in them should be joined to the working directory, or
// to the root if working directory is not set.
if strings.IndexByte(name, '/') > 0 {
if wd == "" {
wd = "/"
}
if !path.IsAbs(wd) {
return "", fmt.Errorf("working directory %q must be absolute", wd)
}
return path.Join(wd, name), nil
}
// Otherwise, We must lookup the name in the paths, starting from the
// calling context's root directory.
paths := getPath(envv)
func resolveVFS2(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, paths []string, name string) (string, error) {
root := mns.Root()
defer root.DecRef()
for _, p := range paths {
+3 -3
View File
@@ -293,11 +293,11 @@ func setupContainerFS(ctx context.Context, conf *Config, mntr *containerMounter,
procArgs.MountNamespace = mns
// Resolve the executable path from working dir and environment.
f, err := user.ResolveExecutablePath(ctx, procArgs.Credentials, procArgs.MountNamespace, procArgs.Envv, procArgs.WorkingDirectory, procArgs.Argv[0])
resolved, err := user.ResolveExecutablePath(ctx, procArgs)
if err != nil {
return fmt.Errorf("searching for executable %q, cwd: %q, envv: %q: %v", procArgs.Argv[0], procArgs.WorkingDirectory, procArgs.Envv, err)
return err
}
procArgs.Filename = f
procArgs.Filename = resolved
return nil
}
+3 -3
View File
@@ -96,11 +96,11 @@ func setupContainerVFS2(ctx context.Context, conf *Config, mntr *containerMounte
procArgs.MountNamespaceVFS2 = mns
// Resolve the executable path from working dir and environment.
f, err := user.ResolveExecutablePathVFS2(ctx, procArgs.Credentials, procArgs.MountNamespaceVFS2, procArgs.Envv, procArgs.WorkingDirectory, procArgs.Argv[0])
resolved, err := user.ResolveExecutablePath(ctx, procArgs)
if err != nil {
return fmt.Errorf("searching for executable %q, cwd: %q, envv: %q: %v", procArgs.Argv[0], procArgs.WorkingDirectory, procArgs.Envv, err)
return err
}
procArgs.Filename = f
procArgs.Filename = resolved
return nil
}
+202 -82
View File
@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path"
"path/filepath"
@@ -53,9 +54,8 @@ func waitForProcessList(cont *Container, want []*control.Process) error {
err = fmt.Errorf("error getting process data from container: %v", err)
return &backoff.PermanentError{Err: err}
}
if r, err := procListsEqual(got, want); !r {
return fmt.Errorf("container got process list: %s, want: %s: error: %v",
procListToString(got), procListToString(want), err)
if !procListsEqual(got, want) {
return fmt.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(want))
}
return nil
}
@@ -92,36 +92,72 @@ func blockUntilWaitable(pid int) error {
return err
}
// procListsEqual is used to check whether 2 Process lists are equal for all
// implemented fields.
func procListsEqual(got, want []*control.Process) (bool, error) {
if len(got) != len(want) {
return false, nil
// procListsEqual is used to check whether 2 Process lists are equal. Fields
// set to -1 in wants are ignored. Timestamp and threads fields are always
// ignored.
func procListsEqual(gots, wants []*control.Process) bool {
if len(gots) != len(wants) {
return false
}
for i := range got {
pd1 := got[i]
pd2 := want[i]
// Zero out timing dependant fields.
pd1.Time = ""
pd1.STime = ""
pd1.C = 0
// Ignore TTY field too, since it's not relevant in the cases
// where we use this method. Tests that care about the TTY
// field should check for it themselves.
pd1.TTY = ""
pd1Json, err := control.ProcessListToJSON([]*control.Process{pd1})
if err != nil {
return false, err
for i := range gots {
got := gots[i]
want := wants[i]
if want.UID != math.MaxUint32 && want.UID != got.UID {
return false
}
pd2Json, err := control.ProcessListToJSON([]*control.Process{pd2})
if err != nil {
return false, err
if want.PID != -1 && want.PID != got.PID {
return false
}
if pd1Json != pd2Json {
return false, nil
if want.PPID != -1 && want.PPID != got.PPID {
return false
}
if len(want.TTY) != 0 && want.TTY != got.TTY {
return false
}
if len(want.Cmd) != 0 && want.Cmd != got.Cmd {
return false
}
}
return true, nil
return true
}
type processBuilder struct {
process control.Process
}
func newProcessBuilder() *processBuilder {
return &processBuilder{
process: control.Process{
UID: math.MaxUint32,
PID: -1,
PPID: -1,
},
}
}
func (p *processBuilder) Cmd(cmd string) *processBuilder {
p.process.Cmd = cmd
return p
}
func (p *processBuilder) PID(pid kernel.ThreadID) *processBuilder {
p.process.PID = pid
return p
}
func (p *processBuilder) PPID(ppid kernel.ThreadID) *processBuilder {
p.process.PPID = ppid
return p
}
func (p *processBuilder) UID(uid auth.KUID) *processBuilder {
p.process.UID = uid
return p
}
func (p *processBuilder) Process() *control.Process {
return &p.process
}
func procListToString(pl []*control.Process) string {
@@ -323,14 +359,7 @@ func TestLifecycle(t *testing.T) {
// expectedPL lists the expected process state of the container.
expectedPL := []*control.Process{
{
UID: 0,
PID: 1,
PPID: 0,
C: 0,
Cmd: "sleep",
Threads: []kernel.ThreadID{1},
},
newProcessBuilder().Cmd("sleep").Process(),
}
// Create the container.
args := Args{
@@ -608,7 +637,134 @@ func doAppExitStatus(t *testing.T, vfs2 bool) {
// TestExec verifies that a container can exec a new program.
func TestExec(t *testing.T) {
for name, conf := range configsWithVFS2(t, overlay) {
for name, conf := range configsWithVFS2(t, all...) {
t.Run(name, func(t *testing.T) {
dir, err := ioutil.TempDir(testutil.TmpDir(), "exec-test")
if err != nil {
t.Fatalf("error creating temporary directory: %v", err)
}
cmd := fmt.Sprintf("ln -s /bin/true %q/symlink && sleep 100", dir)
spec := testutil.NewSpecWithArgs("sh", "-c", cmd)
_, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
if err != nil {
t.Fatalf("error setting up container: %v", err)
}
defer cleanup()
// Create and start the container.
args := Args{
ID: testutil.RandomContainerID(),
Spec: spec,
BundleDir: bundleDir,
}
cont, err := New(conf, args)
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)
}
// Wait until sleep is running to ensure the symlink was created.
expectedPL := []*control.Process{
newProcessBuilder().Cmd("sh").Process(),
newProcessBuilder().Cmd("sleep").Process(),
}
if err := waitForProcessList(cont, expectedPL); err != nil {
t.Fatalf("waitForProcessList: %v", err)
}
for _, tc := range []struct {
name string
args control.ExecArgs
}{
{
name: "complete",
args: control.ExecArgs{
Filename: "/bin/true",
Argv: []string{"/bin/true"},
},
},
{
name: "filename",
args: control.ExecArgs{
Filename: "/bin/true",
},
},
{
name: "argv",
args: control.ExecArgs{
Argv: []string{"/bin/true"},
},
},
{
name: "filename resolution",
args: control.ExecArgs{
Filename: "true",
Envv: []string{"PATH=/bin"},
},
},
{
name: "argv resolution",
args: control.ExecArgs{
Argv: []string{"true"},
Envv: []string{"PATH=/bin"},
},
},
{
name: "argv symlink",
args: control.ExecArgs{
Argv: []string{filepath.Join(dir, "symlink")},
},
},
{
name: "working dir",
args: control.ExecArgs{
Argv: []string{"/bin/sh", "-c", `if [[ "${PWD}" != "/tmp" ]]; then exit 1; fi`},
WorkingDirectory: "/tmp",
},
},
{
name: "user",
args: control.ExecArgs{
Argv: []string{"/bin/sh", "-c", `if [[ "$(id -u)" != "343" ]]; then exit 1; fi`},
KUID: 343,
},
},
{
name: "group",
args: control.ExecArgs{
Argv: []string{"/bin/sh", "-c", `if [[ "$(id -g)" != "343" ]]; then exit 1; fi`},
KGID: 343,
},
},
{
name: "env",
args: control.ExecArgs{
Argv: []string{"/bin/sh", "-c", `if [[ "${FOO}" != "123" ]]; then exit 1; fi`},
Envv: []string{"FOO=123"},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
// t.Parallel()
if ws, err := cont.executeSync(&tc.args); err != nil {
t.Fatalf("executeAsync(%+v): %v", tc.args, err)
} else if ws != 0 {
t.Fatalf("executeAsync(%+v) failed with exit: %v", tc.args, ws)
}
})
}
})
}
}
// TestExecProcList verifies that a container can exec a new program and it
// shows correcly in the process list.
func TestExecProcList(t *testing.T) {
for name, conf := range configsWithVFS2(t, all...) {
t.Run(name, func(t *testing.T) {
const uid = 343
spec := testutil.NewSpecWithArgs("sleep", "100")
@@ -634,31 +790,6 @@ func TestExec(t *testing.T) {
t.Fatalf("error starting container: %v", err)
}
// expectedPL lists the expected process state of the container.
expectedPL := []*control.Process{
{
UID: 0,
PID: 1,
PPID: 0,
C: 0,
Cmd: "sleep",
Threads: []kernel.ThreadID{1},
},
{
UID: uid,
PID: 2,
PPID: 0,
C: 0,
Cmd: "sleep",
Threads: []kernel.ThreadID{2},
},
}
// Verify that "sleep 100" is running.
if err := waitForProcessList(cont, expectedPL[:1]); err != nil {
t.Error(err)
}
execArgs := &control.ExecArgs{
Filename: "/bin/sleep",
Argv: []string{"/bin/sleep", "5"},
@@ -666,9 +797,8 @@ func TestExec(t *testing.T) {
KUID: uid,
}
// Verify that "sleep 100" and "sleep 5" are running
// after exec. First, start running exec (whick
// blocks).
// Verify that "sleep 100" and "sleep 5" are running after exec. First,
// start running exec (which blocks).
ch := make(chan error)
go func() {
exitStatus, err := cont.executeSync(execArgs)
@@ -681,6 +811,11 @@ func TestExec(t *testing.T) {
}
}()
// expectedPL lists the expected process state of the container.
expectedPL := []*control.Process{
newProcessBuilder().PID(1).PPID(0).Cmd("sleep").UID(0).Process(),
newProcessBuilder().PID(2).PPID(0).Cmd("sleep").UID(uid).Process(),
}
if err := waitForProcessList(cont, expectedPL); err != nil {
t.Fatalf("error waiting for processes: %v", err)
}
@@ -1242,24 +1377,9 @@ func TestCapabilities(t *testing.T) {
// expectedPL lists the expected process state of the container.
expectedPL := []*control.Process{
{
UID: 0,
PID: 1,
PPID: 0,
C: 0,
Cmd: "sleep",
Threads: []kernel.ThreadID{1},
},
{
UID: uid,
PID: 2,
PPID: 0,
C: 0,
Cmd: "exe",
Threads: []kernel.ThreadID{2},
},
newProcessBuilder().Cmd("sleep").Process(),
}
if err := waitForProcessList(cont, expectedPL[:1]); err != nil {
if err := waitForProcessList(cont, expectedPL); err != nil {
t.Fatalf("Failed to wait for sleep to start, err: %v", err)
}
+23 -22
View File
@@ -149,13 +149,13 @@ func TestMultiContainerSanity(t *testing.T) {
// Check via ps that multiple processes are running.
expectedPL := []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
}
expectedPL = []*control.Process{
{PID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{2}},
newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -195,13 +195,13 @@ func TestMultiPIDNS(t *testing.T) {
// Check via ps that multiple processes are running.
expectedPL := []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
}
expectedPL = []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -257,7 +257,7 @@ func TestMultiPIDNSPath(t *testing.T) {
// Check via ps that multiple processes are running.
expectedPL := []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -267,7 +267,7 @@ func TestMultiPIDNSPath(t *testing.T) {
}
expectedPL = []*control.Process{
{PID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{2}},
newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -300,7 +300,7 @@ func TestMultiContainerWait(t *testing.T) {
// Check via ps that multiple processes are running.
expectedPL := []*control.Process{
{PID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{2}},
newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -345,7 +345,7 @@ func TestMultiContainerWait(t *testing.T) {
// After Wait returns, ensure that the root container is running and
// the child has finished.
expectedPL = []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Errorf("failed to wait for %q to start: %v", strings.Join(containers[0].Spec.Process.Args, " "), err)
@@ -377,7 +377,7 @@ func TestExecWait(t *testing.T) {
// Check via ps that process is running.
expectedPL := []*control.Process{
{PID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{2}},
newProcessBuilder().Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Fatalf("failed to wait for sleep to start: %v", err)
@@ -412,7 +412,7 @@ func TestExecWait(t *testing.T) {
// Wait for the exec'd process to exit.
expectedPL = []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Fatalf("failed to wait for second container to stop: %v", err)
@@ -498,9 +498,8 @@ func TestMultiContainerSignal(t *testing.T) {
// Check via ps that container 1 process is running.
expectedPL := []*control.Process{
{PID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{2}},
newProcessBuilder().Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
}
@@ -512,7 +511,7 @@ func TestMultiContainerSignal(t *testing.T) {
// Make sure process 1 is still running.
expectedPL = []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -626,8 +625,10 @@ func TestMultiContainerDestroy(t *testing.T) {
if err != nil {
t.Fatalf("error getting process data from sandbox: %v", err)
}
expectedPL := []*control.Process{{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}}}
if r, err := procListsEqual(pss, expectedPL); !r {
expectedPL := []*control.Process{
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if !procListsEqual(pss, expectedPL) {
t.Errorf("container got process list: %s, want: %s: error: %v",
procListToString(pss), procListToString(expectedPL), err)
}
@@ -664,7 +665,7 @@ func TestMultiContainerProcesses(t *testing.T) {
// Check root's container process list doesn't include other containers.
expectedPL0 := []*control.Process{
{PID: 1, Cmd: "sleep", Threads: []kernel.ThreadID{1}},
newProcessBuilder().PID(1).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[0], expectedPL0); err != nil {
t.Errorf("failed to wait for process to start: %v", err)
@@ -672,8 +673,8 @@ func TestMultiContainerProcesses(t *testing.T) {
// Same for the other container.
expectedPL1 := []*control.Process{
{PID: 2, Cmd: "sh", Threads: []kernel.ThreadID{2}},
{PID: 3, PPID: 2, Cmd: "sleep", Threads: []kernel.ThreadID{3}},
newProcessBuilder().PID(2).Cmd("sh").Process(),
newProcessBuilder().PID(3).PPID(2).Cmd("sleep").Process(),
}
if err := waitForProcessList(containers[1], expectedPL1); err != nil {
t.Errorf("failed to wait for process to start: %v", err)
@@ -687,7 +688,7 @@ func TestMultiContainerProcesses(t *testing.T) {
if _, err := containers[1].Execute(args); err != nil {
t.Fatalf("error exec'ing: %v", err)
}
expectedPL1 = append(expectedPL1, &control.Process{PID: 4, Cmd: "sleep", Threads: []kernel.ThreadID{4}})
expectedPL1 = append(expectedPL1, newProcessBuilder().PID(4).Cmd("sleep").Process())
if err := waitForProcessList(containers[1], expectedPL1); err != nil {
t.Errorf("failed to wait for process to start: %v", err)
}
@@ -1505,7 +1506,7 @@ func TestMultiContainerGoferKilled(t *testing.T) {
// Ensure container is running
c := containers[2]
expectedPL := []*control.Process{
{PID: 3, Cmd: "sleep", Threads: []kernel.ThreadID{3}},
newProcessBuilder().PID(3).Cmd("sleep").Process(),
}
if err := waitForProcessList(c, expectedPL); err != nil {
t.Errorf("failed to wait for sleep to start: %v", err)
@@ -1533,7 +1534,7 @@ func TestMultiContainerGoferKilled(t *testing.T) {
continue // container[2] has been killed.
}
pl := []*control.Process{
{PID: kernel.ThreadID(i + 1), Cmd: "sleep", Threads: []kernel.ThreadID{kernel.ThreadID(i + 1)}},
newProcessBuilder().PID(kernel.ThreadID(i + 1)).Cmd("sleep").Process(),
}
if err := waitForProcessList(c, pl); err != nil {
t.Errorf("Container %q was affected by another container: %v", c.ID, err)
@@ -1553,7 +1554,7 @@ func TestMultiContainerGoferKilled(t *testing.T) {
// Wait until sandbox stops. waitForProcessList will loop until sandbox exits
// and RPC errors out.
impossiblePL := []*control.Process{
{PID: 100, Cmd: "non-existent-process", Threads: []kernel.ThreadID{100}},
newProcessBuilder().Cmd("non-existent-process").Process(),
}
if err := waitForProcessList(c, impossiblePL); err == nil {
t.Fatalf("Sandbox was not killed after gofer death")