Merge pull request #499 from crosbymichael/state-fixes

Fix various state bugs for pause and destroy
This commit is contained in:
Alexander Morozov 2016-01-25 11:33:59 -08:00
commit 3268a1ea00
5 changed files with 72 additions and 56 deletions

View File

@ -189,29 +189,27 @@ func (c *linuxContainer) Start(process *Process) error {
} }
return newSystemError(err) return newSystemError(err)
} }
c.state = &runningState{
c: c,
}
if doInit { if doInit {
if err := c.updateState(parent); err != nil { if err := c.updateState(parent); err != nil {
return err return err
} }
} else { if c.config.Hooks != nil {
c.state.transition(&nullState{ s := configs.HookState{
c: c, Version: c.config.Version,
s: Running, ID: c.id,
}) Pid: parent.pid(),
} Root: c.config.Rootfs,
if c.config.Hooks != nil { }
s := configs.HookState{ for _, hook := range c.config.Hooks.Poststart {
Version: c.config.Version, if err := hook.Run(s); err != nil {
ID: c.id, if err := parent.terminate(); err != nil {
Pid: parent.pid(), logrus.Warn(err)
Root: c.config.Rootfs, }
} return newSystemError(err)
for _, hook := range c.config.Hooks.Poststart {
if err := hook.Run(s); err != nil {
if err := parent.terminate(); err != nil {
logrus.Warn(err)
} }
return newSystemError(err)
} }
} }
} }
@ -340,6 +338,13 @@ func (c *linuxContainer) Destroy() error {
func (c *linuxContainer) Pause() error { func (c *linuxContainer) Pause() error {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus()
if err != nil {
return err
}
if status != Running {
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
}
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil { if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
return err return err
} }
@ -351,6 +356,13 @@ func (c *linuxContainer) Pause() error {
func (c *linuxContainer) Resume() error { func (c *linuxContainer) Resume() error {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus()
if err != nil {
return err
}
if status != Paused {
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
}
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil { if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
return err return err
} }
@ -939,9 +951,6 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
func (c *linuxContainer) updateState(process parentProcess) error { func (c *linuxContainer) updateState(process parentProcess) error {
c.initProcess = process c.initProcess = process
if err := c.refreshState(); err != nil {
return err
}
state, err := c.currentState() state, err := c.currentState()
if err != nil { if err != nil {
return err return err
@ -1017,35 +1026,36 @@ func (c *linuxContainer) isPaused() (bool, error) {
} }
func (c *linuxContainer) currentState() (*State, error) { func (c *linuxContainer) currentState() (*State, error) {
status, err := c.currentStatus() var (
if err != nil { startTime string
return nil, err externalDescriptors []string
} pid = -1
if status == Destroyed { )
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists) if c.initProcess != nil {
} pid = c.initProcess.pid()
startTime, err := c.initProcess.startTime() startTime, _ = c.initProcess.startTime()
if err != nil { externalDescriptors = c.initProcess.externalDescriptors()
return nil, newSystemError(err)
} }
state := &State{ state := &State{
BaseState: BaseState{ BaseState: BaseState{
ID: c.ID(), ID: c.ID(),
Config: *c.config, Config: *c.config,
InitProcessPid: c.initProcess.pid(), InitProcessPid: pid,
InitProcessStartTime: startTime, InitProcessStartTime: startTime,
}, },
CgroupPaths: c.cgroupManager.GetPaths(), CgroupPaths: c.cgroupManager.GetPaths(),
NamespacePaths: make(map[configs.NamespaceType]string), NamespacePaths: make(map[configs.NamespaceType]string),
ExternalDescriptors: c.initProcess.externalDescriptors(), ExternalDescriptors: externalDescriptors,
} }
for _, ns := range c.config.Namespaces { if pid > 0 {
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid()) for _, ns := range c.config.Namespaces {
} state.NamespacePaths[ns.Type] = ns.GetPath(pid)
for _, nsType := range configs.NamespaceTypes() { }
if _, ok := state.NamespacePaths[nsType]; !ok { for _, nsType := range configs.NamespaceTypes() {
ns := configs.Namespace{Type: nsType} if _, ok := state.NamespacePaths[nsType]; !ok {
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid()) ns := configs.Namespace{Type: nsType}
state.NamespacePaths[ns.Type] = ns.GetPath(pid)
}
} }
} }
return state, nil return state, nil

View File

@ -16,6 +16,7 @@ const (
ContainerPaused ContainerPaused
ContainerNotStopped ContainerNotStopped
ContainerNotRunning ContainerNotRunning
ContainerNotPaused
// Process errors // Process errors
ProcessNotExecuted ProcessNotExecuted
@ -46,6 +47,8 @@ func (c ErrorCode) String() string {
return "Container is not running" return "Container is not running"
case ConsoleExists: case ConsoleExists:
return "Console exists for process" return "Console exists for process"
case ContainerNotPaused:
return "Container is not paused"
default: default:
return "Unknown error" return "Unknown error"
} }

View File

@ -203,6 +203,9 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
root: containerRoot, root: containerRoot,
} }
c.state = &nullState{c: c} c.state = &nullState{c: c}
if err := c.refreshState(); err != nil {
return nil, err
}
return c, nil return c, nil
} }

View File

@ -49,6 +49,7 @@ func destroy(c *linuxContainer) error {
if herr := runPoststopHooks(c); err == nil { if herr := runPoststopHooks(c); err == nil {
err = herr err = herr
} }
c.state = &stoppedState{c: c}
return err return err
} }
@ -116,10 +117,10 @@ func (r *runningState) transition(s containerState) error {
} }
r.c.state = s r.c.state = s
return nil return nil
case *pausedState: case *pausedState, *nullState:
r.c.state = s r.c.state = s
return nil return nil
case *runningState, *nullState: case *runningState:
return nil return nil
} }
return newStateTransitionError(r, s) return newStateTransitionError(r, s)
@ -148,7 +149,7 @@ func (p *pausedState) status() Status {
func (p *pausedState) transition(s containerState) error { func (p *pausedState) transition(s containerState) error {
switch s.(type) { switch s.(type) {
case *runningState: case *runningState, *stoppedState:
p.c.state = s p.c.state = s
return nil return nil
case *pausedState: case *pausedState:
@ -158,6 +159,16 @@ func (p *pausedState) transition(s containerState) error {
} }
func (p *pausedState) destroy() error { func (p *pausedState) destroy() error {
isRunning, err := p.c.isRunning()
if err != nil {
return err
}
if !isRunning {
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
return err
}
return destroy(p.c)
}
return newGenericError(fmt.Errorf("container is paused"), ContainerPaused) return newGenericError(fmt.Errorf("container is paused"), ContainerPaused)
} }
@ -203,12 +214,7 @@ func (n *nullState) status() Status {
} }
func (n *nullState) transition(s containerState) error { func (n *nullState) transition(s containerState) error {
switch s.(type) { n.c.state = s
case *restoredState:
n.c.state = s
default:
// do nothing for null states
}
return nil return nil
} }

View File

@ -49,19 +49,13 @@ func TestPausedStateTransition(t *testing.T) {
valid := []containerState{ valid := []containerState{
&pausedState{}, &pausedState{},
&runningState{}, &runningState{},
&stoppedState{},
} }
for _, v := range valid { for _, v := range valid {
if err := s.transition(v); err != nil { if err := s.transition(v); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
err := s.transition(&stoppedState{})
if err == nil {
t.Fatal("transition to stopped state should fail")
}
if !isStateTransitionError(err) {
t.Fatal("expected stateTransitionError")
}
} }
func TestRestoredStateTransition(t *testing.T) { func TestRestoredStateTransition(t *testing.T) {