runsc: Add exec flag that specifies where to save the sandbox-internal pid.
This is different from the existing -pid-file flag, which saves a host pid. PiperOrigin-RevId: 212713968 Change-Id: I2c486de8dd5cfd9b923fb0970165ef7c5fc597f0
This commit is contained in:
parent
0efde2bfbd
commit
2eff1fdd06
|
@ -87,6 +87,24 @@ type ExecArgs struct {
|
||||||
|
|
||||||
// Exec runs a new task.
|
// Exec runs a new task.
|
||||||
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
||||||
|
newTG, err := proc.execAsync(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for completion.
|
||||||
|
newTG.WaitExited()
|
||||||
|
*waitStatus = newTG.ExitStatus().Status()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecAsync runs a new task, but doesn't wait for it to finish. It is defined
|
||||||
|
// as a function rather than a method to avoid exposing execAsync as an RPC.
|
||||||
|
func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, error) {
|
||||||
|
return proc.execAsync(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, error) {
|
||||||
// Import file descriptors.
|
// Import file descriptors.
|
||||||
l := limits.NewLimitSet()
|
l := limits.NewLimitSet()
|
||||||
fdm := proc.Kernel.NewFDMap()
|
fdm := proc.Kernel.NewFDMap()
|
||||||
|
@ -121,7 +139,7 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
||||||
paths := fs.GetPath(initArgs.Envv)
|
paths := fs.GetPath(initArgs.Envv)
|
||||||
f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
|
f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
|
return nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
|
||||||
}
|
}
|
||||||
initArgs.Filename = f
|
initArgs.Filename = f
|
||||||
}
|
}
|
||||||
|
@ -133,7 +151,7 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
||||||
// Import the given file FD. This dups the FD as well.
|
// Import the given file FD. This dups the FD as well.
|
||||||
file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
|
file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.DecRef()
|
defer file.DecRef()
|
||||||
|
|
||||||
|
@ -141,20 +159,11 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
|
if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the new task.
|
return proc.Kernel.CreateProcess(initArgs)
|
||||||
newTG, err := proc.Kernel.CreateProcess(initArgs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for completion.
|
|
||||||
newTG.WaitExited()
|
|
||||||
*waitStatus = newTG.ExitStatus().Status()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PsArgs is the set of arguments to ps.
|
// PsArgs is the set of arguments to ps.
|
||||||
|
|
|
@ -41,9 +41,9 @@ const (
|
||||||
// container used by "runsc events".
|
// container used by "runsc events".
|
||||||
ContainerEvent = "containerManager.Event"
|
ContainerEvent = "containerManager.Event"
|
||||||
|
|
||||||
// ContainerExecute is the URPC endpoint for executing a command in a
|
// ContainerExecuteAsync is the URPC endpoint for executing a command in a
|
||||||
// container..
|
// container..
|
||||||
ContainerExecute = "containerManager.Execute"
|
ContainerExecuteAsync = "containerManager.ExecuteAsync"
|
||||||
|
|
||||||
// ContainerPause pauses the container.
|
// ContainerPause pauses the container.
|
||||||
ContainerPause = "containerManager.Pause"
|
ContainerPause = "containerManager.Pause"
|
||||||
|
@ -233,33 +233,40 @@ type ExecArgs struct {
|
||||||
CID string
|
CID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs a command on a created or running sandbox.
|
// ExecuteAsync starts running a command on a created or running sandbox. It
|
||||||
func (cm *containerManager) Execute(e *ExecArgs, waitStatus *uint32) error {
|
// returns the pid of the new process.
|
||||||
log.Debugf("containerManager.Execute: %+v", *e)
|
func (cm *containerManager) ExecuteAsync(args *ExecArgs, pid *int32) error {
|
||||||
|
log.Debugf("containerManager.ExecuteAsync: %+v", args)
|
||||||
|
|
||||||
// Get the container Root Dirent from the Task, since we must run this
|
// Get the container Root Dirent from the Task, since we must run this
|
||||||
// process with the same Root.
|
// process with the same Root.
|
||||||
cm.l.mu.Lock()
|
cm.l.mu.Lock()
|
||||||
tgid, ok := cm.l.containerRootTGIDs[e.CID]
|
tgid, ok := cm.l.containerRootTGIDs[args.CID]
|
||||||
cm.l.mu.Unlock()
|
cm.l.mu.Unlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("cannot exec in container %q: no such container", e.CID)
|
return fmt.Errorf("cannot exec in container %q: no such container", args.CID)
|
||||||
}
|
}
|
||||||
t := cm.l.k.TaskSet().Root.TaskWithID(kernel.ThreadID(tgid))
|
t := cm.l.k.TaskSet().Root.TaskWithID(kernel.ThreadID(tgid))
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return fmt.Errorf("cannot exec in container %q: no thread group with ID %d", e.CID, tgid)
|
return fmt.Errorf("cannot exec in container %q: no thread group with ID %d", args.CID, tgid)
|
||||||
}
|
}
|
||||||
t.WithMuLocked(func(t *kernel.Task) {
|
t.WithMuLocked(func(t *kernel.Task) {
|
||||||
e.Root = t.FSContext().RootDirectory()
|
args.Root = t.FSContext().RootDirectory()
|
||||||
})
|
})
|
||||||
if e.Root != nil {
|
if args.Root != nil {
|
||||||
defer e.Root.DecRef()
|
defer args.Root.DecRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the process.
|
||||||
proc := control.Proc{Kernel: cm.l.k}
|
proc := control.Proc{Kernel: cm.l.k}
|
||||||
if err := proc.Exec(&e.ExecArgs, waitStatus); err != nil {
|
newTG, err := control.ExecAsync(&proc, &args.ExecArgs)
|
||||||
return fmt.Errorf("error executing: %+v: %v", e, err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("error executing: %+v: %v", args, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the pid of the newly-created process.
|
||||||
|
ts := cm.l.k.TaskSet()
|
||||||
|
*pid = int32(ts.Root.IDOfThreadGroup(newTG))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ type Exec struct {
|
||||||
detach bool
|
detach bool
|
||||||
processPath string
|
processPath string
|
||||||
pidFile string
|
pidFile string
|
||||||
|
internalPidFile string
|
||||||
|
|
||||||
// consoleSocket is the path to an AF_UNIX socket which will receive a
|
// consoleSocket is the path to an AF_UNIX socket which will receive a
|
||||||
// file descriptor referencing the master end of the console's
|
// file descriptor referencing the master end of the console's
|
||||||
|
@ -97,6 +98,7 @@ func (ex *Exec) SetFlags(f *flag.FlagSet) {
|
||||||
f.BoolVar(&ex.detach, "detach", false, "detach from the container's process")
|
f.BoolVar(&ex.detach, "detach", false, "detach from the container's process")
|
||||||
f.StringVar(&ex.processPath, "process", "", "path to the process.json")
|
f.StringVar(&ex.processPath, "process", "", "path to the process.json")
|
||||||
f.StringVar(&ex.pidFile, "pid-file", "", "filename that the container pid will be written to")
|
f.StringVar(&ex.pidFile, "pid-file", "", "filename that the container pid will be written to")
|
||||||
|
f.StringVar(&ex.internalPidFile, "internal-pid-file", "", "filename that the container-internal pid will be written to")
|
||||||
f.StringVar(&ex.consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal")
|
f.StringVar(&ex.consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,10 +148,25 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws, err := c.Execute(e)
|
// Start the new process and get it pid.
|
||||||
|
pid, err := c.Execute(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("error getting processes for container: %v", err)
|
Fatalf("error getting processes for container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the sandbox-internal pid if required.
|
||||||
|
if ex.internalPidFile != "" {
|
||||||
|
pidStr := []byte(strconv.Itoa(int(pid)))
|
||||||
|
if err := ioutil.WriteFile(ex.internalPidFile, pidStr, 0644); err != nil {
|
||||||
|
Fatalf("error writing internal pid file %q: %v", ex.internalPidFile, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the process to exit.
|
||||||
|
ws, err := c.WaitPID(pid)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("error waiting on pid %d: %v", pid, err)
|
||||||
|
}
|
||||||
*waitStatus = ws
|
*waitStatus = ws
|
||||||
return subcommands.ExitSuccess
|
return subcommands.ExitSuccess
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,13 +353,14 @@ func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocke
|
||||||
return c.Wait()
|
return c.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs the specified command in the container.
|
// Execute runs the specified command in the container. It returns the pid of
|
||||||
func (c *Container) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) {
|
// the newly created process.
|
||||||
log.Debugf("Execute in container %q, args: %+v", c.ID, e)
|
func (c *Container) Execute(args *control.ExecArgs) (int32, error) {
|
||||||
|
log.Debugf("Execute in container %q, args: %+v", c.ID, args)
|
||||||
if c.Status != Created && c.Status != Running {
|
if c.Status != Created && c.Status != Running {
|
||||||
return 0, fmt.Errorf("cannot exec in container in state %s", c.Status)
|
return 0, fmt.Errorf("cannot exec in container in state %s", c.Status)
|
||||||
}
|
}
|
||||||
return c.Sandbox.Execute(c.ID, e)
|
return c.Sandbox.Execute(c.ID, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event returns events for the container.
|
// Event returns events for the container.
|
||||||
|
|
|
@ -49,11 +49,11 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitForProcessList waits for the given process list to show up in the container.
|
// waitForProcessList waits for the given process list to show up in the container.
|
||||||
func waitForProcessList(s *Container, expected []*control.Process) error {
|
func waitForProcessList(cont *Container, expected []*control.Process) error {
|
||||||
var got []*control.Process
|
var got []*control.Process
|
||||||
for start := time.Now(); time.Now().Sub(start) < 10*time.Second; {
|
for start := time.Now(); time.Now().Sub(start) < 10*time.Second; {
|
||||||
var err error
|
var err error
|
||||||
got, err = s.Processes()
|
got, err = cont.Processes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting process data from container: %v", err)
|
return fmt.Errorf("error getting process data from container: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -485,12 +485,12 @@ func TestExec(t *testing.T) {
|
||||||
defer os.RemoveAll(bundleDir)
|
defer os.RemoveAll(bundleDir)
|
||||||
|
|
||||||
// Create and start the container.
|
// Create and start the container.
|
||||||
s, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "")
|
cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating container: %v", err)
|
t.Fatalf("error creating container: %v", err)
|
||||||
}
|
}
|
||||||
defer s.Destroy()
|
defer cont.Destroy()
|
||||||
if err := s.Start(conf); err != nil {
|
if err := cont.Start(conf); err != nil {
|
||||||
t.Fatalf("error starting container: %v", err)
|
t.Fatalf("error starting container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,11 +513,11 @@ func TestExec(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that "sleep 100" is running.
|
// Verify that "sleep 100" is running.
|
||||||
if err := waitForProcessList(s, expectedPL[:1]); err != nil {
|
if err := waitForProcessList(cont, expectedPL[:1]); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
execArgs := control.ExecArgs{
|
args := &control.ExecArgs{
|
||||||
Filename: "/bin/sleep",
|
Filename: "/bin/sleep",
|
||||||
Argv: []string{"sleep", "5"},
|
Argv: []string{"sleep", "5"},
|
||||||
WorkingDirectory: "/",
|
WorkingDirectory: "/",
|
||||||
|
@ -528,17 +528,19 @@ func TestExec(t *testing.T) {
|
||||||
// First, start running exec (whick blocks).
|
// First, start running exec (whick blocks).
|
||||||
status := make(chan error, 1)
|
status := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
exitStatus, err := s.Execute(&execArgs)
|
exitStatus, err := cont.executeSync(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("error executing: %v", err)
|
||||||
status <- err
|
status <- err
|
||||||
} else if exitStatus != 0 {
|
} else if exitStatus != 0 {
|
||||||
|
log.Debugf("bad status: %d", exitStatus)
|
||||||
status <- fmt.Errorf("failed with exit status: %v", exitStatus)
|
status <- fmt.Errorf("failed with exit status: %v", exitStatus)
|
||||||
} else {
|
} else {
|
||||||
status <- nil
|
status <- nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := waitForProcessList(s, expectedPL); err != nil {
|
if err := waitForProcessList(cont, expectedPL); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,7 +550,7 @@ func TestExec(t *testing.T) {
|
||||||
t.Fatalf("container timed out waiting for exec to finish.")
|
t.Fatalf("container timed out waiting for exec to finish.")
|
||||||
case st := <-status:
|
case st := <-status:
|
||||||
if st != nil {
|
if st != nil {
|
||||||
t.Errorf("container failed to exec %v: %v", execArgs, err)
|
t.Errorf("container failed to exec %v: %v", args, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -884,15 +886,18 @@ func TestPauseResume(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
script := fmt.Sprintf("while [[ -f %q ]]; do sleep 0.1; done", lock.Name())
|
script := fmt.Sprintf("while [[ -f %q ]]; do sleep 0.1; done", lock.Name())
|
||||||
execArgs := control.ExecArgs{
|
args := &control.ExecArgs{
|
||||||
Filename: "/bin/bash",
|
Filename: "/bin/bash",
|
||||||
Argv: []string{"bash", "-c", script},
|
Argv: []string{"bash", "-c", script},
|
||||||
WorkingDirectory: "/",
|
WorkingDirectory: "/",
|
||||||
KUID: uid,
|
KUID: uid,
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, start running exec (which blocks).
|
// First, start running exec.
|
||||||
go cont.Execute(&execArgs)
|
_, err = cont.Execute(args)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error executing: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that "sleep 5" is running.
|
// Verify that "sleep 5" is running.
|
||||||
if err := waitForProcessList(cont, expectedPL); err != nil {
|
if err := waitForProcessList(cont, expectedPL); err != nil {
|
||||||
|
@ -1022,12 +1027,12 @@ func TestCapabilities(t *testing.T) {
|
||||||
defer os.RemoveAll(bundleDir)
|
defer os.RemoveAll(bundleDir)
|
||||||
|
|
||||||
// Create and start the container.
|
// Create and start the container.
|
||||||
s, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "")
|
cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating container: %v", err)
|
t.Fatalf("error creating container: %v", err)
|
||||||
}
|
}
|
||||||
defer s.Destroy()
|
defer cont.Destroy()
|
||||||
if err := s.Start(conf); err != nil {
|
if err := cont.Start(conf); err != nil {
|
||||||
t.Fatalf("error starting container: %v", err)
|
t.Fatalf("error starting container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,7 +1053,7 @@ func TestCapabilities(t *testing.T) {
|
||||||
Cmd: "exe",
|
Cmd: "exe",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := waitForProcessList(s, expectedPL[:1]); err != nil {
|
if err := waitForProcessList(cont, expectedPL[:1]); err != nil {
|
||||||
t.Fatalf("Failed to wait for sleep to start, err: %v", err)
|
t.Fatalf("Failed to wait for sleep to start, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1064,7 +1069,7 @@ func TestCapabilities(t *testing.T) {
|
||||||
// Need to traverse the intermediate directory.
|
// Need to traverse the intermediate directory.
|
||||||
os.Chmod(rootDir, 0755)
|
os.Chmod(rootDir, 0755)
|
||||||
|
|
||||||
execArgs := control.ExecArgs{
|
args := &control.ExecArgs{
|
||||||
Filename: exePath,
|
Filename: exePath,
|
||||||
Argv: []string{exePath},
|
Argv: []string{exePath},
|
||||||
WorkingDirectory: "/",
|
WorkingDirectory: "/",
|
||||||
|
@ -1074,17 +1079,17 @@ func TestCapabilities(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// "exe" should fail because we don't have the necessary permissions.
|
// "exe" should fail because we don't have the necessary permissions.
|
||||||
if _, err := s.Execute(&execArgs); err == nil {
|
if _, err := cont.executeSync(args); err == nil {
|
||||||
t.Fatalf("container executed without error, but an error was expected")
|
t.Fatalf("container executed without error, but an error was expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we run with the capability enabled and should succeed.
|
// Now we run with the capability enabled and should succeed.
|
||||||
execArgs.Capabilities = &auth.TaskCapabilities{
|
args.Capabilities = &auth.TaskCapabilities{
|
||||||
EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
||||||
}
|
}
|
||||||
// "exe" should not fail this time.
|
// "exe" should not fail this time.
|
||||||
if _, err := s.Execute(&execArgs); err != nil {
|
if _, err := cont.executeSync(args); err != nil {
|
||||||
t.Fatalf("container failed to exec %v: %v", execArgs, err)
|
t.Fatalf("container failed to exec %v: %v", args, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1404,11 +1409,11 @@ func TestContainerVolumeContentsShared(t *testing.T) {
|
||||||
filename := filepath.Join(dir, "file")
|
filename := filepath.Join(dir, "file")
|
||||||
|
|
||||||
// File does not exist yet. Reading from the sandbox should fail.
|
// File does not exist yet. Reading from the sandbox should fail.
|
||||||
execArgsTestFile := control.ExecArgs{
|
argsTestFile := &control.ExecArgs{
|
||||||
Filename: "/usr/bin/test",
|
Filename: "/usr/bin/test",
|
||||||
Argv: []string{"test", "-f", filename},
|
Argv: []string{"test", "-f", filename},
|
||||||
}
|
}
|
||||||
if ws, err := c.Execute(&execArgsTestFile); err != nil {
|
if ws, err := c.executeSync(argsTestFile); err != nil {
|
||||||
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
||||||
} else if ws.ExitStatus() == 0 {
|
} else if ws.ExitStatus() == 0 {
|
||||||
t.Errorf("test %q exited with code %v, wanted not zero", ws.ExitStatus(), err)
|
t.Errorf("test %q exited with code %v, wanted not zero", ws.ExitStatus(), err)
|
||||||
|
@ -1420,7 +1425,7 @@ func TestContainerVolumeContentsShared(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we should be able to test the file from within the sandbox.
|
// Now we should be able to test the file from within the sandbox.
|
||||||
if ws, err := c.Execute(&execArgsTestFile); err != nil {
|
if ws, err := c.executeSync(argsTestFile); err != nil {
|
||||||
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
||||||
} else if ws.ExitStatus() != 0 {
|
} else if ws.ExitStatus() != 0 {
|
||||||
t.Errorf("test %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
t.Errorf("test %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
||||||
|
@ -1433,18 +1438,18 @@ func TestContainerVolumeContentsShared(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// File should no longer exist at the old path within the sandbox.
|
// File should no longer exist at the old path within the sandbox.
|
||||||
if ws, err := c.Execute(&execArgsTestFile); err != nil {
|
if ws, err := c.executeSync(argsTestFile); err != nil {
|
||||||
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
t.Fatalf("unexpected error testing file %q: %v", filename, err)
|
||||||
} else if ws.ExitStatus() == 0 {
|
} else if ws.ExitStatus() == 0 {
|
||||||
t.Errorf("test %q exited with code %v, wanted not zero", filename, ws.ExitStatus())
|
t.Errorf("test %q exited with code %v, wanted not zero", filename, ws.ExitStatus())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should be able to test the new filename from within the sandbox.
|
// We should be able to test the new filename from within the sandbox.
|
||||||
execArgsTestNewFile := control.ExecArgs{
|
argsTestNewFile := &control.ExecArgs{
|
||||||
Filename: "/usr/bin/test",
|
Filename: "/usr/bin/test",
|
||||||
Argv: []string{"test", "-f", newFilename},
|
Argv: []string{"test", "-f", newFilename},
|
||||||
}
|
}
|
||||||
if ws, err := c.Execute(&execArgsTestNewFile); err != nil {
|
if ws, err := c.executeSync(argsTestNewFile); err != nil {
|
||||||
t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
|
t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
|
||||||
} else if ws.ExitStatus() != 0 {
|
} else if ws.ExitStatus() != 0 {
|
||||||
t.Errorf("test %q exited with code %v, wanted zero", newFilename, ws.ExitStatus())
|
t.Errorf("test %q exited with code %v, wanted zero", newFilename, ws.ExitStatus())
|
||||||
|
@ -1456,20 +1461,20 @@ func TestContainerVolumeContentsShared(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renamed file should no longer exist at the old path within the sandbox.
|
// Renamed file should no longer exist at the old path within the sandbox.
|
||||||
if ws, err := c.Execute(&execArgsTestNewFile); err != nil {
|
if ws, err := c.executeSync(argsTestNewFile); err != nil {
|
||||||
t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
|
t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
|
||||||
} else if ws.ExitStatus() == 0 {
|
} else if ws.ExitStatus() == 0 {
|
||||||
t.Errorf("test %q exited with code %v, wanted not zero", newFilename, ws.ExitStatus())
|
t.Errorf("test %q exited with code %v, wanted not zero", newFilename, ws.ExitStatus())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now create the file from WITHIN the sandbox.
|
// Now create the file from WITHIN the sandbox.
|
||||||
execArgsTouch := control.ExecArgs{
|
argsTouch := &control.ExecArgs{
|
||||||
Filename: "/usr/bin/touch",
|
Filename: "/usr/bin/touch",
|
||||||
Argv: []string{"touch", filename},
|
Argv: []string{"touch", filename},
|
||||||
KUID: auth.KUID(os.Getuid()),
|
KUID: auth.KUID(os.Getuid()),
|
||||||
KGID: auth.KGID(os.Getgid()),
|
KGID: auth.KGID(os.Getgid()),
|
||||||
}
|
}
|
||||||
if ws, err := c.Execute(&execArgsTouch); err != nil {
|
if ws, err := c.executeSync(argsTouch); err != nil {
|
||||||
t.Fatalf("unexpected error touching file %q: %v", filename, err)
|
t.Fatalf("unexpected error touching file %q: %v", filename, err)
|
||||||
} else if ws.ExitStatus() != 0 {
|
} else if ws.ExitStatus() != 0 {
|
||||||
t.Errorf("touch %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
t.Errorf("touch %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
||||||
|
@ -1486,11 +1491,11 @@ func TestContainerVolumeContentsShared(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the file from within the sandbox.
|
// Delete the file from within the sandbox.
|
||||||
execArgsRemove := control.ExecArgs{
|
argsRemove := &control.ExecArgs{
|
||||||
Filename: "/bin/rm",
|
Filename: "/bin/rm",
|
||||||
Argv: []string{"rm", filename},
|
Argv: []string{"rm", filename},
|
||||||
}
|
}
|
||||||
if ws, err := c.Execute(&execArgsRemove); err != nil {
|
if ws, err := c.executeSync(argsRemove); err != nil {
|
||||||
t.Fatalf("unexpected error removing file %q: %v", filename, err)
|
t.Fatalf("unexpected error removing file %q: %v", filename, err)
|
||||||
} else if ws.ExitStatus() != 0 {
|
} else if ws.ExitStatus() != 0 {
|
||||||
t.Errorf("remove %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
t.Errorf("remove %q exited with code %v, wanted zero", filename, ws.ExitStatus())
|
||||||
|
@ -1547,6 +1552,19 @@ func TestGoferExits(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// executeSync synchronously executes a new process.
|
||||||
|
func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus, error) {
|
||||||
|
pid, err := cont.Execute(args)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("error executing: %v", err)
|
||||||
|
}
|
||||||
|
ws, err := cont.WaitPID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("error waiting: %v", err)
|
||||||
|
}
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
testutil.RunAsRoot(m)
|
testutil.RunAsRoot(m)
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,8 +187,9 @@ func (s *Sandbox) Processes(cid string) ([]*control.Process, error) {
|
||||||
return pl, nil
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs the specified command in the container.
|
// Execute runs the specified command in the container. It returns the pid of
|
||||||
func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus, error) {
|
// the newly created process.
|
||||||
|
func (s *Sandbox) Execute(cid string, args *control.ExecArgs) (int32, error) {
|
||||||
log.Debugf("Executing new process in container %q in sandbox %q", cid, s.ID)
|
log.Debugf("Executing new process in container %q in sandbox %q", cid, s.ID)
|
||||||
conn, err := s.sandboxConnect()
|
conn, err := s.sandboxConnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -196,20 +197,14 @@ func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus,
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
ea := &boot.ExecArgs{
|
rpcArgs := &boot.ExecArgs{ExecArgs: *args, CID: cid}
|
||||||
ExecArgs: *e,
|
|
||||||
CID: cid,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a message to the sandbox control server to start the container.
|
// Send a message to the sandbox control server to start the container.
|
||||||
var waitStatus uint32
|
var pid int32
|
||||||
// TODO: Pass in the container id (cid) here. The sandbox
|
if err := conn.Call(boot.ContainerExecuteAsync, rpcArgs, &pid); err != nil {
|
||||||
// should execute in the context of that container.
|
|
||||||
if err := conn.Call(boot.ContainerExecute, ea, &waitStatus); err != nil {
|
|
||||||
return 0, fmt.Errorf("error executing in sandbox: %v", err)
|
return 0, fmt.Errorf("error executing in sandbox: %v", err)
|
||||||
}
|
}
|
||||||
|
return pid, nil
|
||||||
return syscall.WaitStatus(waitStatus), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event retrieves stats about the sandbox such as memory and CPU utilization.
|
// Event retrieves stats about the sandbox such as memory and CPU utilization.
|
||||||
|
|
Loading…
Reference in New Issue