This moves the shim's API and protos out of the containerd services package and into the linux runtime package. This is because the shim is an implementation detail of the linux runtime that we have and it is not a containerd user facing api. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
313 lines
6.9 KiB
Go
313 lines
6.9 KiB
Go
// +build !windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
gocontext "context"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/grpclog"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/containerd/console"
|
|
shim "github.com/containerd/containerd/linux/shim/v1"
|
|
protobuf "github.com/gogo/protobuf/types"
|
|
google_protobuf "github.com/golang/protobuf/ptypes/empty"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var empty = &google_protobuf.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",
|
|
},
|
|
}
|
|
|
|
var shimCommand = cli.Command{
|
|
Name: "shim",
|
|
Usage: "interact with a shim directly",
|
|
Subcommands: []cli.Command{
|
|
shimCreateCommand,
|
|
shimStartCommand,
|
|
shimDeleteCommand,
|
|
shimEventsCommand,
|
|
shimStateCommand,
|
|
shimExecCommand,
|
|
},
|
|
}
|
|
|
|
var shimCreateCommand = cli.Command{
|
|
Name: "create",
|
|
Usage: "create a container with a shim",
|
|
Flags: append(fifoFlags,
|
|
cli.StringFlag{
|
|
Name: "bundle",
|
|
Usage: "bundle path for the container",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "runtime",
|
|
Value: "runc",
|
|
Usage: "runtime to use for the container",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "attach,a",
|
|
Usage: "stay attached to the container and open the fifos",
|
|
},
|
|
),
|
|
Action: func(context *cli.Context) error {
|
|
var (
|
|
id = context.Args().First()
|
|
ctx = gocontext.Background()
|
|
)
|
|
|
|
if id == "" {
|
|
return errors.New("container id must be provided")
|
|
}
|
|
service, err := getShimService()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tty := context.Bool("tty")
|
|
wg, err := prepareStdio(context.String("stdin"), context.String("stdout"), context.String("stderr"), tty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r, err := service.Create(ctx, &shim.CreateTaskRequest{
|
|
ID: id,
|
|
Bundle: context.String("bundle"),
|
|
Runtime: context.String("runtime"),
|
|
Stdin: context.String("stdin"),
|
|
Stdout: context.String("stdout"),
|
|
Stderr: context.String("stderr"),
|
|
Terminal: tty,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("container created with id %s and pid %d\n", id, r.Pid)
|
|
if context.Bool("attach") {
|
|
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{
|
|
Pid: r.Pid,
|
|
Width: uint32(size.Width),
|
|
Height: uint32(size.Height),
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
wg.Wait()
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var shimStartCommand = cli.Command{
|
|
Name: "start",
|
|
Usage: "start a container with a shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = service.Start(gocontext.Background(), empty)
|
|
return err
|
|
},
|
|
}
|
|
|
|
var shimDeleteCommand = cli.Command{
|
|
Name: "delete",
|
|
Usage: "delete a container with a shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService()
|
|
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 shimStateCommand = cli.Command{
|
|
Name: "state",
|
|
Usage: "get the state of all the processes of the shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r, err := service.State(gocontext.Background(), empty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data, err := json.Marshal(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buf := bytes.NewBuffer(nil)
|
|
if err := json.Indent(buf, data, " ", " "); err != nil {
|
|
return err
|
|
}
|
|
buf.WriteTo(os.Stdout)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var shimExecCommand = 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()
|
|
ctx := gocontext.Background()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
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
|
|
}
|
|
|
|
rq := &shim.ExecProcessRequest{
|
|
Spec: &protobuf.Any{
|
|
TypeUrl: specs.Version,
|
|
Value: spec,
|
|
},
|
|
Stdin: context.String("stdin"),
|
|
Stdout: context.String("stdout"),
|
|
Stderr: context.String("stderr"),
|
|
Terminal: tty,
|
|
}
|
|
r, err := service.Exec(ctx, rq)
|
|
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{
|
|
Pid: r.Pid,
|
|
Width: uint32(size.Width),
|
|
Height: uint32(size.Height),
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
wg.Wait()
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var shimEventsCommand = cli.Command{
|
|
Name: "events",
|
|
Usage: "get events for a shim",
|
|
Action: func(context *cli.Context) error {
|
|
service, err := getShimService()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
events, err := service.Stream(gocontext.Background(), &shim.StreamEventsRequest{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
e, err := events.Recv()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("type=%s id=%s pid=%d status=%d\n", e.Type, e.ID, e.Pid, e.ExitStatus)
|
|
}
|
|
},
|
|
}
|
|
|
|
func getShimService() (shim.ShimClient, error) {
|
|
bindSocket := "shim.sock"
|
|
|
|
// reset the logger for grpc to log to dev/null so that it does not mess with our stdio
|
|
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
|
|
dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)}
|
|
dialOpts = append(dialOpts,
|
|
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
|
return net.DialTimeout("unix", bindSocket, timeout)
|
|
},
|
|
))
|
|
conn, err := grpc.Dial(fmt.Sprintf("unix://%s", bindSocket), dialOpts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return shim.NewShimClient(conn), nil
|
|
}
|