checkpoint_test.go 4.5 KB

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