checkpoint_test.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package container // import "github.com/docker/docker/integration/container"
  2. import (
  3. "context"
  4. "os/exec"
  5. "regexp"
  6. "sort"
  7. "testing"
  8. "time"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/api/types/checkpoint"
  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. stdoutStderr, err := exec.Command("criu", "check").CombinedOutput()
  35. t.Logf("%s", stdoutStderr)
  36. assert.NilError(t, err)
  37. ctx := context.Background()
  38. apiClient := request.NewAPIClient(t)
  39. t.Log("Start a container")
  40. cID := container.Run(ctx, t, apiClient, container.WithMount(mounttypes.Mount{
  41. Type: mounttypes.TypeTmpfs,
  42. Target: "/tmp",
  43. }))
  44. poll.WaitOn(t,
  45. container.IsInState(ctx, apiClient, cID, "running"),
  46. poll.WithDelay(100*time.Millisecond),
  47. )
  48. // FIXME: ipv6 iptables modules are not uploaded in the test environment
  49. stdoutStderr, err = exec.Command("bash", "-c", "set -x; "+
  50. "mount --bind $(type -P true) $(type -P ip6tables-restore) && "+
  51. "mount --bind $(type -P true) $(type -P ip6tables-save)",
  52. ).CombinedOutput()
  53. t.Logf("%s", stdoutStderr)
  54. assert.NilError(t, err)
  55. defer func() {
  56. stdoutStderr, err = exec.Command("bash", "-c", "set -x; "+
  57. "umount -c -i -l $(type -P ip6tables-restore); "+
  58. "umount -c -i -l $(type -P ip6tables-save)",
  59. ).CombinedOutput()
  60. t.Logf("%s", stdoutStderr)
  61. assert.NilError(t, err)
  62. }()
  63. t.Log("Do a checkpoint and leave the container running")
  64. err = apiClient.CheckpointCreate(ctx, cID, checkpoint.CreateOptions{
  65. Exit: false,
  66. CheckpointID: "test",
  67. })
  68. if err != nil {
  69. // An error can contain a path to a dump file
  70. t.Log(err)
  71. re := regexp.MustCompile("path= (.*): ")
  72. m := re.FindStringSubmatch(err.Error())
  73. if len(m) >= 2 {
  74. dumpLog := m[1]
  75. t.Logf("%s", dumpLog)
  76. cmd := exec.Command("cat", dumpLog)
  77. stdoutStderr, err = cmd.CombinedOutput()
  78. t.Logf("%s", stdoutStderr)
  79. }
  80. }
  81. assert.NilError(t, err)
  82. inspect, err := apiClient.ContainerInspect(ctx, cID)
  83. assert.NilError(t, err)
  84. assert.Check(t, is.Equal(true, inspect.State.Running))
  85. checkpoints, err := apiClient.CheckpointList(ctx, cID, checkpoint.ListOptions{})
  86. assert.NilError(t, err)
  87. assert.Equal(t, len(checkpoints), 1)
  88. assert.Equal(t, checkpoints[0].Name, "test")
  89. // Create a test file on a tmpfs mount.
  90. containerExec(t, apiClient, cID, []string{"touch", "/tmp/test-file"})
  91. // Do a second checkpoint
  92. t.Log("Do a checkpoint and stop the container")
  93. err = apiClient.CheckpointCreate(ctx, cID, checkpoint.CreateOptions{
  94. Exit: true,
  95. CheckpointID: "test2",
  96. })
  97. assert.NilError(t, err)
  98. poll.WaitOn(t,
  99. container.IsInState(ctx, apiClient, cID, "exited"),
  100. poll.WithDelay(100*time.Millisecond),
  101. )
  102. inspect, err = apiClient.ContainerInspect(ctx, cID)
  103. assert.NilError(t, err)
  104. assert.Check(t, is.Equal(false, inspect.State.Running))
  105. // Check that both checkpoints are listed.
  106. checkpoints, err = apiClient.CheckpointList(ctx, cID, checkpoint.ListOptions{})
  107. assert.NilError(t, err)
  108. assert.Equal(t, len(checkpoints), 2)
  109. cptNames := make([]string, 2)
  110. for i, c := range checkpoints {
  111. cptNames[i] = c.Name
  112. }
  113. sort.Strings(cptNames)
  114. assert.Equal(t, cptNames[0], "test")
  115. assert.Equal(t, cptNames[1], "test2")
  116. // Restore the container from a second checkpoint.
  117. t.Log("Restore the container")
  118. err = apiClient.ContainerStart(ctx, cID, types.ContainerStartOptions{
  119. CheckpointID: "test2",
  120. })
  121. assert.NilError(t, err)
  122. inspect, err = apiClient.ContainerInspect(ctx, cID)
  123. assert.NilError(t, err)
  124. assert.Check(t, is.Equal(true, inspect.State.Running))
  125. // Check that the test file has been restored.
  126. containerExec(t, apiClient, cID, []string{"test", "-f", "/tmp/test-file"})
  127. for _, id := range []string{"test", "test2"} {
  128. err = apiClient.CheckpointDelete(ctx, cID, checkpoint.DeleteOptions{
  129. CheckpointID: id,
  130. })
  131. assert.NilError(t, err)
  132. }
  133. }