checkpoint_test.go 4.5 KB

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