mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
12f9094761
commit
4e96b94915
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user