
By replacing grpc with ttrpc, we can reduce total memory runtime requirements and binary size. With minimal code changes, the shim can now be controlled by the much lightweight protocol, reducing the total memory required per container. When reviewing this change, take particular notice of the generated shim code. Signed-off-by: Stephen J Day <stephen.day@docker.com>
231 lines
4.9 KiB
Go
231 lines
4.9 KiB
Go
// +build !windows
|
|
|
|
package shim
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
|
|
gocontext "context"
|
|
|
|
"github.com/containerd/console"
|
|
"github.com/containerd/containerd/cmd/ctr/commands"
|
|
shim "github.com/containerd/containerd/linux/shim/v1"
|
|
"github.com/containerd/typeurl"
|
|
ptypes "github.com/gogo/protobuf/types"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stevvooe/ttrpc"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var empty = &ptypes.Empty{}
|
|
|
|
var fifoFlags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "stdin",
|
|
Usage: "specify the path to the stdin fifo",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "stdout",
|
|
Usage: "specify the path to the stdout fifo",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "stderr",
|
|
Usage: "specify the path to the stderr fifo",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "tty,t",
|
|
Usage: "enable tty support",
|
|
},
|
|
}
|
|
|
|
// Command is the cli command for interacting with a shim
|
|
var Command = cli.Command{
|
|
Name: "shim",
|
|
Usage: "interact with a shim directly",
|
|
Flags: []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "socket",
|
|
Usage: "socket on which to connect to the shim",
|
|
},
|
|
},
|
|
Subcommands: []cli.Command{
|
|
deleteCommand,
|
|
execCommand,
|
|
startCommand,
|
|
stateCommand,
|
|
},
|
|
}
|
|
|
|
var startCommand = cli.Command{
|
|
Name: "start",
|
|
Usage: "start a container with a shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = service.Start(gocontext.Background(), &shim.StartRequest{
|
|
ID: context.Args().First(),
|
|
})
|
|
return err
|
|
},
|
|
}
|
|
|
|
var deleteCommand = cli.Command{
|
|
Name: "delete",
|
|
Usage: "delete a container with a shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r, err := service.Delete(gocontext.Background(), empty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("container deleted and returned exit status %d\n", r.ExitStatus)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var stateCommand = cli.Command{
|
|
Name: "state",
|
|
Usage: "get the state of all the processes of the shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r, err := service.State(gocontext.Background(), &shim.StateRequest{
|
|
ID: context.Args().First(),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
commands.PrintAsJSON(r)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var execCommand = cli.Command{
|
|
Name: "exec",
|
|
Usage: "exec a new process in the shim's container",
|
|
Flags: append(fifoFlags,
|
|
cli.BoolFlag{
|
|
Name: "attach,a",
|
|
Usage: "stay attached to the container and open the fifos",
|
|
},
|
|
cli.StringSliceFlag{
|
|
Name: "env,e",
|
|
Usage: "add environment vars",
|
|
Value: &cli.StringSlice{},
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cwd",
|
|
Usage: "current working directory",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "spec",
|
|
Usage: "runtime spec",
|
|
},
|
|
),
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var (
|
|
id = context.Args().First()
|
|
ctx = gocontext.Background()
|
|
)
|
|
|
|
if id == "" {
|
|
return errors.New("exec id must be provided")
|
|
}
|
|
|
|
tty := context.Bool("tty")
|
|
wg, err := prepareStdio(context.String("stdin"), context.String("stdout"), context.String("stderr"), tty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// read spec file and extract Any object
|
|
spec, err := ioutil.ReadFile(context.String("spec"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
url, err := typeurl.TypeURL(specs.Process{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rq := &shim.ExecProcessRequest{
|
|
ID: id,
|
|
Spec: &ptypes.Any{
|
|
TypeUrl: url,
|
|
Value: spec,
|
|
},
|
|
Stdin: context.String("stdin"),
|
|
Stdout: context.String("stdout"),
|
|
Stderr: context.String("stderr"),
|
|
Terminal: tty,
|
|
}
|
|
if _, err := service.Exec(ctx, rq); err != nil {
|
|
return err
|
|
}
|
|
r, err := service.Start(ctx, &shim.StartRequest{
|
|
ID: id,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("exec running with pid %d\n", r.Pid)
|
|
if context.Bool("attach") {
|
|
logrus.Info("attaching")
|
|
if tty {
|
|
current := console.Current()
|
|
defer current.Reset()
|
|
if err := current.SetRaw(); err != nil {
|
|
return err
|
|
}
|
|
size, err := current.Size()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := service.ResizePty(ctx, &shim.ResizePtyRequest{
|
|
ID: id,
|
|
Width: uint32(size.Width),
|
|
Height: uint32(size.Height),
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
wg.Wait()
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func getShimService(context *cli.Context) (shim.ShimService, error) {
|
|
bindSocket := context.GlobalString("socket")
|
|
if bindSocket == "" {
|
|
return nil, errors.New("socket path must be specified")
|
|
}
|
|
|
|
conn, err := net.Dial("unix", "\x00"+bindSocket)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := ttrpc.NewClient(conn)
|
|
|
|
// TODO(stevvooe): This actually leaks the connection. We were leaking it
|
|
// before, so may not be a huge deal.
|
|
|
|
return shim.NewShimClient(client), nil
|
|
}
|