2017-01-12 18:29:37 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-02-13 18:23:28 +00:00
|
|
|
"net"
|
2017-01-12 18:29:37 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2017-01-26 19:29:19 +00:00
|
|
|
"strings"
|
2017-01-12 18:29:37 +00:00
|
|
|
"syscall"
|
|
|
|
|
2017-01-19 22:16:50 +00:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
2017-01-24 23:03:04 +00:00
|
|
|
"github.com/Sirupsen/logrus"
|
2017-04-03 20:14:15 +00:00
|
|
|
"github.com/containerd/containerd"
|
|
|
|
shimapi "github.com/containerd/containerd/api/services/shim"
|
|
|
|
"github.com/containerd/containerd/linux/shim"
|
|
|
|
"github.com/containerd/containerd/reaper"
|
|
|
|
"github.com/containerd/containerd/sys"
|
2017-03-10 00:11:57 +00:00
|
|
|
runc "github.com/crosbymichael/go-runc"
|
2017-01-19 22:16:50 +00:00
|
|
|
"github.com/urfave/cli"
|
2017-01-12 18:29:37 +00:00
|
|
|
)
|
|
|
|
|
2017-01-19 22:16:50 +00:00
|
|
|
const usage = `
|
|
|
|
__ _ __ __ _
|
|
|
|
_________ ____ / /_____ _(_)___ ___ _________/ / _____/ /_ (_)___ ___
|
|
|
|
/ ___/ __ \/ __ \/ __/ __ ` + "`" + `/ / __ \/ _ \/ ___/ __ /_____/ ___/ __ \/ / __ ` + "`" + `__ \
|
|
|
|
/ /__/ /_/ / / / / /_/ /_/ / / / / / __/ / / /_/ /_____(__ ) / / / / / / / / /
|
|
|
|
\___/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \__,_/ /____/_/ /_/_/_/ /_/ /_/
|
|
|
|
|
|
|
|
shim for container lifecycle and reconnection
|
|
|
|
`
|
2017-01-12 18:29:37 +00:00
|
|
|
|
|
|
|
func main() {
|
2017-01-19 22:16:50 +00:00
|
|
|
app := cli.NewApp()
|
|
|
|
app.Name = "containerd-shim"
|
|
|
|
app.Version = containerd.Version
|
|
|
|
app.Usage = usage
|
|
|
|
app.Flags = []cli.Flag{
|
|
|
|
cli.BoolFlag{
|
|
|
|
Name: "debug",
|
|
|
|
Usage: "enable debug output in logs",
|
|
|
|
},
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
app.Before = func(context *cli.Context) error {
|
|
|
|
if context.GlobalBool("debug") {
|
|
|
|
logrus.SetLevel(logrus.DebugLevel)
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
return nil
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
app.Action = func(context *cli.Context) error {
|
|
|
|
// start handling signals as soon as possible so that things are properly reaped
|
|
|
|
// or if runtime exits before we hit the handler
|
|
|
|
signals, err := setupSignals()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-02-13 18:23:28 +00:00
|
|
|
if err := setupRoot(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
path, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
var (
|
|
|
|
server = grpc.NewServer()
|
2017-03-10 23:28:21 +00:00
|
|
|
sv = shim.New(path)
|
2017-01-19 22:16:50 +00:00
|
|
|
)
|
2017-01-25 21:27:48 +00:00
|
|
|
logrus.Debug("registering grpc server")
|
2017-02-09 07:31:26 +00:00
|
|
|
shimapi.RegisterShimServer(server, sv)
|
2017-01-25 21:27:48 +00:00
|
|
|
if err := serve(server, "shim.sock"); err != nil {
|
2017-01-19 22:16:50 +00:00
|
|
|
return err
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
return handleSignals(signals, server)
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
if err := app.Run(os.Args); err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "containerd-shim: %s\n", err)
|
|
|
|
os.Exit(1)
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 22:16:50 +00:00
|
|
|
// setupSignals creates a new signal handler for all signals and sets the shim as a
|
|
|
|
// sub-reaper so that the container processes are reparented
|
|
|
|
func setupSignals() (chan os.Signal, error) {
|
|
|
|
signals := make(chan os.Signal, 2048)
|
|
|
|
signal.Notify(signals)
|
2017-03-10 00:11:57 +00:00
|
|
|
// make sure runc is setup to use the monitor
|
|
|
|
// for waiting on processes
|
|
|
|
runc.Monitor = reaper.Default
|
2017-01-19 22:16:50 +00:00
|
|
|
// set the shim as the subreaper for all orphaned processes created by the container
|
|
|
|
if err := sys.SetSubreaper(1); err != nil {
|
|
|
|
return nil, err
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-19 22:16:50 +00:00
|
|
|
return signals, nil
|
2017-01-12 18:29:37 +00:00
|
|
|
}
|
2017-01-25 21:27:48 +00:00
|
|
|
|
|
|
|
// serve serves the grpc API over a unix socket at the provided path
|
|
|
|
// this function does not block
|
|
|
|
func serve(server *grpc.Server, path string) error {
|
2017-02-13 18:23:28 +00:00
|
|
|
l, err := net.FileListener(os.NewFile(3, "socket"))
|
2017-01-25 21:27:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logrus.WithField("socket", path).Debug("serving api on unix socket")
|
|
|
|
go func() {
|
|
|
|
defer l.Close()
|
2017-01-26 19:29:19 +00:00
|
|
|
if err := server.Serve(l); err != nil &&
|
|
|
|
!strings.Contains(err.Error(), "use of closed network connection") {
|
2017-01-25 21:27:48 +00:00
|
|
|
logrus.WithError(err).Fatal("containerd-shim: GRPC server failure")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-10 23:28:21 +00:00
|
|
|
func handleSignals(signals chan os.Signal, server *grpc.Server) error {
|
2017-01-25 21:27:48 +00:00
|
|
|
for s := range signals {
|
|
|
|
logrus.WithField("signal", s).Debug("received signal")
|
|
|
|
switch s {
|
|
|
|
case syscall.SIGCHLD:
|
2017-03-10 23:28:21 +00:00
|
|
|
if err := reaper.Reap(); err != nil {
|
2017-01-25 21:27:48 +00:00
|
|
|
logrus.WithError(err).Error("reap exit status")
|
|
|
|
}
|
|
|
|
case syscall.SIGTERM, syscall.SIGINT:
|
|
|
|
// TODO: should we forward signals to the processes if they are still running?
|
|
|
|
// i.e. machine reboot
|
2017-01-26 19:29:19 +00:00
|
|
|
server.Stop()
|
2017-01-25 21:27:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-02-13 18:23:28 +00:00
|
|
|
|
|
|
|
// setupRoot sets up the root as the shim is started in its own mount namespace
|
|
|
|
func setupRoot() error {
|
|
|
|
return syscall.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, "")
|
|
|
|
}
|