On startup, actually shutdown the container.

When a container is left running after the daemon exits (e.g. the daemon
is SIGKILL'd or crashes), it should stop any running containers when the
daemon starts back up.

What actually happens is the daemon only sends the container's
configured stop signal and does not check if it has exited.
If the container does not actually exit then it is left running.

This fixes this unexpected behavior by calling the same function to shut
down the container that the daemon shutdown process does.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2020-04-07 17:03:32 -07:00
parent 9c71a2be31
commit ced91bee4b
2 changed files with 53 additions and 1 deletions

View File

@ -349,10 +349,11 @@ func (daemon *Daemon) restore() error {
return return
} }
} else if !daemon.configStore.LiveRestoreEnabled { } else if !daemon.configStore.LiveRestoreEnabled {
if err := daemon.kill(c, c.StopSignal()); err != nil && !errdefs.IsNotFound(err) { if err := daemon.shutdownContainer(c); err != nil && !errdefs.IsNotFound(err) {
logrus.WithError(err).WithField("container", c.ID).Error("error shutting down container") logrus.WithError(err).WithField("container", c.ID).Error("error shutting down container")
return return
} }
c.ResetRestartManager(false)
} }
if c.IsRunning() || c.IsPaused() { if c.IsRunning() || c.IsPaused() {

View File

@ -0,0 +1,51 @@
package container
import (
"context"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/testutil/daemon"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
// Make sure a container that does not exit when it upon receiving it's stop signal is actually shutdown on daemon
// startup.
func TestContainerKillOnDaemonStart(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
skip.If(t, testEnv.IsRootless, "scenario doesn't work with rootless mode")
t.Parallel()
d := daemon.New(t)
defer d.Cleanup(t)
d.StartWithBusybox(t, "--iptables=false")
defer d.Stop(t)
client := d.NewClientT(t)
ctx := context.Background()
// The intention of this container is to ignore stop signals.
// Sadly this means the test will take longer, but at least this test can be parallelized.
id := container.Run(ctx, t, client, container.WithCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done"))
defer func() {
err := client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
assert.NilError(t, err)
}()
inspect, err := client.ContainerInspect(ctx, id)
assert.NilError(t, err)
assert.Assert(t, inspect.State.Running)
assert.NilError(t, d.Kill())
d.Start(t)
inspect, err = client.ContainerInspect(ctx, id)
assert.Check(t, is.Nil(err))
assert.Assert(t, !inspect.State.Running)
}