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"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sync"
"syscall" "syscall"
"github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups"
@ -22,6 +23,7 @@ type linuxContainer struct {
cgroupManager cgroups.Manager cgroupManager cgroups.Manager
initArgs []string initArgs []string
initProcess parentProcess initProcess parentProcess
m sync.Mutex
} }
// ID returns the container's unique ID // ID returns the container's unique ID
@ -35,46 +37,15 @@ func (c *linuxContainer) Config() configs.Config {
} }
func (c *linuxContainer) Status() (Status, error) { func (c *linuxContainer) Status() (Status, error) {
if c.initProcess == nil { c.m.Lock()
return Destroyed, nil defer c.m.Unlock()
} return c.currentStatus()
// 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) State() (*State, error) { func (c *linuxContainer) State() (*State, error) {
status, err := c.Status() c.m.Lock()
if err != nil { defer c.m.Unlock()
return nil, err return c.currentState()
}
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
} }
func (c *linuxContainer) Processes() ([]int, error) { func (c *linuxContainer) Processes() ([]int, error) {
@ -109,7 +80,9 @@ func (c *linuxContainer) Stats() (*Stats, error) {
} }
func (c *linuxContainer) Start(process *Process) (int, 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 { if err != nil {
return -1, err return -1, err
} }
@ -126,6 +99,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
return -1, newSystemError(err) return -1, newSystemError(err)
} }
if doInit { if doInit {
c.updateState(parent) c.updateState(parent)
} }
return parent.pid(), nil return parent.pid(), nil
@ -222,7 +196,9 @@ func newPipe() (parent *os.File, child *os.File, err error) {
} }
func (c *linuxContainer) Destroy() error { func (c *linuxContainer) Destroy() error {
status, err := c.Status() c.m.Lock()
defer c.m.Unlock()
status, err := c.currentStatus()
if err != nil { if err != nil {
return err return err
} }
@ -243,14 +219,20 @@ func (c *linuxContainer) Destroy() error {
} }
func (c *linuxContainer) Pause() error { func (c *linuxContainer) Pause() error {
c.m.Lock()
defer c.m.Unlock()
return c.cgroupManager.Freeze(configs.Frozen) return c.cgroupManager.Freeze(configs.Frozen)
} }
func (c *linuxContainer) Resume() error { func (c *linuxContainer) Resume() error {
c.m.Lock()
defer c.m.Unlock()
return c.cgroupManager.Freeze(configs.Thawed) return c.cgroupManager.Freeze(configs.Thawed)
} }
func (c *linuxContainer) Signal(signal os.Signal) error { func (c *linuxContainer) Signal(signal os.Signal) error {
c.m.Lock()
defer c.m.Unlock()
if c.initProcess == nil { if c.initProcess == nil {
return newGenericError(nil, ContainerNotRunning) return newGenericError(nil, ContainerNotRunning)
} }
@ -263,7 +245,7 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
func (c *linuxContainer) updateState(process parentProcess) error { func (c *linuxContainer) updateState(process parentProcess) error {
c.initProcess = process c.initProcess = process
state, err := c.State() state, err := c.currentState()
if err != nil { if err != nil {
return err return err
} }
@ -274,3 +256,46 @@ func (c *linuxContainer) updateState(process parentProcess) error {
defer f.Close() defer f.Close()
return json.NewEncoder(f).Encode(state) 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
}