173 lines
3.6 KiB
Go
173 lines
3.6 KiB
Go
|
package runtime
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/Sirupsen/logrus"
|
||
|
"github.com/opencontainers/specs"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
ExitFile = "exit"
|
||
|
ExitStatusFile = "exitStatus"
|
||
|
StateFile = "state.json"
|
||
|
InitProcessID = "init"
|
||
|
)
|
||
|
|
||
|
type state struct {
|
||
|
Bundle string `json:"bundle"`
|
||
|
}
|
||
|
|
||
|
// New returns a new container
|
||
|
func New(root, id, bundle string) (Container, error) {
|
||
|
c := &container{
|
||
|
root: root,
|
||
|
id: id,
|
||
|
bundle: bundle,
|
||
|
processes: make(map[string]*process),
|
||
|
}
|
||
|
if err := os.Mkdir(filepath.Join(root, id), 0755); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
f, err := os.Create(filepath.Join(root, id, StateFile))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer f.Close()
|
||
|
if err := json.NewEncoder(f).Encode(state{
|
||
|
Bundle: bundle,
|
||
|
}); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
func Load(root, id string) (Container, error) {
|
||
|
var s state
|
||
|
f, err := os.Open(filepath.Join(root, id, StateFile))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer f.Close()
|
||
|
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
c := &container{
|
||
|
root: root,
|
||
|
id: id,
|
||
|
bundle: s.Bundle,
|
||
|
processes: make(map[string]*process),
|
||
|
}
|
||
|
dirs, err := ioutil.ReadDir(filepath.Join(root, id))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
for _, d := range dirs {
|
||
|
if !d.IsDir() {
|
||
|
continue
|
||
|
}
|
||
|
pid := d.Name()
|
||
|
// TODO: get the process spec from a state file in the process dir
|
||
|
p, err := loadProcess(filepath.Join(root, id, pid), pid, c, specs.Process{})
|
||
|
if err != nil {
|
||
|
if err == ErrProcessExited {
|
||
|
logrus.WithField("id", id).WithField("pid", pid).Debug("containerd: process exited while away")
|
||
|
// TODO: fire events to do the removal
|
||
|
if err := os.RemoveAll(filepath.Join(root, id, pid)); err != nil {
|
||
|
logrus.WithField("error", err).Warn("containerd: remove process state")
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
return nil, err
|
||
|
}
|
||
|
c.processes[pid] = p
|
||
|
}
|
||
|
if len(c.processes) == 0 {
|
||
|
return nil, ErrContainerExited
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
type container struct {
|
||
|
// path to store runtime state information
|
||
|
root string
|
||
|
id string
|
||
|
bundle string
|
||
|
processes map[string]*process
|
||
|
}
|
||
|
|
||
|
func (c *container) ID() string {
|
||
|
return c.id
|
||
|
}
|
||
|
|
||
|
func (c *container) Path() string {
|
||
|
return c.bundle
|
||
|
}
|
||
|
|
||
|
func (c *container) Start() (Process, error) {
|
||
|
processRoot := filepath.Join(c.root, c.id, InitProcessID)
|
||
|
if err := os.MkdirAll(processRoot, 0755); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
cmd := exec.Command("containerd-shim", processRoot, c.id)
|
||
|
cmd.Dir = c.bundle
|
||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||
|
Setpgid: true,
|
||
|
}
|
||
|
spec, err := c.readSpec()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
p, err := newProcess(processRoot, InitProcessID, c, spec.Process)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := cmd.Start(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
c.processes[InitProcessID] = p
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
func (c *container) readSpec() (*specs.LinuxSpec, error) {
|
||
|
var spec specs.LinuxSpec
|
||
|
f, err := os.Open(filepath.Join(c.bundle, "config.json"))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer f.Close()
|
||
|
if err := json.NewDecoder(f).Decode(&spec); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &spec, nil
|
||
|
}
|
||
|
|
||
|
func (c *container) Pause() error {
|
||
|
return errNotImplemented
|
||
|
}
|
||
|
|
||
|
func (c *container) Resume() error {
|
||
|
return errNotImplemented
|
||
|
}
|
||
|
|
||
|
func (c *container) State() State {
|
||
|
return Running
|
||
|
}
|
||
|
|
||
|
func (c *container) Delete() error {
|
||
|
return os.RemoveAll(filepath.Join(c.root, c.id))
|
||
|
}
|
||
|
|
||
|
func (c *container) Processes() ([]Process, error) {
|
||
|
out := []Process{}
|
||
|
for _, p := range c.processes {
|
||
|
out = append(out, p)
|
||
|
}
|
||
|
return out, nil
|
||
|
}
|