Update shim for exec
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
6808dbc02f
commit
835f3b6a97
37 changed files with 786 additions and 709 deletions
|
@ -1,43 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/util"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
const (
|
||||
bufferSize = 2048
|
||||
)
|
||||
var fexec bool
|
||||
|
||||
type stdio struct {
|
||||
stdin *os.File
|
||||
stdout *os.File
|
||||
stderr *os.File
|
||||
console string
|
||||
}
|
||||
|
||||
func (s *stdio) Close() error {
|
||||
err := s.stdin.Close()
|
||||
if oerr := s.stdout.Close(); err == nil {
|
||||
err = oerr
|
||||
}
|
||||
if oerr := s.stderr.Close(); err == nil {
|
||||
err = oerr
|
||||
}
|
||||
return err
|
||||
func init() {
|
||||
flag.BoolVar(&fexec, "exec", false, "exec a process instead of starting the init")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// containerd-shim is a small shim that sits in front of a runc implementation
|
||||
|
@ -45,41 +23,27 @@ func (s *stdio) Close() error {
|
|||
//
|
||||
// the cwd of the shim should be the bundle for the container. Arg1 should be the path
|
||||
// to the state directory where the shim can locate fifos and other information.
|
||||
//
|
||||
// └── shim
|
||||
// ├── control
|
||||
// ├── stderr
|
||||
// ├── stdin
|
||||
// ├── stdout
|
||||
// ├── pid
|
||||
// └── exit
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
logrus.Fatal("shim: no arguments provided")
|
||||
}
|
||||
// start handling signals as soon as possible so that things are properly reaped
|
||||
// or if runc exits before we hit the handler
|
||||
signals := make(chan os.Signal, bufferSize)
|
||||
signals := make(chan os.Signal, 2048)
|
||||
signal.Notify(signals)
|
||||
// set the shim as the subreaper for all orphaned processes created by the container
|
||||
if err := util.SetSubreaper(1); err != nil {
|
||||
logrus.WithField("error", err).Fatal("shim: set as subreaper")
|
||||
}
|
||||
// open the exit pipe
|
||||
f, err := os.OpenFile(filepath.Join(os.Args[1], "exit"), syscall.O_WRONLY, 0)
|
||||
f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
logrus.WithField("error", err).Fatal("shim: open exit pipe")
|
||||
}
|
||||
defer f.Close()
|
||||
// open the fifos for use with the command
|
||||
std, err := openContainerSTDIO(os.Args[1])
|
||||
p, err := newProcess(flag.Arg(0), flag.Arg(1), fexec)
|
||||
if err != nil {
|
||||
logrus.WithField("error", err).Fatal("shim: open container STDIO from fifo")
|
||||
logrus.WithField("error", err).Fatal("shim: create new process")
|
||||
}
|
||||
// star the container process by invoking runc
|
||||
runcPid, err := startRunc(std, os.Args[2])
|
||||
if err != nil {
|
||||
logrus.WithField("error", err).Fatal("shim: start runc")
|
||||
if err := p.start(); err != nil {
|
||||
logrus.WithField("error", err).Fatal("shim: start process")
|
||||
}
|
||||
var exitShim bool
|
||||
for s := range signals {
|
||||
|
@ -92,22 +56,27 @@ func main() {
|
|||
}
|
||||
for _, e := range exits {
|
||||
// check to see if runc is one of the processes that has exited
|
||||
if e.Pid == runcPid {
|
||||
if e.Pid == p.pid() {
|
||||
exitShim = true
|
||||
logrus.WithFields(logrus.Fields{"pid": e.Pid, "status": e.Status}).Info("shim: runc exited")
|
||||
|
||||
if err := writeInt(filepath.Join(os.Args[1], "exitStatus"), e.Status); err != nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err, "status": e.Status}).Error("shim: write exit status")
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"pid": e.Pid,
|
||||
"status": e.Status,
|
||||
}).Info("shim: runc exited")
|
||||
if err := writeInt("exitStatus", e.Status); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"status": e.Status,
|
||||
}).Error("shim: write exit status")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// runc has exited so the shim can also exit
|
||||
if exitShim {
|
||||
if err := std.Close(); err != nil {
|
||||
if err := p.Close(); err != nil {
|
||||
logrus.WithField("error", err).Error("shim: close stdio")
|
||||
}
|
||||
if err := deleteContainer(os.Args[2]); err != nil {
|
||||
if err := p.delete(); err != nil {
|
||||
logrus.WithField("error", err).Error("shim: delete runc state")
|
||||
}
|
||||
return
|
||||
|
@ -115,90 +84,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// startRunc starts runc detached and returns the container's pid
|
||||
func startRunc(s *stdio, id string) (int, error) {
|
||||
pidFile := filepath.Join(os.Args[1], "pid")
|
||||
cmd := exec.Command("runc", "--id", id, "start", "-d", "--console", s.console, "--pid-file", pidFile)
|
||||
cmd.Stdin = s.stdin
|
||||
cmd.Stdout = s.stdout
|
||||
cmd.Stderr = s.stderr
|
||||
// set the parent death signal to SIGKILL so that if the shim dies the container
|
||||
// process also dies
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Pdeathsig: syscall.SIGKILL,
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
data, err := ioutil.ReadFile(pidFile)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return strconv.Atoi(string(data))
|
||||
}
|
||||
|
||||
func deleteContainer(id string) error {
|
||||
return exec.Command("runc", "--id", id, "delete").Run()
|
||||
}
|
||||
|
||||
// openContainerSTDIO opens the pre-created fifo's for use with the container
|
||||
// in RDWR so that they remain open if the other side stops listening
|
||||
func openContainerSTDIO(dir string) (*stdio, error) {
|
||||
s := &stdio{}
|
||||
spec, err := getSpec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if spec.Process.Terminal {
|
||||
console, err := libcontainer.NewConsole(int(spec.Process.User.UID), int(spec.Process.User.GID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.console = console.Path()
|
||||
stdin, err := os.OpenFile(filepath.Join(dir, "stdin"), syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(console, stdin)
|
||||
}()
|
||||
stdout, err := os.OpenFile(filepath.Join(dir, "stdout"), syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(stdout, console)
|
||||
console.Close()
|
||||
}()
|
||||
return s, nil
|
||||
}
|
||||
for name, dest := range map[string]**os.File{
|
||||
"stdin": &s.stdin,
|
||||
"stdout": &s.stdout,
|
||||
"stderr": &s.stderr,
|
||||
} {
|
||||
f, err := os.OpenFile(filepath.Join(dir, name), syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*dest = f
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getSpec() (*specs.Spec, error) {
|
||||
var s specs.Spec
|
||||
f, err := os.Open("config.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func writeInt(path string, i int) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue