moby/integration/container/checkpoint_test.go
Sebastiaan van Stijn 26be2bc6b9
integration/container: use consistent name for api-client
The `client` variable was colliding with the `client` import in various
files. While it didn't conflict in all files, there was inconsistency
in the naming, sometimes using the confusing `cli` name (it's not the
"cli"), and such names can easily start spreading (through copy/paste,
or "code by example").

Let's make a one-time pass through all of them in this package to use
the same name.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-08-11 13:51:57 +02:00

164 lines
4.7 KiB
Go

package container // import "github.com/docker/docker/integration/container"
import (
"context"
"os/exec"
"regexp"
"sort"
"testing"
"time"
"github.com/docker/docker/api/types"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/testutil/request"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
//nolint:unused // false positive: linter detects this as "unused"
func containerExec(t *testing.T, client client.APIClient, cID string, cmd []string) {
t.Logf("Exec: %s", cmd)
ctx := context.Background()
r, err := container.Exec(ctx, client, cID, cmd)
assert.NilError(t, err)
t.Log(r.Combined())
assert.Equal(t, r.ExitCode, 0)
}
func TestCheckpoint(t *testing.T) {
t.Skip("TestCheckpoint is broken; see https://github.com/moby/moby/issues/38963")
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
defer setupTest(t)()
cmd := exec.Command("criu", "check")
stdoutStderr, err := cmd.CombinedOutput()
t.Logf("%s", stdoutStderr)
assert.NilError(t, err)
ctx := context.Background()
apiClient := request.NewAPIClient(t)
mnt := mounttypes.Mount{
Type: mounttypes.TypeTmpfs,
Target: "/tmp",
}
t.Log("Start a container")
cID := container.Run(ctx, t, apiClient, container.WithMount(mnt))
poll.WaitOn(t,
container.IsInState(ctx, apiClient, cID, "running"),
poll.WithDelay(100*time.Millisecond),
)
cptOpt := types.CheckpointCreateOptions{
Exit: false,
CheckpointID: "test",
}
{
// FIXME: ipv6 iptables modules are not uploaded in the test environment
cmd := exec.Command("bash", "-c", "set -x; "+
"mount --bind $(type -P true) $(type -P ip6tables-restore) && "+
"mount --bind $(type -P true) $(type -P ip6tables-save)")
stdoutStderr, err = cmd.CombinedOutput()
t.Logf("%s", stdoutStderr)
assert.NilError(t, err)
defer func() {
cmd := exec.Command("bash", "-c", "set -x; "+
"umount -c -i -l $(type -P ip6tables-restore); "+
"umount -c -i -l $(type -P ip6tables-save)")
stdoutStderr, err = cmd.CombinedOutput()
t.Logf("%s", stdoutStderr)
assert.NilError(t, err)
}()
}
t.Log("Do a checkpoint and leave the container running")
err = apiClient.CheckpointCreate(ctx, cID, cptOpt)
if err != nil {
// An error can contain a path to a dump file
t.Log(err)
re := regexp.MustCompile("path= (.*): ")
m := re.FindStringSubmatch(err.Error())
if len(m) >= 2 {
dumpLog := m[1]
t.Logf("%s", dumpLog)
cmd := exec.Command("cat", dumpLog)
stdoutStderr, err = cmd.CombinedOutput()
t.Logf("%s", stdoutStderr)
}
}
assert.NilError(t, err)
inspect, err := apiClient.ContainerInspect(ctx, cID)
assert.NilError(t, err)
assert.Check(t, is.Equal(true, inspect.State.Running))
checkpoints, err := apiClient.CheckpointList(ctx, cID, types.CheckpointListOptions{})
assert.NilError(t, err)
assert.Equal(t, len(checkpoints), 1)
assert.Equal(t, checkpoints[0].Name, "test")
// Create a test file on a tmpfs mount.
containerExec(t, apiClient, cID, []string{"touch", "/tmp/test-file"})
// Do a second checkpoint
cptOpt = types.CheckpointCreateOptions{
Exit: true,
CheckpointID: "test2",
}
t.Log("Do a checkpoint and stop the container")
err = apiClient.CheckpointCreate(ctx, cID, cptOpt)
assert.NilError(t, err)
poll.WaitOn(t,
container.IsInState(ctx, apiClient, cID, "exited"),
poll.WithDelay(100*time.Millisecond),
)
inspect, err = apiClient.ContainerInspect(ctx, cID)
assert.NilError(t, err)
assert.Check(t, is.Equal(false, inspect.State.Running))
// Check that both checkpoints are listed.
checkpoints, err = apiClient.CheckpointList(ctx, cID, types.CheckpointListOptions{})
assert.NilError(t, err)
assert.Equal(t, len(checkpoints), 2)
cptNames := make([]string, 2)
for i, c := range checkpoints {
cptNames[i] = c.Name
}
sort.Strings(cptNames)
assert.Equal(t, cptNames[0], "test")
assert.Equal(t, cptNames[1], "test2")
// Restore the container from a second checkpoint.
startOpt := types.ContainerStartOptions{
CheckpointID: "test2",
}
t.Log("Restore the container")
err = apiClient.ContainerStart(ctx, cID, startOpt)
assert.NilError(t, err)
inspect, err = apiClient.ContainerInspect(ctx, cID)
assert.NilError(t, err)
assert.Check(t, is.Equal(true, inspect.State.Running))
// Check that the test file has been restored.
containerExec(t, apiClient, cID, []string{"test", "-f", "/tmp/test-file"})
for _, id := range []string{"test", "test2"} {
cptDelOpt := types.CheckpointDeleteOptions{
CheckpointID: id,
}
err = apiClient.CheckpointDelete(ctx, cID, cptDelOpt)
assert.NilError(t, err)
}
}