129 lines
2.8 KiB
Go
129 lines
2.8 KiB
Go
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),
|
|
},
|
|
},
|
|
}
|
|
}
|