76cf593212
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
235 lines
4.7 KiB
Go
235 lines
4.7 KiB
Go
// +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) OOM() (<-chan struct{}, 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)
|
|
}
|
|
|
|
func (p *runcProcess) Close() error {
|
|
return nil
|
|
}
|
|
|
|
type runcRuntime struct {
|
|
stateDir string
|
|
}
|
|
|
|
func (r *runcRuntime) Type() string {
|
|
return "runc"
|
|
}
|
|
|
|
func (r *runcRuntime) Create(id, bundlePath, consolePath string) (runtime.Container, *runtime.IO, error) {
|
|
var s specs.Spec
|
|
f, err := os.Open(filepath.Join(bundlePath, "config.json"))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
cmd := exec.Command("runc", "--root", r.stateDir, "--id", id, "start")
|
|
cmd.Dir = bundlePath
|
|
i, err := r.createIO(cmd)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &runcContainer{
|
|
id: id,
|
|
path: bundlePath,
|
|
stateDir: r.stateDir,
|
|
initProcess: &runcProcess{
|
|
cmd: cmd,
|
|
spec: s.Process,
|
|
},
|
|
processes: make(map[int]*runcProcess),
|
|
}, i, nil
|
|
}
|
|
|
|
func (r *runcRuntime) createIO(cmd *exec.Cmd) (*runtime.IO, error) {
|
|
w, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ro, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
re, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &runtime.IO{
|
|
Stdin: w,
|
|
Stdout: ro,
|
|
Stderr: re,
|
|
}, nil
|
|
}
|
|
|
|
func (r *runcRuntime) StartProcess(ci runtime.Container, p specs.Process, consolePath string) (runtime.Process, *runtime.IO, error) {
|
|
c, ok := ci.(*runcContainer)
|
|
if !ok {
|
|
return nil, nil, runtime.ErrInvalidContainerType
|
|
}
|
|
f, err := ioutil.TempFile("", "containerd")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
err = json.NewEncoder(f).Encode(p)
|
|
f.Close()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
cmd := c.newCommand("exec", f.Name())
|
|
i, err := r.createIO(cmd)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
process := &runcProcess{
|
|
cmd: cmd,
|
|
spec: p,
|
|
}
|
|
if err := cmd.Start(); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
pid, err := process.Pid()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
c.processes[pid] = process
|
|
return process, i, nil
|
|
}
|