execution: remove statedir type

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
Kenfe-Mickael Laventure 2017-01-18 20:24:53 -08:00
parent 3f2d9d19bf
commit bd6057c8e1
5 changed files with 308 additions and 273 deletions

View file

@ -1,91 +1,179 @@
package execution package execution
import "fmt" import (
"context"
"io/ioutil"
"os"
"path/filepath"
"sync"
func NewContainer(stateRoot, id, bundle string) (*Container, error) { "github.com/docker/containerd/log"
stateDir, err := NewStateDir(stateRoot, id) "github.com/pkg/errors"
if err != nil { )
return nil, err
} const (
return &Container{ InitProcessID = "init"
processesDirName = "processes"
bundleFileName = "bundle"
)
func LoadContainer(ctx context.Context, stateDir, id string) (c *Container, err error) {
c = &Container{
id: id, id: id,
bundle: bundle,
stateDir: stateDir, stateDir: stateDir,
status: Created, processes: make(map[string]Process, 1),
processes: make(map[string]Process), ctx: ctx,
}, nil status: Unknown,
}
data, err := ioutil.ReadFile(filepath.Join(stateDir, bundleFileName))
if err != nil {
err = errors.Wrapf(err, "failed to read bundle path")
return
}
c.bundle = string(data)
return
} }
func LoadContainer(dir StateDir, id, bundle string, status Status) *Container { func NewContainer(ctx context.Context, stateDir, id, bundle string) (c *Container, err error) {
return &Container{ c = &Container{
id: id, id: id,
stateDir: dir, stateDir: stateDir,
bundle: bundle, bundle: bundle,
status: status, processes: make(map[string]Process, 1),
processes: make(map[string]Process), status: Created,
ctx: ctx,
} }
defer func() {
if err != nil {
c.Cleanup()
c = nil
}
}()
if err = os.Mkdir(stateDir, 0700); err != nil {
err = errors.Wrap(err, "failed to create container state dir")
return
}
bundleFile := filepath.Join(stateDir, bundleFileName)
if err = ioutil.WriteFile(bundleFile, []byte(bundle), 0600); err != nil {
err = errors.Wrap(err, "failed to store bundle path")
return
}
processesDir := filepath.Join(stateDir, processesDirName)
if err = os.Mkdir(processesDir, 0700); err != nil {
err = errors.Wrap(err, "failed to create processes statedir")
return
}
return
} }
type Container struct { type Container struct {
id string id string
bundle string stateDir string
stateDir StateDir bundle string
initPid int64
status Status
processes map[string]Process processes map[string]Process
status Status
ctx context.Context
mu sync.Mutex
} }
func (c *Container) ID() string { func (c *Container) ID() string {
return c.id return c.id
} }
func (c *Container) Status() Status {
for _, p := range c.processes {
if p.Pid() == c.initPid {
c.status = p.Status()
break
}
}
return c.status
}
func (c *Container) Bundle() string { func (c *Container) Bundle() string {
return c.bundle return c.bundle
} }
func (c *Container) StateDir() StateDir {
return c.stateDir
}
func (c *Container) Wait() (uint32, error) { func (c *Container) Wait() (uint32, error) {
for _, p := range c.processes { initProcess := c.GetProcess(InitProcessID)
if p.Pid() == c.initPid { return initProcess.Wait()
return p.Wait()
}
}
return 0, fmt.Errorf("no init process")
} }
func (c *Container) AddProcess(p Process, isInit bool) { func (c *Container) Status() Status {
if isInit { initProcess := c.GetProcess(InitProcessID)
c.initPid = p.Pid() return initProcess.Status()
} }
func (c *Container) AddProcess(p Process) {
c.mu.Lock()
c.processes[p.ID()] = p c.processes[p.ID()] = p
c.mu.Unlock()
}
func (c *Container) RemoveProcess(id string) error {
if _, ok := c.processes[id]; !ok {
return errors.Errorf("no such process %s", id)
}
c.mu.Lock()
delete(c.processes, id)
c.mu.Unlock()
processStateDir := filepath.Join(c.stateDir, processesDirName, id)
err := os.RemoveAll(processStateDir)
if err != nil {
return errors.Wrap(err, "failed to remove process state dir")
}
return nil
} }
func (c *Container) GetProcess(id string) Process { func (c *Container) GetProcess(id string) Process {
c.mu.Lock()
defer c.mu.Unlock()
return c.processes[id] return c.processes[id]
} }
func (c *Container) RemoveProcess(id string) { func (c *Container) Processes() []Process {
delete(c.processes, id) var procs []Process
c.mu.Lock()
for _, p := range c.processes {
procs = append(procs, p)
}
c.mu.Unlock()
return procs
} }
func (c *Container) Processes() []Process { // ProcessStateDir returns the path of the state dir for a given
var out []Process // process id. The process doesn't have to exist for this to succeed.
for _, p := range c.processes { func (c *Container) ProcessStateDir(id string) string {
out = append(out, p) return filepath.Join(c.stateDir, processesDirName, id)
} }
return out
// ProcessesStateDir returns a map matching process ids to their state
// directory
func (c *Container) ProcessesStateDir() (map[string]string, error) {
root := filepath.Join(c.stateDir, processesDirName)
dirs, err := ioutil.ReadDir(root)
if err != nil {
return nil, errors.Wrapf(err, "failed to list processes state dirs")
}
procs := make(map[string]string, 1)
for _, d := range dirs {
if d.IsDir() {
procs[d.Name()] = filepath.Join(root, d.Name())
}
}
return procs, nil
}
func (c *Container) Cleanup() {
if err := os.RemoveAll(c.stateDir); err != nil {
log.G(c.ctx).Warnf("failed to remove container state dir: %v", err)
}
}
func (c *Container) Context() context.Context {
return c.ctx
} }

