Add mutex around stateful container operations

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-02-13 14:41:37 -08:00
parent 2b45128091
commit 4c43b0f498
1 changed files with 65 additions and 40 deletions

View File

@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path/filepath"
"sync"
"syscall"
"github.com/docker/libcontainer/cgroups"
@ -22,6 +23,7 @@ type linuxContainer struct {
cgroupManager cgroups.Manager
initArgs []string
initProcess parentProcess
m sync.Mutex
}
// ID returns the container's unique ID
@ -35,46 +37,15 @@ func (c *linuxContainer) Config() configs.Config {
}
func (c *linuxContainer) Status() (Status, error) {
if c.initProcess == nil {
return Destroyed, nil
}
// return Running if the init process is alive
if err := syscall.Kill(c.initProcess.pid(), 0); err != nil {
if err == syscall.ESRCH {
return Destroyed, nil
}
return 0, newSystemError(err)
}
if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen {
return Paused, nil
}
return Running, nil
c.m.Lock()
defer c.m.Unlock()
return c.currentStatus()
}
func (c *linuxContainer) State() (*State, error) {
status, err := c.Status()
if err != nil {
return nil, err
}
if status == Destroyed {
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists)
}
startTime, err := c.initProcess.startTime()
if err != nil {
return nil, newSystemError(err)
}
state := &State{
ID: c.ID(),
Config: *c.config,
InitProcessPid: c.initProcess.pid(),
InitProcessStartTime: startTime,
CgroupPaths: c.cgroupManager.GetPaths(),
NamespacePaths: make(map[string]string),
}
for _, ns := range c.config.Namespaces {
state.NamespacePaths[string(ns.Type)] = ns.GetPath(c.initProcess.pid())
}
return state, nil
c.m.Lock()
defer c.m.Unlock()
return c.currentState()
}
func (c *linuxContainer) Processes() ([]int, error) {
@ -109,7 +80,9 @@ func (c *linuxContainer) Stats() (*Stats, error) {
}
func (c *linuxContainer) Start(process *Process) (int, error) {
status, err := c.Status()
c.m.Lock()
defer c.m.Unlock()
status, err := c.currentStatus()
if err != nil {
return -1, err
}
@ -126,6 +99,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
return -1, newSystemError(err)
}
if doInit {
c.updateState(parent)
}
return parent.pid(), nil
@ -222,7 +196,9 @@ func newPipe() (parent *os.File, child *os.File, err error) {
}
func (c *linuxContainer) Destroy() error {
status, err := c.Status()
c.m.Lock()
defer c.m.Unlock()
status, err := c.currentStatus()
if err != nil {
return err
}
@ -243,14 +219,20 @@ func (c *linuxContainer) Destroy() error {
}
func (c *linuxContainer) Pause() error {
c.m.Lock()
defer c.m.Unlock()
return c.cgroupManager.Freeze(configs.Frozen)
}
func (c *linuxContainer) Resume() error {
c.m.Lock()
defer c.m.Unlock()
return c.cgroupManager.Freeze(configs.Thawed)
}
func (c *linuxContainer) Signal(signal os.Signal) error {
c.m.Lock()
defer c.m.Unlock()
if c.initProcess == nil {
return newGenericError(nil, ContainerNotRunning)
}
@ -263,7 +245,7 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
func (c *linuxContainer) updateState(process parentProcess) error {
c.initProcess = process
state, err := c.State()
state, err := c.currentState()
if err != nil {
return err
}
@ -274,3 +256,46 @@ func (c *linuxContainer) updateState(process parentProcess) error {
defer f.Close()
return json.NewEncoder(f).Encode(state)
}
func (c *linuxContainer) currentStatus() (Status, error) {
if c.initProcess == nil {
return Destroyed, nil
}
// return Running if the init process is alive
if err := syscall.Kill(c.initProcess.pid(), 0); err != nil {
if err == syscall.ESRCH {
return Destroyed, nil
}
return 0, newSystemError(err)
}
if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen {
return Paused, nil
}
return Running, nil
}
func (c *linuxContainer) currentState() (*State, error) {
status, err := c.currentStatus()
if err != nil {
return nil, err
}
if status == Destroyed {
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists)
}
startTime, err := c.initProcess.startTime()
if err != nil {
return nil, newSystemError(err)
}
state := &State{
ID: c.ID(),
Config: *c.config,
InitProcessPid: c.initProcess.pid(),
InitProcessStartTime: startTime,
CgroupPaths: c.cgroupManager.GetPaths(),
NamespacePaths: make(map[string]string),
}
for _, ns := range c.config.Namespaces {
state.NamespacePaths[string(ns.Type)] = ns.GetPath(c.initProcess.pid())
}
return state, nil
}