Add ctr exec

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2017-04-05 13:33:45 -07:00
parent 7715ddcefa
commit 47225c130c
7 changed files with 159 additions and 19 deletions

128
cmd/ctr/exec.go Normal file
View file

@ -0,0 +1,128 @@
package main
import (
gocontext "context"
"encoding/json"
"os"
"path/filepath"
"github.com/containerd/containerd/api/services/execution"
"github.com/crosbymichael/console"
protobuf "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
)
var execCommand = cli.Command{
Name: "exec",
Usage: "execute additional processes in an existing container",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "id of the container",
},
cli.BoolFlag{
Name: "tty,t",
Usage: "allocate a TTY for the container",
},
},
Action: func(context *cli.Context) error {
var (
id = context.String("id")
ctx = gocontext.Background()
)
process := createProcess(context.Args(), "", context.Bool("tty"))
data, err := json.Marshal(process)
if err != nil {
return err
}
containers, err := getExecutionService(context)
if err != nil {
return err
}
events, err := containers.Events(ctx, &execution.EventsRequest{})
if err != nil {
return err
}
tmpDir, err := getTempDir(id)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
request := &execution.ExecRequest{
ID: id,
Spec: &protobuf.Any{
TypeUrl: specs.Version,
Value: data,
},
Terminal: context.Bool("tty"),
Stdin: filepath.Join(tmpDir, "stdin"),
Stdout: filepath.Join(tmpDir, "stdout"),
Stderr: filepath.Join(tmpDir, "stderr"),
}
if request.Terminal {
con := console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
fwg, err := prepareStdio(request.Stdin, request.Stdout, request.Stderr, request.Terminal)
if err != nil {
return err
}
response, err := containers.Exec(ctx, request)
if err != nil {
return err
}
// Ensure we read all io only if container started successfully.
defer fwg.Wait()
status, err := waitContainer(events, id, response.Pid)
if err != nil {
return err
}
if status != 0 {
return cli.NewExitError("", int(status))
}
return nil
},
}
func createProcess(args []string, cwd string, tty bool) specs.Process {
env := []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
}
if tty {
env = append(env, "TERM=xterm")
}
if cwd == "" {
cwd = "/"
}
return specs.Process{
Args: args,
Env: env,
Terminal: tty,
Cwd: cwd,
NoNewPrivileges: true,
User: specs.User{
UID: 0,
GID: 0,
},
Capabilities: &specs.LinuxCapabilities{
Bounding: capabilities,
Permitted: capabilities,
Inheritable: capabilities,
Effective: capabilities,
Ambient: capabilities,
},
Rlimits: []specs.LinuxRlimit{
{
Type: "RLIMIT_NOFILE",
Hard: uint64(1024),
Soft: uint64(1024),
},
},
}
}

View file

@ -55,6 +55,7 @@ containerd client
killCommand,
shimCommand,
pprofCommand,
execCommand,
}
app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") {

View file

@ -377,7 +377,7 @@ var runCommand = cli.Command{
// Ensure we read all io only if container started successfully.
defer fwg.Wait()
status, err := waitContainer(events, response)
status, err := waitContainer(events, response.ID, response.Pid)
if err != nil {
return err
}

View file

@ -155,7 +155,7 @@ func getTempDir(id string) (string, error) {
return tmpDir, nil
}
func waitContainer(events execution.ContainerService_EventsClient, response *execution.CreateResponse) (uint32, error) {
func waitContainer(events execution.ContainerService_EventsClient, id string, pid uint32) (uint32, error) {
for {
e, err := events.Recv()
if err != nil {
@ -164,8 +164,7 @@ func waitContainer(events execution.ContainerService_EventsClient, response *exe
if e.Type != container.Event_EXIT {
continue
}
if e.ID == response.ID &&
e.Pid == response.Pid {
if e.ID == id && e.Pid == pid {
return e.ExitStatus, nil
}
}