View file

@ -27,34 +27,37 @@ type newProcessOpts struct {
runtimeArgs []string runtimeArgs []string
container *execution.Container container *execution.Container
exec bool exec bool
stateDir string
execution.StartProcessOpts execution.StartProcessOpts
} }
func newProcess(ctx context.Context, o newProcessOpts) (*process, error) { func newProcess(ctx context.Context, o newProcessOpts) (p *process, err error) {
procStateDir, err := o.container.StateDir().NewProcess(o.ID) p = &process{
if err != nil { id: o.ID,
return nil, err stateDir: o.stateDir,
exitChan: make(chan struct{}),
ctx: ctx,
} }
defer func() { defer func() {
if err != nil { if err != nil {
o.container.StateDir().DeleteProcess(o.ID) p.cleanup()
p = nil
} }
}() }()
exitPipe, controlPipe, err := getControlPipes(procStateDir) if err = os.Mkdir(o.stateDir, 0700); err != nil {
if err != nil { err = errors.Wrap(err, "failed to create process state dir")
return nil, err return
} }
defer func() {
if err != nil {
exitPipe.Close()
controlPipe.Close()
}
}()
cmd, err := newShim(o, procStateDir) p.exitPipe, p.controlPipe, err = getControlPipes(o.stateDir)
if err != nil { if err != nil {
return nil, err return
}
cmd, err := newShimProcess(o)
if err != nil {
return
} }
defer func() { defer func() {
if err != nil { if err != nil {
@ -75,70 +78,52 @@ func newProcess(ctx context.Context, o newProcessOpts) (*process, error) {
close(abortCh) close(abortCh)
}() }()
process := &process{ p.pid, p.startTime, p.status, err = waitUntilReady(ctx, abortCh, o.stateDir)
root: procStateDir,
id: o.ID,
exitChan: make(chan struct{}),
exitPipe: exitPipe,
controlPipe: controlPipe,
}
pid, stime, status, err := waitForPid(ctx, abortCh, procStateDir)
if err != nil { if err != nil {
return nil, err return
} }
process.pid = int64(pid)
process.status = status
process.startTime = stime
return process, nil return
} }
func loadProcess(root, id string) (*process, error) { func loadProcess(ctx context.Context, stateDir, id string) (p *process, err error) {
pid, err := runc.ReadPidFile(filepath.Join(root, pidFilename)) p = &process{
if err != nil { id: id,
return nil, err stateDir: stateDir,
} exitChan: make(chan struct{}),
status: execution.Running,
stime, err := ioutil.ReadFile(filepath.Join(root, startTimeFilename)) ctx: ctx,
if err != nil {
return nil, err
}
path := filepath.Join(root, exitPipeFilename)
exitPipe, err := os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
if err != nil {
return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
exitPipe.Close() p.cleanup()
p = nil
} }
}() }()
path = filepath.Join(root, controlPipeFilename) p.pid, err = getPidFromFile(filepath.Join(stateDir, pidFilename))
controlPipe, err := os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
if err != nil { if err != nil {
return nil, err err = errors.Wrap(err, "failed to read pid")
return
} }
defer func() {
if err != nil {
controlPipe.Close()
}
}()
p := &process{ p.startTime, err = getStartTimeFromFile(filepath.Join(stateDir, startTimeFilename))
root: root, if err != nil {
id: id, return
pid: int64(pid), }
exitChan: make(chan struct{}),
exitPipe: exitPipe, path := filepath.Join(stateDir, exitPipeFilename)
controlPipe: controlPipe, p.exitPipe, err = os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
startTime: string(stime), if err != nil {
// TODO: status may need to be stored on disk to handle err = errors.Wrapf(err, "failed to open exit pipe")
// Created state for init (i.e. a Start is needed to run the return
// container) }
status: execution.Running,
path = filepath.Join(stateDir, controlPipeFilename)
p.controlPipe, err = os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
if err != nil {
err = errors.Wrapf(err, "failed to open control pipe")
return
} }
markAsStopped := func(p *process) (*process, error) { markAsStopped := func(p *process) (*process, error) {
@ -146,30 +131,32 @@ func loadProcess(root, id string) (*process, error) {
return p, nil return p, nil
} }
if err = syscall.Kill(pid, 0); err != nil { if err = syscall.Kill(int(p.pid), 0); err != nil {
if err == syscall.ESRCH { if err == syscall.ESRCH {
return markAsStopped(p) return markAsStopped(p)
} }
return nil, err err = errors.Wrapf(err, "failed to check if process is still alive")
return
} }
cstime, err := starttime.GetProcessStartTime(pid) cstime, err := starttime.GetProcessStartTime(int(p.pid))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return markAsStopped(p) return markAsStopped(p)
} }
return nil, err err = errors.Wrapf(err, "failed retrieve current process start time")
return
} }
if p.startTime != cstime { if p.startTime != cstime {
return markAsStopped(p) return markAsStopped(p)
} }
return p, nil return
} }
type process struct { type process struct {
root string stateDir string
id string id string
pid int64 pid int64
exitChan chan struct{} exitChan chan struct{}
@ -211,7 +198,7 @@ func (p *process) Wait() (uint32, error) {
return uint32(128 + int(syscall.SIGKILL)), nil return uint32(128 + int(syscall.SIGKILL)), nil
} }
data, err := ioutil.ReadFile(filepath.Join(p.root, exitStatusFilename)) data, err := ioutil.ReadFile(filepath.Join(p.stateDir, exitStatusFilename))
if err != nil { if err != nil {
return execution.UnknownStatusCode, errors.Wrap(err, "failed to read process exit status") return execution.UnknownStatusCode, errors.Wrap(err, "failed to read process exit status")
} }
@ -278,7 +265,19 @@ func (p *process) isAlive() bool {
return true return true
} }
func waitForPid(ctx context.Context, abortCh chan syscall.WaitStatus, root string) (pid int, stime string, status execution.Status, err error) { func (p *process) cleanup() {
for _, f := range []*os.File{p.exitPipe, p.controlPipe} {
if f != nil {
f.Close()
}
}
if err := os.RemoveAll(p.stateDir); err != nil {
log.G(p.ctx).Warnf("failed to remove process state dir: %v", err)
}
}
func waitUntilReady(ctx context.Context, abortCh chan syscall.WaitStatus, root string) (pid int64, stime string, status execution.Status, err error) {
status = execution.Unknown status = execution.Unknown
for { for {
select { select {
@ -293,7 +292,7 @@ func waitForPid(ctx context.Context, abortCh chan syscall.WaitStatus, root strin
return return
default: default:
} }
pid, err = runc.ReadPidFile(filepath.Join(root, pidFilename)) pid, err = getPidFromFile(filepath.Join(root, pidFilename))
if err == nil { if err == nil {
break break
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
@ -301,7 +300,7 @@ func waitForPid(ctx context.Context, abortCh chan syscall.WaitStatus, root strin
} }
} }
status = execution.Created status = execution.Created
stime, err = starttime.GetProcessStartTime(pid) stime, err = starttime.GetProcessStartTime(int(pid))
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
status = execution.Stopped status = execution.Stopped
@ -328,9 +327,9 @@ func waitForPid(ctx context.Context, abortCh chan syscall.WaitStatus, root strin
return pid, stime, status, nil return pid, stime, status, nil
} }
func newShim(o newProcessOpts, workDir string) (*exec.Cmd, error) { func newShimProcess(o newProcessOpts) (*exec.Cmd, error) {
cmd := exec.Command(o.shimBinary, o.container.ID(), o.container.Bundle(), o.runtime) cmd := exec.Command(o.shimBinary, o.container.ID(), o.container.Bundle(), o.runtime)
cmd.Dir = workDir cmd.Dir = o.stateDir
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true, Setpgid: true,
} }
@ -348,12 +347,11 @@ func newShim(o newProcessOpts, workDir string) (*exec.Cmd, error) {
RootGID: int(o.Spec.User.GID), RootGID: int(o.Spec.User.GID),
} }
f, err := os.Create(filepath.Join(workDir, "process.json")) f, err := os.Create(filepath.Join(o.stateDir, "process.json"))
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to create shim's process.json for container %s", o.container.ID()) return nil, errors.Wrapf(err, "failed to create shim's process.json for container %s", o.container.ID())
} }
defer f.Close() defer f.Close()
if err := json.NewEncoder(f).Encode(state); err != nil { if err := json.NewEncoder(f).Encode(state); err != nil {
return nil, errors.Wrapf(err, "failed to create shim's processState for container %s", o.container.ID()) return nil, errors.Wrapf(err, "failed to create shim's processState for container %s", o.container.ID())
} }
@ -368,19 +366,39 @@ func newShim(o newProcessOpts, workDir string) (*exec.Cmd, error) {
func getControlPipes(root string) (exitPipe *os.File, controlPipe *os.File, err error) { func getControlPipes(root string) (exitPipe *os.File, controlPipe *os.File, err error) {
path := filepath.Join(root, exitPipeFilename) path := filepath.Join(root, exitPipeFilename)
if err = unix.Mkfifo(path, 0700); err != nil { if err = unix.Mkfifo(path, 0700); err != nil {
return exitPipe, controlPipe, errors.Wrap(err, "failed to create shim exit fifo") err = errors.Wrap(err, "failed to create shim exit fifo")
return
} }
if exitPipe, err = os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0); err != nil { if exitPipe, err = os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0); err != nil {
return exitPipe, controlPipe, errors.Wrap(err, "failed to open shim exit fifo") err = errors.Wrap(err, "failed to open shim exit fifo")
return
} }
path = filepath.Join(root, controlPipeFilename) path = filepath.Join(root, controlPipeFilename)
if err = unix.Mkfifo(path, 0700); err != nil { if err = unix.Mkfifo(path, 0700); err != nil {
return exitPipe, controlPipe, errors.Wrap(err, "failed to create shim control fifo") err = errors.Wrap(err, "failed to create shim control fifo")
return
} }
if controlPipe, err = os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { if controlPipe, err = os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0); err != nil {
return exitPipe, controlPipe, errors.Wrap(err, "failed to open shim control fifo") err = errors.Wrap(err, "failed to open shim control fifo")
return
} }
return exitPipe, controlPipe, nil return
}
func getPidFromFile(path string) (int64, error) {
pid, err := runc.ReadPidFile(path)
if err != nil {
return -1, err
}
return int64(pid), nil
}
func getStartTimeFromFile(path string) (string, error) {
stime, err := ioutil.ReadFile(path)
if err != nil {
return "", errors.Wrapf(err, "failed to read start time")
}
return string(stime), nil
} }

View file

@ -1,6 +1,7 @@
package shim package shim
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
@ -24,7 +25,6 @@ const (
startTimeFilename = "starttime" startTimeFilename = "starttime"
exitPipeFilename = "exit" exitPipeFilename = "exit"
controlPipeFilename = "control" controlPipeFilename = "control"
initProcessID = "init"
exitStatusFilename = "exitStatus" exitStatusFilename = "exitStatus"
) )
@ -93,21 +93,17 @@ func (s *ShimRuntime) Create(ctx context.Context, id string, o execution.CreateO
return nil, execution.ErrContainerExists return nil, execution.ErrContainerExists
} }
container, err := execution.NewContainer(s.root, id, o.Bundle) containerCtx := log.WithModule(log.WithModule(ctx, "container"), id)
container, err := execution.NewContainer(containerCtx, filepath.Join(s.root, id), id, o.Bundle)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
container.StateDir().Delete() container.Cleanup()
} }
}() }()
err = ioutil.WriteFile(filepath.Join(string(container.StateDir()), "bundle"), []byte(o.Bundle), 0600)
if err != nil {
return nil, errors.Wrap(err, "failed to save bundle path to disk")
}
// extract Process spec from bundle's config.json // extract Process spec from bundle's config.json
var spec specs.Spec var spec specs.Spec
f, err := os.Open(filepath.Join(o.Bundle, "config.json")) f, err := os.Open(filepath.Join(o.Bundle, "config.json"))
@ -125,8 +121,9 @@ func (s *ShimRuntime) Create(ctx context.Context, id string, o execution.CreateO
runtimeArgs: s.runtimeArgs, runtimeArgs: s.runtimeArgs,
container: container, container: container,
exec: false, exec: false,
stateDir: container.ProcessStateDir(execution.InitProcessID),
StartProcessOpts: execution.StartProcessOpts{ StartProcessOpts: execution.StartProcessOpts{
ID: initProcessID, ID: execution.InitProcessID,
Spec: spec.Process, Spec: spec.Process,
Console: o.Console, Console: o.Console,
Stdin: o.Stdin, Stdin: o.Stdin,
@ -135,14 +132,14 @@ func (s *ShimRuntime) Create(ctx context.Context, id string, o execution.CreateO
}, },
} }
process, err := newProcess(ctx, processOpts) processCtx := log.WithModule(log.WithModule(containerCtx, "process"), execution.InitProcessID)
process, err := newProcess(processCtx, processOpts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
process.ctx = log.WithModule(log.WithModule(s.ctx, "container"), id)
s.monitorProcess(process) s.monitorProcess(process)
container.AddProcess(process, true) container.AddProcess(process)
s.addContainer(container) s.addContainer(container)
@ -194,7 +191,7 @@ func (s *ShimRuntime) Delete(ctx context.Context, c *execution.Container) error
return errors.Errorf("cannot delete a container in the '%s' state", c.Status()) return errors.Errorf("cannot delete a container in the '%s' state", c.Status())
} }
c.StateDir().Delete() c.Cleanup()
s.removeContainer(c) s.removeContainer(c)
return nil return nil
} }
@ -232,7 +229,9 @@ func (s *ShimRuntime) StartProcess(ctx context.Context, c *execution.Container,
exec: true, exec: true,
StartProcessOpts: o, StartProcessOpts: o,
} }
process, err := newProcess(ctx, processOpts)
processCtx := log.WithModule(log.WithModule(c.Context(), "process"), execution.InitProcessID)
process, err := newProcess(processCtx, processOpts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,7 +239,7 @@ func (s *ShimRuntime) StartProcess(ctx context.Context, c *execution.Container,
process.status = execution.Running process.status = execution.Running
s.monitorProcess(process) s.monitorProcess(process)
c.AddProcess(process, false) c.AddProcess(process)
return process, nil return process, nil
} }
@ -250,11 +249,11 @@ func (s *ShimRuntime) SignalProcess(ctx context.Context, c *execution.Container,
process := c.GetProcess(id) process := c.GetProcess(id)
if process == nil { if process == nil {
return errors.Errorf("no such process %s", id) return errors.Errorf("container %s has no process named %s", c.ID(), id)
} }
err := syscall.Kill(int(process.Pid()), sig.(syscall.Signal)) err := syscall.Kill(int(process.Pid()), sig.(syscall.Signal))
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to send %v signal to process %v", sig, process.Pid()) return errors.Wrapf(err, "failed to send %v signal to container %s process %v", sig, c.ID(), process.Pid())
} }
return err return err
} }
@ -263,13 +262,14 @@ func (s *ShimRuntime) DeleteProcess(ctx context.Context, c *execution.Container,
log.G(s.ctx).WithFields(logrus.Fields{"container": c, "process-id": id}). log.G(s.ctx).WithFields(logrus.Fields{"container": c, "process-id": id}).
Debug("DeleteProcess()") Debug("DeleteProcess()")
c.RemoveProcess(id) if p := c.GetProcess(id); p != nil {
return c.StateDir().DeleteProcess(id) p.(*process).cleanup()
}
// return c.RemoveProcess(id)
// }
//
return errors.Errorf("container %s has no process named %s", c.ID(), id)
}
func (s *ShimRuntime) monitor() { func (s *ShimRuntime) monitor() {
var events [128]syscall.EpollEvent var events [128]syscall.EpollEvent
@ -375,46 +375,56 @@ func (s *ShimRuntime) loadContainers() {
continue continue
} }
stateDir, err := execution.LoadStateDir(s.root, c.Name()) stateDir := filepath.Join(s.root, c.Name())
containerCtx := log.WithModule(log.WithModule(s.ctx, "container"), c.Name())
container, err := execution.LoadContainer(containerCtx, stateDir, c.Name())
if err != nil { if err != nil {
// We should never fail the above call unless someone log.G(s.ctx).WithField("container-id", c.Name()).Warn(err)
// delete the directory while we're loading
log.G(s.ctx).WithFields(logrus.Fields{"container": c.Name(), "statedir": s.root}).
Warn("failed to load container statedir:", err)
continue
}
bundle, err := ioutil.ReadFile(filepath.Join(string(stateDir), "bundle"))
if err != nil {
log.G(s.ctx).WithField("container", c.Name()).
Warn("failed to load container bundle path:", err)
continue continue
} }
container := execution.LoadContainer(stateDir, c.Name(), string(bundle), execution.Unknown) processDirs, err := container.ProcessesStateDir()
s.addContainer(container)
processDirs, err := stateDir.Processes()
if err != nil { if err != nil {
log.G(s.ctx).WithField("container", c.Name()). log.G(s.ctx).WithField("container-id", c.Name()).Warn(err)
Warn("failed to retrieve container processes:", err)
continue continue
} }
for _, procStateRoot := range processDirs { for processID, processStateDir := range processDirs {
id := filepath.Base(procStateRoot) processCtx := log.WithModule(log.WithModule(containerCtx, "process"), processID)
proc, err := loadProcess(procStateRoot, id) var p *process
p, err = loadProcess(processCtx, processStateDir, processID)
if err != nil { if err != nil {
log.G(s.ctx).WithFields(logrus.Fields{"container": c.Name(), "process": id}). log.G(s.ctx).WithFields(logrus.Fields{"container-id": c.Name(), "process": processID}).Warn(err)
Warn("failed to load process:", err)
s.removeContainer(container)
for _, p := range container.Processes() {
s.unmonitorProcess(p.(*process))
}
break break
} }
proc.ctx = log.WithModule(log.WithModule(s.ctx, "container"), container.ID()) if processID == execution.InitProcessID && p.status == execution.Running {
container.AddProcess(proc, proc.ID() == initProcessID) p.status = s.loadContainerStatus(container.ID())
s.monitorProcess(proc) }
container.AddProcess(p)
}
// if successfull, add the container to our list
if err == nil {
for _, p := range container.Processes() {
s.monitorProcess(p.(*process))
}
s.addContainer(container)
log.G(s.ctx).Infof("restored container %s", container.ID())
} }
} }
} }
func (s *ShimRuntime) loadContainerStatus(id string) execution.Status {
cmd := exec.Command(s.runtime, append(s.runtimeArgs, "state", id)...)
out, err := cmd.CombinedOutput()
if err != nil {
return execution.Unknown
}
var st struct{ Status string }
if err := json.NewDecoder(bytes.NewReader(out)).Decode(&st); err != nil {
return execution.Unknown
}
return execution.Status(st.Status)
}

View file

@ -1,82 +0,0 @@
package execution
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
)
const processesDirName = "processes"
type StateDir string
func LoadStateDir(root, id string) (StateDir, error) {
path := filepath.Join(root, id)
if _, err := os.Stat(path); err != nil {
return "", errors.Wrap(err, "could not find container statedir")
}
return StateDir(path), nil
}
func NewStateDir(root, id string) (StateDir, error) {
path := filepath.Join(root, id)
if err := os.Mkdir(path, 0700); err != nil {
return "", errors.Wrap(err, "could not create container statedir")
}
if err := os.Mkdir(StateDir(path).processesDir(), 0700); err != nil {
os.RemoveAll(path)
return "", errors.Wrap(err, "could not create processes statedir")
}
return StateDir(path), nil
}
func (s StateDir) Delete() error {
err := os.RemoveAll(string(s))
if err != nil {
return errors.Wrapf(err, "failed to remove statedir %s", string(s))
}
return nil
}
func (s StateDir) NewProcess(id string) (dir string, err error) {
dir = filepath.Join(s.processesDir(), id)
if err = os.Mkdir(dir, 0700); err != nil {
return "", errors.Wrap(err, "could not create process statedir")
}
return dir, nil
}
func (s StateDir) ProcessDir(id string) string {
return filepath.Join(s.processesDir(), id)
}
func (s StateDir) DeleteProcess(id string) error {
err := os.RemoveAll(filepath.Join(s.processesDir(), id))
if err != nil {
return errors.Wrapf(err, "failed to remove process %d statedir", id)
}
return nil
}
func (s StateDir) Processes() ([]string, error) {
procsDir := s.processesDir()
dirs, err := ioutil.ReadDir(procsDir)
if err != nil {
return nil, errors.Wrap(err, "could not list processes statedir")
}
paths := make([]string, 0)
for _, d := range dirs {
if d.IsDir() {
paths = append(paths, filepath.Join(procsDir, d.Name()))
}
}
return paths, nil
}
func (s StateDir) processesDir() string {
return filepath.Join(string(s), processesDirName)
}

View file

@ -5,6 +5,7 @@ type Status string
const ( const (
Created Status = "created" Created Status = "created"
Paused Status = "paused" Paused Status = "paused"
Pausing Status = "pausing"
Running Status = "running" Running Status = "running"
Stopped Status = "stopped" Stopped Status = "stopped"
Deleted Status = "deleted" Deleted Status = "deleted"