Add ctr exec
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
7715ddcefa
commit
47225c130c
7 changed files with 159 additions and 19 deletions
128
cmd/ctr/exec.go
Normal file
128
cmd/ctr/exec.go
Normal 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),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ containerd client
|
|||
killCommand,
|
||||
shimCommand,
|
||||
pprofCommand,
|
||||
execCommand,
|
||||
}
|
||||
app.Before = func(context *cli.Context) error {
|
||||
if context.GlobalBool("debug") {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue