liverestore: Don't remove --rm containers on restart

When live-restore is enabled, containers with autoremove enabled
shouldn't be forcibly killed when engine restarts.
They still should be removed if they exited while the engine was down
though.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2023-11-28 10:21:25 +01:00
parent f6533a1df1
commit c5ea3d595c
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A
2 changed files with 67 additions and 3 deletions

View file

@ -495,9 +495,12 @@ func (daemon *Daemon) restore(cfg *configStore) error {
restartContainers[c] = make(chan struct{})
mapLock.Unlock()
} else if c.HostConfig != nil && c.HostConfig.AutoRemove {
mapLock.Lock()
removeContainers[c.ID] = c
mapLock.Unlock()
// Remove the container if live-restore is disabled or if the container has already exited.
if !cfg.LiveRestoreEnabled || !alive {
mapLock.Lock()
removeContainers[c.ID] = c
mapLock.Unlock()
}
}
c.Lock()

View file

@ -13,6 +13,7 @@ import (
"strings"
"syscall"
"testing"
"time"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
@ -381,6 +382,66 @@ func TestLiveRestore(t *testing.T) {
_ = testutil.StartSpan(baseContext, t)
t.Run("volume references", testLiveRestoreVolumeReferences)
t.Run("autoremove", testLiveRestoreAutoRemove)
}
func testLiveRestoreAutoRemove(t *testing.T) {
skip.If(t, testEnv.IsRootless(), "restarted rootless daemon will have a new process namespace")
t.Parallel()
ctx := testutil.StartSpan(baseContext, t)
run := func(t *testing.T) (*daemon.Daemon, func(), string) {
d := daemon.New(t)
d.StartWithBusybox(ctx, t, "--live-restore", "--iptables=false")
t.Cleanup(func() {
d.Stop(t)
d.Cleanup(t)
})
tmpDir := t.TempDir()
apiClient := d.NewClientT(t)
cID := container.Run(ctx, t, apiClient,
container.WithBind(tmpDir, "/v"),
// Run until a 'stop' file is created.
container.WithCmd("sh", "-c", "while [ ! -f /v/stop ]; do sleep 0.1; done"),
container.WithAutoRemove)
t.Cleanup(func() { apiClient.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true}) })
finishContainer := func() {
file, err := os.Create(filepath.Join(tmpDir, "stop"))
assert.NilError(t, err, "Failed to create 'stop' file")
file.Close()
}
return d, finishContainer, cID
}
t.Run("engine restart shouldnt kill alive containers", func(t *testing.T) {
d, finishContainer, cID := run(t)
d.Restart(t, "--live-restore", "--iptables=false")
apiClient := d.NewClientT(t)
_, err := apiClient.ContainerInspect(ctx, cID)
assert.NilError(t, err, "Container shouldn't be removed after engine restart")
finishContainer()
poll.WaitOn(t, container.IsRemoved(ctx, apiClient, cID))
})
t.Run("engine restart should remove containers that exited", func(t *testing.T) {
d, finishContainer, cID := run(t)
d.Stop(t)
finishContainer()
time.Sleep(time.Millisecond * 200)
d.Start(t, "--live-restore", "--iptables=false")
poll.WaitOn(t, container.IsRemoved(ctx, d.NewClientT(t), cID))
})
}
func testLiveRestoreVolumeReferences(t *testing.T) {