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
0
containerd-shim/example/config.json
Normal file
0
containerd-shim/example/config.json
Normal file
0
containerd-shim/example/init/exit
Normal file
0
containerd-shim/example/init/exit
Normal file
0
containerd-shim/example/init/pid
Normal file
0
containerd-shim/example/init/pid
Normal file
0
containerd-shim/example/init/process.json
Normal file
0
containerd-shim/example/init/process.json
Normal file
0
containerd-shim/example/init/resize
Normal file
0
containerd-shim/example/init/resize
Normal file
0
containerd-shim/example/init/stderr
Normal file
0
containerd-shim/example/init/stderr
Normal file
0
containerd-shim/example/init/stdin
Normal file
0
containerd-shim/example/init/stdin
Normal file
0
containerd-shim/example/init/stdout
Normal file
0
containerd-shim/example/init/stdout
Normal file
0
containerd-shim/example/logger/exit
Normal file
0
containerd-shim/example/logger/exit
Normal file
0
containerd-shim/example/logger/pid
Normal file
0
containerd-shim/example/logger/pid
Normal file
0
containerd-shim/example/logger/process.json
Normal file
0
containerd-shim/example/logger/process.json
Normal file
0
containerd-shim/example/logger/resize
Normal file
0
containerd-shim/example/logger/resize
Normal file
0
containerd-shim/example/logger/stderr
Normal file
0
containerd-shim/example/logger/stderr
Normal file
0
containerd-shim/example/logger/stdin
Normal file
0
containerd-shim/example/logger/stdin
Normal file
0
containerd-shim/example/logger/stdout
Normal file
0
containerd-shim/example/logger/stdout
Normal file
|
@ -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 {
|
||||
|
|
165
containerd-shim/process.go
Normal file
165
containerd-shim/process.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
type process struct {
|
||||
id string
|
||||
bundle string
|
||||
stdio *stdio
|
||||
s specs.Process
|
||||
exec bool
|
||||
containerPid int
|
||||
}
|
||||
|
||||
func newProcess(id, bundle string, exec bool) (*process, error) {
|
||||
f, err := os.Open("process.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
p := &process{
|
||||
id: id,
|
||||
bundle: bundle,
|
||||
exec: exec,
|
||||
}
|
||||
if err := json.NewDecoder(f).Decode(&p.s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.openIO(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *process) start() error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{
|
||||
"--id", p.id,
|
||||
}
|
||||
if p.exec {
|
||||
args = append(args, "exec",
|
||||
"--process", filepath.Join(cwd, "process.json"))
|
||||
} else {
|
||||
args = append(args, "start",
|
||||
"--bundle", p.bundle)
|
||||
}
|
||||
args = append(args,
|
||||
"-d",
|
||||
"--console", p.stdio.console,
|
||||
"--pid-file", filepath.Join(cwd, "pid"),
|
||||
)
|
||||
cmd := exec.Command("runc", args...)
|
||||
cmd.Stdin = p.stdio.stdin
|
||||
cmd.Stdout = p.stdio.stdout
|
||||
cmd.Stderr = p.stdio.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 err
|
||||
}
|
||||
data, err := ioutil.ReadFile("pid")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pid, err := strconv.Atoi(string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.containerPid = pid
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p *process) pid() int {
|
||||
return p.containerPid
|
||||
}
|
||||
|
||||
func (p *process) delete() error {
|
||||
if !p.exec {
|
||||
return exec.Command("runc", "--id", p.id, "delete").Run()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// openIO 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 (p *process) openIO() error {
|
||||
p.stdio = &stdio{}
|
||||
if p.s.Terminal {
|
||||
// FIXME: this is wrong for user namespaces and will need to be translated
|
||||
console, err := libcontainer.NewConsole(int(p.s.User.UID), int(p.s.User.GID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.stdio.console = console.Path()
|
||||
stdin, err := os.OpenFile("stdin", syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(console, stdin)
|
||||
}()
|
||||
stdout, err := os.OpenFile("stdout", syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(stdout, console)
|
||||
console.Close()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
// non-tty
|
||||
for name, dest := range map[string]**os.File{
|
||||
"stdin": &p.stdio.stdin,
|
||||
"stdout": &p.stdio.stdout,
|
||||
"stderr": &p.stdio.stderr,
|
||||
} {
|
||||
f, err := os.OpenFile(name, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dest = f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *process) Close() error {
|
||||
return p.stdio.Close()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue