Add mutex around stateful container operations
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
2b45128091
commit
4c43b0f498
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue