Merge pull request #34027 from cpuguy83/15853_allow_stopping_paused_container
Allow stopping of paused container
This commit is contained in:
commit
6fdb2fb069
|
@ -43,7 +43,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
"github.com/docker/docker/pkg/registrar"
|
"github.com/docker/docker/pkg/registrar"
|
||||||
"github.com/docker/docker/pkg/signal"
|
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/pkg/truncindex"
|
"github.com/docker/docker/pkg/truncindex"
|
||||||
|
@ -838,42 +837,7 @@ func (daemon *Daemon) waitForStartupDone() {
|
||||||
|
|
||||||
func (daemon *Daemon) shutdownContainer(c *container.Container) error {
|
func (daemon *Daemon) shutdownContainer(c *container.Container) error {
|
||||||
stopTimeout := c.StopTimeout()
|
stopTimeout := c.StopTimeout()
|
||||||
// TODO(windows): Handle docker restart with paused containers
|
|
||||||
if c.IsPaused() {
|
|
||||||
// To terminate a process in freezer cgroup, we should send
|
|
||||||
// SIGTERM to this process then unfreeze it, and the process will
|
|
||||||
// force to terminate immediately.
|
|
||||||
logrus.Debugf("Found container %s is paused, sending SIGTERM before unpausing it", c.ID)
|
|
||||||
sig, ok := signal.SignalMap["TERM"]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("System does not support SIGTERM")
|
|
||||||
}
|
|
||||||
if err := daemon.kill(c, int(sig)); err != nil {
|
|
||||||
return fmt.Errorf("sending SIGTERM to container %s with error: %v", c.ID, err)
|
|
||||||
}
|
|
||||||
if err := daemon.containerUnpause(c); err != nil {
|
|
||||||
return fmt.Errorf("Failed to unpause container %s with error: %v", c.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(stopTimeout)*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Wait with timeout for container to exit.
|
|
||||||
if status := <-c.Wait(ctx, container.WaitConditionNotRunning); status.Err() != nil {
|
|
||||||
logrus.Debugf("container %s failed to exit in %d second of SIGTERM, sending SIGKILL to force", c.ID, stopTimeout)
|
|
||||||
sig, ok := signal.SignalMap["KILL"]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("System does not support SIGKILL")
|
|
||||||
}
|
|
||||||
if err := daemon.kill(c, int(sig)); err != nil {
|
|
||||||
logrus.Errorf("Failed to SIGKILL container %s", c.ID)
|
|
||||||
}
|
|
||||||
// Wait for exit again without a timeout.
|
|
||||||
// Explicitly ignore the result.
|
|
||||||
_ = <-c.Wait(context.Background(), container.WaitConditionNotRunning)
|
|
||||||
return status.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If container failed to exit in stopTimeout seconds of SIGTERM, then using the force
|
// If container failed to exit in stopTimeout seconds of SIGTERM, then using the force
|
||||||
if err := daemon.containerStop(c, stopTimeout); err != nil {
|
if err := daemon.containerStop(c, stopTimeout); err != nil {
|
||||||
return fmt.Errorf("Failed to stop container %s with error: %v", c.ID, err)
|
return fmt.Errorf("Failed to stop container %s with error: %v", c.ID, err)
|
||||||
|
|
|
@ -60,15 +60,11 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
// We could unpause the container for them rather than returning this error
|
|
||||||
if container.Paused {
|
|
||||||
return fmt.Errorf("Container %s is paused. Unpause the container before stopping or killing", container.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !container.Running {
|
if !container.Running {
|
||||||
return errNotRunning{container.ID}
|
return errNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var unpause bool
|
||||||
if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
|
if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
|
||||||
containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
|
containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,9 +72,11 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
}
|
}
|
||||||
if containerStopSignal == syscall.Signal(sig) {
|
if containerStopSignal == syscall.Signal(sig) {
|
||||||
container.ExitOnNext()
|
container.ExitOnNext()
|
||||||
|
unpause = container.Paused
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
container.ExitOnNext()
|
container.ExitOnNext()
|
||||||
|
unpause = container.Paused
|
||||||
}
|
}
|
||||||
|
|
||||||
if !daemon.IsShuttingDown() {
|
if !daemon.IsShuttingDown() {
|
||||||
|
@ -98,11 +96,19 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
if strings.Contains(err.Error(), "container not found") ||
|
if strings.Contains(err.Error(), "container not found") ||
|
||||||
strings.Contains(err.Error(), "no such process") {
|
strings.Contains(err.Error(), "no such process") {
|
||||||
logrus.Warnf("container kill failed because of 'container not found' or 'no such process': %s", err.Error())
|
logrus.Warnf("container kill failed because of 'container not found' or 'no such process': %s", err.Error())
|
||||||
|
unpause = false
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if unpause {
|
||||||
|
// above kill signal will be sent once resume is finished
|
||||||
|
if err := daemon.containerd.Resume(container.ID); err != nil {
|
||||||
|
logrus.Warn("Cannot unpause container %s: %s", container.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
attributes := map[string]string{
|
attributes := map[string]string{
|
||||||
"signal": fmt.Sprintf("%d", sig),
|
"signal": fmt.Sprintf("%d", sig),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/integration-cli/checker"
|
"github.com/docker/docker/integration-cli/checker"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
|
@ -65,3 +66,13 @@ func (s *DockerSuite) TestPauseFailsOnWindowsServerContainers(c *check.C) {
|
||||||
out, _, _ := dockerCmdWithError("pause", "test")
|
out, _, _ := dockerCmdWithError("pause", "test")
|
||||||
c.Assert(out, checker.Contains, "cannot pause Windows Server Containers")
|
c.Assert(out, checker.Contains, "cannot pause Windows Server Containers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestStopPausedContainer(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
|
id := runSleepingContainer(c)
|
||||||
|
cli.WaitRun(c, id)
|
||||||
|
cli.DockerCmd(c, "pause", id)
|
||||||
|
cli.DockerCmd(c, "stop", id)
|
||||||
|
cli.WaitForInspectResult(c, id, "{{.State.Running}}", "false", 30*time.Second)
|
||||||
|
}
|
||||||
|
|
|
@ -396,7 +396,7 @@ func runSleepingContainerInImage(c *check.C, image string, extraArgs ...string)
|
||||||
args = append(args, extraArgs...)
|
args = append(args, extraArgs...)
|
||||||
args = append(args, image)
|
args = append(args, image)
|
||||||
args = append(args, sleepCommandForDaemonPlatform()...)
|
args = append(args, sleepCommandForDaemonPlatform()...)
|
||||||
return cli.DockerCmd(c, args...).Combined()
|
return strings.TrimSpace(cli.DockerCmd(c, args...).Combined())
|
||||||
}
|
}
|
||||||
|
|
||||||
// minimalBaseImage returns the name of the minimal base image for the current
|
// minimalBaseImage returns the name of the minimal base image for the current
|
||||||
|
|
Loading…
Reference in New Issue