checkpoint_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package container // import "github.com/docker/docker/integration/container"
  2. import (
  3. "context"
  4. "fmt"
  5. "os/exec"
  6. "regexp"
  7. "sort"
  8. "testing"
  9. "time"
  10. "github.com/docker/docker/api/types"
  11. mounttypes "github.com/docker/docker/api/types/mount"
  12. "github.com/docker/docker/client"
  13. "github.com/docker/docker/integration/internal/container"
  14. "github.com/docker/docker/testutil/request"
  15. "gotest.tools/v3/assert"
  16. is "gotest.tools/v3/assert/cmp"
  17. "gotest.tools/v3/poll"
  18. "gotest.tools/v3/skip"
  19. )
  20. //nolint:unused // false positive: linter detects this as "unused"
  21. func containerExec(t *testing.T, client client.APIClient, cID string, cmd []string) {
  22. t.Logf("Exec: %s", cmd)
  23. ctx := context.Background()
  24. r, err := container.Exec(ctx, client, cID, cmd)
  25. assert.NilError(t, err)
  26. t.Log(r.Combined())
  27. assert.Equal(t, r.ExitCode, 0)
  28. }
  29. func TestCheckpoint(t *testing.T) {
  30. t.Skip("TestCheckpoint is broken; see https://github.com/moby/moby/issues/38963")
  31. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  32. skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
  33. defer setupTest(t)()
  34. cmd := exec.Command("criu", "check")
  35. stdoutStderr, err := cmd.CombinedOutput()
  36. t.Logf("%s", stdoutStderr)
  37. assert.NilError(t, err)
  38. ctx := context.Background()
  39. client := request.NewAPIClient(t)
  40. mnt := mounttypes.Mount{
  41. Type: mounttypes.TypeTmpfs,
  42. Target: "/tmp",
  43. }
  44. t.Log("Start a container")
  45. cID := container.Run(ctx, t, client, container.WithMount(mnt))
  46. poll.WaitOn(t,
  47. container.IsInState(ctx, client, cID, "running"),
  48. poll.WithDelay(100*time.Millisecond),
  49. )
  50. cptOpt := types.CheckpointCreateOptions{
  51. Exit: false,
  52. CheckpointID: "test",
  53. }
  54. {
  55. // FIXME: ipv6 iptables modules are not uploaded in the test environment
  56. cmd := exec.Command("bash", "-c", "set -x; "+
  57. "mount --bind $(type -P true) $(type -P ip6tables-restore) && "+
  58. "mount --bind $(type -P true) $(type -P ip6tables-save)")
  59. stdoutStderr, err = cmd.CombinedOutput()
  60. t.Logf("%s", stdoutStderr)
  61. assert.NilError(t, err)
  62. defer func() {
  63. cmd := exec.Command("bash", "-c", "set -x; "+
  64. "umount -c -i -l $(type -P ip6tables-restore); "+
  65. "umount -c -i -l $(type -P ip6tables-save)")
  66. stdoutStderr, err = cmd.CombinedOutput()
  67. t.Logf("%s", stdoutStderr)
  68. assert.NilError(t, err)
  69. }()
  70. }
  71. t.Log("Do a checkpoint and leave the container running")
  72. err = client.CheckpointCreate(ctx, cID, cptOpt)
  73. if err != nil {
  74. // An error can contain a path to a dump file
  75. t.Logf("%s", err)
  76. re := regexp.MustCompile("path= (.*): ")
  77. m := re.FindStringSubmatch(fmt.Sprintf("%s", err))
  78. if len(m) >= 2 {
  79. dumpLog := m[1]
  80. t.Logf("%s", dumpLog)
  81. cmd := exec.Command("cat", dumpLog)
  82. stdoutStderr, err = cmd.CombinedOutput()
  83. t.Logf("%s", stdoutStderr)
  84. }
  85. }
  86. assert.NilError(t, err)
  87. inspect, err := client.ContainerInspect(ctx, cID)
  88. assert.NilError(t, err)
  89. assert.Check(t, is.Equal(true, inspect.State.Running))
  90. checkpoints, err := client.CheckpointList(ctx, cID, types.CheckpointListOptions{})
  91. assert.NilError(t, err)
  92. assert.Equal(t, len(checkpoints), 1)
  93. assert.Equal(t, checkpoints[0].Name, "test")
  94. // Create a test file on a tmpfs mount.
  95. containerExec(t, client, cID, []string{"touch", "/tmp/test-file"})
  96. // Do a second checkpoint
  97. cptOpt = types.CheckpointCreateOptions{
  98. Exit: true,
  99. CheckpointID: "test2",
  100. }
  101. t.Log("Do a checkpoint and stop the container")
  102. err = client.CheckpointCreate(ctx, cID, cptOpt)
  103. assert.NilError(t, err)
  104. poll.WaitOn(t,
  105. container.IsInState(ctx, client, cID, "exited"),
  106. poll.WithDelay(100*time.Millisecond),
  107. )
  108. inspect, err = client.ContainerInspect(ctx, cID)
  109. assert.NilError(t, err)
  110. assert.Check(t, is.Equal(false, inspect.State.Running))
  111. // Check that both checkpoints are listed.
  112. checkpoints, err = client.CheckpointList(ctx, cID, types.CheckpointListOptions{})
  113. assert.NilError(t, err)
  114. assert.Equal(t, len(checkpoints), 2)
  115. cptNames := make([]string, 2)
  116. for i, c := range checkpoints {
  117. cptNames[i] = c.Name
  118. }
  119. sort.Strings(cptNames)
  120. assert.Equal(t, cptNames[0], "test")
  121. assert.Equal(t, cptNames[1], "test2")
  122. // Restore the container from a second checkpoint.
  123. startOpt := types.ContainerStartOptions{
  124. CheckpointID: "test2",
  125. }
  126. t.Log("Restore the container")
  127. err = client.ContainerStart(ctx, cID, startOpt)
  128. assert.NilError(t, err)
  129. inspect, err = client.ContainerInspect(ctx, cID)
  130. assert.NilError(t, err)
  131. assert.Check(t, is.Equal(true, inspect.State.Running))
  132. // Check that the test file has been restored.
  133. containerExec(t, client, cID, []string{"test", "-f", "/tmp/test-file"})
  134. for _, id := range []string{"test", "test2"} {
  135. cptDelOpt := types.CheckpointDeleteOptions{
  136. CheckpointID: id,
  137. }
  138. err = client.CheckpointDelete(ctx, cID, cptDelOpt)
  139. assert.NilError(t, err)
  140. }
  141. }