2015-12-01 19:56:08 +00:00
|
|
|
package runtime
|
2015-11-05 23:29:53 +00:00
|
|
|
|
2015-11-10 22:57:10 +00:00
|
|
|
import (
|
2016-02-01 19:02:41 +00:00
|
|
|
"encoding/json"
|
2016-02-29 22:15:16 +00:00
|
|
|
"io"
|
2016-02-01 19:02:41 +00:00
|
|
|
"io/ioutil"
|
2015-11-10 22:57:10 +00:00
|
|
|
"os"
|
2016-03-14 20:57:28 +00:00
|
|
|
"os/exec"
|
2016-02-01 19:02:41 +00:00
|
|
|
"path/filepath"
|
2016-03-28 20:30:37 +00:00
|
|
|
"time"
|
2015-11-10 22:57:10 +00:00
|
|
|
|
2016-02-01 19:02:41 +00:00
|
|
|
"github.com/Sirupsen/logrus"
|
2016-02-29 18:48:39 +00:00
|
|
|
"github.com/docker/containerd/specs"
|
2015-11-10 22:57:10 +00:00
|
|
|
)
|
2015-11-10 21:44:35 +00:00
|
|
|
|
2015-11-05 23:29:53 +00:00
|
|
|
type Container interface {
|
2015-11-12 21:40:23 +00:00
|
|
|
// ID returns the container ID
|
2015-11-06 00:40:57 +00:00
|
|
|
ID() string
|
2015-11-12 21:40:23 +00:00
|
|
|
// Path returns the path to the bundle
|
2015-11-10 19:38:26 +00:00
|
|
|
Path() string
|
2016-01-06 21:32:46 +00:00
|
|
|
// Start starts the init process of the container
|
2016-02-03 21:56:15 +00:00
|
|
|
Start(checkpoint string, s Stdio) (Process, error)
|
2016-02-01 19:02:41 +00:00
|
|
|
// Exec starts another process in an existing container
|
2016-02-29 18:48:39 +00:00
|
|
|
Exec(string, specs.ProcessSpec, Stdio) (Process, error)
|
2016-01-06 21:32:46 +00:00
|
|
|
// Delete removes the container's state and any resources
|
2015-11-05 23:29:53 +00:00
|
|
|
Delete() error
|
2015-11-12 21:40:23 +00:00
|
|
|
// Processes returns all the containers processes that have been added
|
2015-11-10 21:44:35 +00:00
|
|
|
Processes() ([]Process, error)
|
2015-11-12 21:40:23 +00:00
|
|
|
// State returns the containers runtime state
|
|
|
|
State() State
|
|
|
|
// Resume resumes a paused container
|
|
|
|
Resume() error
|
|
|
|
// Pause pauses a running container
|
|
|
|
Pause() error
|
2016-02-01 23:07:02 +00:00
|
|
|
// RemoveProcess removes the specified process from the container
|
2016-02-01 19:02:41 +00:00
|
|
|
RemoveProcess(string) error
|
2015-12-07 22:47:03 +00:00
|
|
|
// Checkpoints returns all the checkpoints for a container
|
2016-02-01 23:07:02 +00:00
|
|
|
Checkpoints() ([]Checkpoint, error)
|
2015-12-07 22:47:03 +00:00
|
|
|
// Checkpoint creates a new checkpoint
|
2016-02-01 23:07:02 +00:00
|
|
|
Checkpoint(Checkpoint) error
|
2015-12-07 22:47:03 +00:00
|
|
|
// DeleteCheckpoint deletes the checkpoint for the provided name
|
2016-02-01 23:07:02 +00:00
|
|
|
DeleteCheckpoint(name string) error
|
2016-02-11 21:44:25 +00:00
|
|
|
// Labels are user provided labels for the container
|
|
|
|
Labels() []string
|
2016-02-11 22:07:34 +00:00
|
|
|
// Pids returns all pids inside the container
|
|
|
|
Pids() ([]int, error)
|
2015-12-08 18:04:31 +00:00
|
|
|
// Stats returns realtime container stats and resource information
|
2016-02-11 22:36:32 +00:00
|
|
|
Stats() (*Stat, error)
|
2016-02-25 20:59:34 +00:00
|
|
|
// Name or path of the OCI compliant runtime used to execute the container
|
|
|
|
Runtime() string
|
2016-02-11 22:36:32 +00:00
|
|
|
// OOM signals the channel if the container received an OOM notification
|
2016-02-29 22:15:16 +00:00
|
|
|
OOM() (OOM, error)
|
2016-03-07 23:23:52 +00:00
|
|
|
// UpdateResource updates the containers resources to new values
|
|
|
|
UpdateResources(*Resource) error
|
2016-02-29 22:15:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type OOM interface {
|
|
|
|
io.Closer
|
|
|
|
FD() int
|
|
|
|
ContainerID() string
|
|
|
|
Flush()
|
|
|
|
Removed() bool
|
2015-11-05 23:29:53 +00:00
|
|
|
}
|
2016-02-01 19:02:41 +00:00
|
|
|
|
2016-02-03 21:56:15 +00:00
|
|
|
type Stdio struct {
|
|
|
|
Stdin string
|
|
|
|
Stdout string
|
|
|
|
Stderr string
|
|
|
|
}
|
|
|
|
|
2016-02-11 20:20:29 +00:00
|
|
|
func NewStdio(stdin, stdout, stderr string) Stdio {
|
|
|
|
for _, s := range []*string{
|
|
|
|
&stdin, &stdout, &stderr,
|
|
|
|
} {
|
|
|
|
if *s == "" {
|
|
|
|
*s = "/dev/null"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Stdio{
|
|
|
|
Stdin: stdin,
|
|
|
|
Stdout: stdout,
|
|
|
|
Stderr: stderr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:25:42 +00:00
|
|
|
type ContainerOpts struct {
|
|
|
|
Root string
|
|
|
|
ID string
|
|
|
|
Bundle string
|
|
|
|
Runtime string
|
|
|
|
RuntimeArgs []string
|
2016-04-06 06:42:47 +00:00
|
|
|
Shim string
|
2016-03-30 21:25:42 +00:00
|
|
|
Labels []string
|
|
|
|
NoPivotRoot bool
|
2016-03-28 20:30:37 +00:00
|
|
|
Timeout time.Duration
|
2016-03-30 21:25:42 +00:00
|
|
|
}
|
|
|
|
|
2016-02-01 19:02:41 +00:00
|
|
|
// New returns a new container
|
2016-03-30 21:25:42 +00:00
|
|
|
func New(opts ContainerOpts) (Container, error) {
|
2016-02-01 19:02:41 +00:00
|
|
|
c := &container{
|
2016-03-30 21:25:42 +00:00
|
|
|
root: opts.Root,
|
|
|
|
id: opts.ID,
|
|
|
|
bundle: opts.Bundle,
|
|
|
|
labels: opts.Labels,
|
2016-03-24 20:30:27 +00:00
|
|
|
processes: make(map[string]*process),
|
2016-03-30 21:25:42 +00:00
|
|
|
runtime: opts.Runtime,
|
|
|
|
runtimeArgs: opts.RuntimeArgs,
|
2016-04-06 06:42:47 +00:00
|
|
|
shim: opts.Shim,
|
2016-03-30 21:25:42 +00:00
|
|
|
noPivotRoot: opts.NoPivotRoot,
|
2016-03-28 20:30:37 +00:00
|
|
|
timeout: opts.Timeout,
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
2016-03-30 21:25:42 +00:00
|
|
|
if err := os.Mkdir(filepath.Join(c.root, c.id), 0755); err != nil {
|
2016-02-01 19:02:41 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-03-30 21:25:42 +00:00
|
|
|
f, err := os.Create(filepath.Join(c.root, c.id, StateFile))
|
2016-02-01 19:02:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if err := json.NewEncoder(f).Encode(state{
|
2016-03-30 21:25:42 +00:00
|
|
|
Bundle: c.bundle,
|
|
|
|
Labels: c.labels,
|
|
|
|
Runtime: c.runtime,
|
|
|
|
RuntimeArgs: c.runtimeArgs,
|
|
|
|
NoPivotRoot: opts.NoPivotRoot,
|
2016-02-01 19:02:41 +00:00
|
|
|
}); 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{
|
2016-03-24 20:30:27 +00:00
|
|
|
root: root,
|
|
|
|
id: id,
|
|
|
|
bundle: s.Bundle,
|
|
|
|
labels: s.Labels,
|
|
|
|
runtime: s.Runtime,
|
|
|
|
runtimeArgs: s.RuntimeArgs,
|
2016-04-06 06:42:47 +00:00
|
|
|
shim: s.Shim,
|
2016-03-30 21:25:42 +00:00
|
|
|
noPivotRoot: s.NoPivotRoot,
|
2016-03-24 20:30:27 +00:00
|
|
|
processes: make(map[string]*process),
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
|
|
|
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()
|
2016-02-03 21:56:15 +00:00
|
|
|
s, err := readProcessState(filepath.Join(root, id, pid))
|
2016-02-01 19:02:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-03 21:56:15 +00:00
|
|
|
p, err := loadProcess(filepath.Join(root, id, pid), pid, c, s)
|
2016-02-01 19:02:41 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.WithField("id", id).WithField("pid", pid).Debug("containerd: error loading process %s", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.processes[pid] = p
|
|
|
|
}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2016-02-03 21:56:15 +00:00
|
|
|
func readProcessState(dir string) (*ProcessState, error) {
|
2016-02-01 19:02:41 +00:00
|
|
|
f, err := os.Open(filepath.Join(dir, "process.json"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2016-02-03 21:56:15 +00:00
|
|
|
var s ProcessState
|
2016-02-01 19:02:41 +00:00
|
|
|
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type container struct {
|
|
|
|
// path to store runtime state information
|
2016-03-24 20:30:27 +00:00
|
|
|
root string
|
|
|
|
id string
|
|
|
|
bundle string
|
|
|
|
runtime string
|
|
|
|
runtimeArgs []string
|
2016-04-06 06:42:47 +00:00
|
|
|
shim string
|
2016-03-24 20:30:27 +00:00
|
|
|
processes map[string]*process
|
|
|
|
labels []string
|
|
|
|
oomFds []int
|
2016-03-30 21:25:42 +00:00
|
|
|
noPivotRoot bool
|
2016-03-28 20:30:37 +00:00
|
|
|
timeout time.Duration
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *container) ID() string {
|
|
|
|
return c.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *container) Path() string {
|
|
|
|
return c.bundle
|
|
|
|
}
|
|
|
|
|
2016-02-11 21:44:25 +00:00
|
|
|
func (c *container) Labels() []string {
|
|
|
|
return c.labels
|
|
|
|
}
|
|
|
|
|
2016-03-15 18:31:32 +00:00
|
|
|
func (c *container) readSpec() (*specs.Spec, error) {
|
|
|
|
var spec specs.Spec
|
2016-02-01 19:02:41 +00:00
|
|
|
f, err := os.Open(filepath.Join(c.bundle, "config.json"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2016-03-11 07:08:54 +00:00
|
|
|
if err := json.NewDecoder(f).Decode(&spec); err != nil {
|
2016-02-01 19:02:41 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &spec, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *container) Delete() error {
|
2016-03-14 20:57:28 +00:00
|
|
|
err := os.RemoveAll(filepath.Join(c.root, c.id))
|
2016-03-29 17:26:37 +00:00
|
|
|
|
|
|
|
args := c.runtimeArgs
|
|
|
|
args = append(args, "delete", c.id)
|
2016-03-28 20:30:37 +00:00
|
|
|
if derr := exec.Command(c.runtime, args...).Run(); err == nil {
|
|
|
|
err = derr
|
|
|
|
}
|
2016-03-14 20:57:28 +00:00
|
|
|
return err
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *container) Processes() ([]Process, error) {
|
|
|
|
out := []Process{}
|
|
|
|
for _, p := range c.processes {
|
|
|
|
out = append(out, p)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *container) RemoveProcess(pid string) error {
|
|
|
|
delete(c.processes, pid)
|
2016-02-03 21:56:15 +00:00
|
|
|
return os.RemoveAll(filepath.Join(c.root, c.id, pid))
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
2016-03-07 23:23:52 +00:00
|
|
|
|
|
|
|
func (c *container) UpdateResources(r *Resource) error {
|
|
|
|
container, err := c.getLibctContainer()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
config := container.Config()
|
|
|
|
config.Cgroups.Resources.CpuShares = r.CPUShares
|
|
|
|
config.Cgroups.Resources.BlkioWeight = r.BlkioWeight
|
|
|
|
config.Cgroups.Resources.CpuPeriod = r.CPUPeriod
|
|
|
|
config.Cgroups.Resources.CpuQuota = r.CPUQuota
|
|
|
|
config.Cgroups.Resources.CpusetCpus = r.CpusetCpus
|
|
|
|
config.Cgroups.Resources.CpusetMems = r.CpusetMems
|
|
|
|
config.Cgroups.Resources.KernelMemory = r.KernelMemory
|
|
|
|
config.Cgroups.Resources.Memory = r.Memory
|
|
|
|
config.Cgroups.Resources.MemoryReservation = r.MemoryReservation
|
|
|
|
config.Cgroups.Resources.MemorySwap = r.MemorySwap
|
|
|
|
return container.Set(config)
|
|
|
|
}
|