Skip to content

Commit 556f798

Browse files
committed
Fix various state bugs for pause and destroy
There were issues where a process could die before pausing completed leaving the container in an inconsistent state and unable to be destoryed. This makes sure that if the container is paused and the process is dead it will unfreeze the cgroup before removing them. Signed-off-by: Michael Crosby <[email protected]>
1 parent 27132f2 commit 556f798

File tree

4 files changed

+68
-50
lines changed

4 files changed

+68
-50
lines changed

libcontainer/container_linux.go

+50-40
Original file line numberDiff line numberDiff line change
@@ -189,29 +189,27 @@ func (c *linuxContainer) Start(process *Process) error {
189189
}
190190
return newSystemError(err)
191191
}
192+
c.state = &runningState{
193+
c: c,
194+
}
192195
if doInit {
193196
if err := c.updateState(parent); err != nil {
194197
return err
195198
}
196-
} else {
197-
c.state.transition(&nullState{
198-
c: c,
199-
s: Running,
200-
})
201-
}
202-
if c.config.Hooks != nil {
203-
s := configs.HookState{
204-
Version: c.config.Version,
205-
ID: c.id,
206-
Pid: parent.pid(),
207-
Root: c.config.Rootfs,
208-
}
209-
for _, hook := range c.config.Hooks.Poststart {
210-
if err := hook.Run(s); err != nil {
211-
if err := parent.terminate(); err != nil {
212-
logrus.Warn(err)
199+
if c.config.Hooks != nil {
200+
s := configs.HookState{
201+
Version: c.config.Version,
202+
ID: c.id,
203+
Pid: parent.pid(),
204+
Root: c.config.Rootfs,
205+
}
206+
for _, hook := range c.config.Hooks.Poststart {
207+
if err := hook.Run(s); err != nil {
208+
if err := parent.terminate(); err != nil {
209+
logrus.Warn(err)
210+
}
211+
return newSystemError(err)
213212
}
214-
return newSystemError(err)
215213
}
216214
}
217215
}
@@ -340,6 +338,13 @@ func (c *linuxContainer) Destroy() error {
340338
func (c *linuxContainer) Pause() error {
341339
c.m.Lock()
342340
defer c.m.Unlock()
341+
status, err := c.currentStatus()
342+
if err != nil {
343+
return err
344+
}
345+
if status != Running {
346+
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
347+
}
343348
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
344349
return err
345350
}
@@ -351,6 +356,13 @@ func (c *linuxContainer) Pause() error {
351356
func (c *linuxContainer) Resume() error {
352357
c.m.Lock()
353358
defer c.m.Unlock()
359+
status, err := c.currentStatus()
360+
if err != nil {
361+
return err
362+
}
363+
if status != Paused {
364+
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
365+
}
354366
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
355367
return err
356368
}
@@ -939,9 +951,6 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
939951

940952
func (c *linuxContainer) updateState(process parentProcess) error {
941953
c.initProcess = process
942-
if err := c.refreshState(); err != nil {
943-
return err
944-
}
945954
state, err := c.currentState()
946955
if err != nil {
947956
return err
@@ -1017,35 +1026,36 @@ func (c *linuxContainer) isPaused() (bool, error) {
10171026
}
10181027

10191028
func (c *linuxContainer) currentState() (*State, error) {
1020-
status, err := c.currentStatus()
1021-
if err != nil {
1022-
return nil, err
1023-
}
1024-
if status == Destroyed {
1025-
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists)
1026-
}
1027-
startTime, err := c.initProcess.startTime()
1028-
if err != nil {
1029-
return nil, newSystemError(err)
1029+
var (
1030+
startTime string
1031+
externalDescriptors []string
1032+
pid = -1
1033+
)
1034+
if c.initProcess != nil {
1035+
pid = c.initProcess.pid()
1036+
startTime, _ = c.initProcess.startTime()
1037+
externalDescriptors = c.initProcess.externalDescriptors()
10301038
}
10311039
state := &State{
10321040
BaseState: BaseState{
10331041
ID: c.ID(),
10341042
Config: *c.config,
1035-
InitProcessPid: c.initProcess.pid(),
1043+
InitProcessPid: pid,
10361044
InitProcessStartTime: startTime,
10371045
},
10381046
CgroupPaths: c.cgroupManager.GetPaths(),
10391047
NamespacePaths: make(map[configs.NamespaceType]string),
1040-
ExternalDescriptors: c.initProcess.externalDescriptors(),
1041-
}
1042-
for _, ns := range c.config.Namespaces {
1043-
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid())
1048+
ExternalDescriptors: externalDescriptors,
10441049
}
1045-
for _, nsType := range configs.NamespaceTypes() {
1046-
if _, ok := state.NamespacePaths[nsType]; !ok {
1047-
ns := configs.Namespace{Type: nsType}
1048-
state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid())
1050+
if pid > 0 {
1051+
for _, ns := range c.config.Namespaces {
1052+
state.NamespacePaths[ns.Type] = ns.GetPath(pid)
1053+
}
1054+
for _, nsType := range configs.NamespaceTypes() {
1055+
if _, ok := state.NamespacePaths[nsType]; !ok {
1056+
ns := configs.Namespace{Type: nsType}
1057+
state.NamespacePaths[ns.Type] = ns.GetPath(pid)
1058+
}
10491059
}
10501060
}
10511061
return state, nil

libcontainer/error.go

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
ContainerPaused
1717
ContainerNotStopped
1818
ContainerNotRunning
19+
ContainerNotPaused
1920

2021
// Process errors
2122
ProcessNotExecuted
@@ -46,6 +47,8 @@ func (c ErrorCode) String() string {
4647
return "Container is not running"
4748
case ConsoleExists:
4849
return "Console exists for process"
50+
case ContainerNotPaused:
51+
return "Container is not paused"
4952
default:
5053
return "Unknown error"
5154
}

libcontainer/state_linux.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func destroy(c *linuxContainer) error {
4949
if herr := runPoststopHooks(c); err == nil {
5050
err = herr
5151
}
52+
c.state = &stoppedState{c: c}
5253
return err
5354
}
5455

@@ -116,10 +117,10 @@ func (r *runningState) transition(s containerState) error {
116117
}
117118
r.c.state = s
118119
return nil
119-
case *pausedState:
120+
case *pausedState, *nullState:
120121
r.c.state = s
121122
return nil
122-
case *runningState, *nullState:
123+
case *runningState:
123124
return nil
124125
}
125126
return newStateTransitionError(r, s)
@@ -148,7 +149,7 @@ func (p *pausedState) status() Status {
148149

149150
func (p *pausedState) transition(s containerState) error {
150151
switch s.(type) {
151-
case *runningState:
152+
case *runningState, *stoppedState:
152153
p.c.state = s
153154
return nil
154155
case *pausedState:
@@ -158,6 +159,16 @@ func (p *pausedState) transition(s containerState) error {
158159
}
159160

160161
func (p *pausedState) destroy() error {
162+
isRunning, err := p.c.isRunning()
163+
if err != nil {
164+
return err
165+
}
166+
if !isRunning {
167+
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
168+
return err
169+
}
170+
return destroy(p.c)
171+
}
161172
return newGenericError(fmt.Errorf("container is paused"), ContainerPaused)
162173
}
163174

libcontainer/state_linux_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,13 @@ func TestPausedStateTransition(t *testing.T) {
4949
valid := []containerState{
5050
&pausedState{},
5151
&runningState{},
52+
&stoppedState{},
5253
}
5354
for _, v := range valid {
5455
if err := s.transition(v); err != nil {
5556
t.Fatal(err)
5657
}
5758
}
58-
err := s.transition(&stoppedState{})
59-
if err == nil {
60-
t.Fatal("transition to stopped state should fail")
61-
}
62-
if !isStateTransitionError(err) {
63-
t.Fatal("expected stateTransitionError")
64-
}
6559
}
6660

6761
func TestRestoredStateTransition(t *testing.T) {

0 commit comments

Comments
 (0)