// +build runc package runc import ( "encoding/json" "errors" "io/ioutil" "os" "os/exec" "path/filepath" "github.com/docker/containerd/runtime" "github.com/opencontainers/specs" ) func NewRuntime(stateDir string) (runtime.Runtime, error) { return &runcRuntime{ stateDir: stateDir, }, nil } type runcContainer struct { id string path string stateDir string exitStatus int processes map[int]*runcProcess initProcess *runcProcess } func (c *runcContainer) ID() string { return c.id } func (c *runcContainer) Start() error { return c.initProcess.cmd.Start() } func (c *runcContainer) Stats() (*runtime.Stat, error) { return nil, errors.New("containerd: runc does not support stats in containerd") } func (c *runcContainer) Path() string { return c.path } func (c *runcContainer) Pid() (int, error) { return c.initProcess.cmd.Process.Pid, nil } func (c *runcContainer) SetExited(status int) { c.exitStatus = status } // noop for runc func (c *runcContainer) Delete() error { return nil } func (c *runcContainer) Processes() ([]runtime.Process, error) { procs := []runtime.Process{ c.initProcess, } for _, p := range c.processes { procs = append(procs, p) } return procs, nil } func (c *runcContainer) RemoveProcess(pid int) error { if _, ok := c.processes[pid]; !ok { return runtime.ErrNotChildProcess } delete(c.processes, pid) return nil } func (c *runcContainer) State() runtime.State { // TODO: how to do this with runc return runtime.State{ Status: runtime.Running, } } func (c *runcContainer) Resume() error { return c.newCommand("resume").Run() } func (c *runcContainer) Pause() error { return c.newCommand("pause").Run() } // TODO: pass arguments func (c *runcContainer) Checkpoint(runtime.Checkpoint) error { return c.newCommand("checkpoint").Run() } // TODO: pass arguments func (c *runcContainer) Restore(cp string) error { return c.newCommand("restore").Run() } // TODO: pass arguments func (c *runcContainer) DeleteCheckpoint(cp string) error { return errors.New("not implemented") } // TODO: implement in runc func (c *runcContainer) Checkpoints() ([]runtime.Checkpoint, error) { return nil, errors.New("not implemented") } func (c *runcContainer) newCommand(args ...string) *exec.Cmd { cmd := exec.Command("runc", append([]string{"--root", c.stateDir, "--id", c.id}, args...)...) cmd.Dir = c.path return cmd } type runcProcess struct { cmd *exec.Cmd spec specs.Process } // pid of the container, not of runc func (p *runcProcess) Pid() (int, error) { return p.cmd.Process.Pid, nil } func (p *runcProcess) Spec() specs.Process { return p.spec } func (p *runcProcess) Signal(s os.Signal) error { return p.cmd.Process.Signal(s) } type runcRuntime struct { stateDir string } func (r *runcRuntime) Type() string { return "runc" } func (r *runcRuntime) Create(id, bundlePath string, stdio *runtime.Stdio) (runtime.Container, error) { cmd := exec.Command("runc", "--root", r.stateDir, "--id", id, "start") cmd.Dir = bundlePath // cmd.Stderr = stdio.Stderr // cmd.Stdout = stdio.Stdout var s specs.Spec f, err := os.Open(filepath.Join(bundlePath, "config.json")) if err != nil { return nil, err } defer f.Close() if err := json.NewDecoder(f).Decode(&s); err != nil { return nil, err } return &runcContainer{ id: id, path: bundlePath, stateDir: r.stateDir, initProcess: &runcProcess{ cmd: cmd, spec: s.Process, }, processes: make(map[int]*runcProcess), }, nil } func (r *runcRuntime) StartProcess(ci runtime.Container, p specs.Process, stdio *runtime.Stdio) (runtime.Process, error) { c, ok := ci.(*runcContainer) if !ok { return nil, runtime.ErrInvalidContainerType } f, err := ioutil.TempFile("", "containerd") if err != nil { return nil, err } if err := json.NewEncoder(f).Encode(p); err != nil { f.Close() return nil, err } cmd := c.newCommand("exec", f.Name()) f.Close() process := &runcProcess{ cmd: cmd, spec: p, } if err := cmd.Start(); err != nil { return nil, err } pid, err := process.Pid() if err != nil { return nil, err } c.processes[pid] = process return process, nil }