diff --git a/integration/client/container_checkpoint_test.go b/integration/client/container_checkpoint_test.go index 930b6991b..6fe68396b 100644 --- a/integration/client/container_checkpoint_test.go +++ b/integration/client/container_checkpoint_test.go @@ -533,3 +533,83 @@ func TestCheckpointRestoreWithImagePath(t *testing.T) { <-statusC ntask.Delete(ctx) } + +func TestCheckpointOnPauseStatus(t *testing.T) { + if !supportsCriu { + t.Skip("system does not have criu installed") + } + client, err := newClient(t, address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + if client.Runtime() == plugin.RuntimeLinuxV1 { + t.Skip() + } + + var ( + ctx, cancel = testContext(t) + id = t.Name() + ) + defer cancel() + + image, err := client.GetImage(ctx, testImage) + if err != nil { + t.Fatal(err) + } + container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "10"))) + if err != nil { + t.Fatal(err) + } + defer container.Delete(ctx, WithSnapshotCleanup) + + task, err := container.NewTask(ctx, empty()) + if err != nil { + t.Fatal(err) + } + defer func() { + task.Resume(ctx) + task.Delete(ctx) + }() + + statusC, err := task.Wait(ctx) + if err != nil { + t.Fatal(err) + } + + if err := task.Start(ctx); err != nil { + t.Fatal(err) + } + + if err := task.Pause(ctx); err != nil { + t.Fatal(err) + } + + _, err = container.Checkpoint(ctx, testCheckpointName+"on-pause", []CheckpointOpts{ + WithCheckpointRuntime, + WithCheckpointRW, + WithCheckpointTask, + }...) + if err != nil { + t.Fatal(err) + } + + status, err := task.Status(ctx) + if err != nil { + t.Fatal(err) + } + + if status.Status != Paused { + t.Fatalf("expected paused state, but got %s", status.Status) + } + + if err := task.Resume(ctx); err != nil { + t.Fatal(err) + } + + if err := task.Kill(ctx, syscall.SIGKILL); err != nil { + t.Fatal(err) + } + + <-statusC +} diff --git a/task.go b/task.go index 2704339cd..ab7f9f0a8 100644 --- a/task.go +++ b/task.go @@ -451,11 +451,20 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag } request.Options = any } - // make sure we pause it and resume after all other filesystem operations are completed - if err := t.Pause(ctx); err != nil { + + status, err := t.Status(ctx) + if err != nil { return nil, err } - defer t.Resume(ctx) + + if status.Status != Paused { + // make sure we pause it and resume after all other filesystem operations are completed + if err := t.Pause(ctx); err != nil { + return nil, err + } + defer t.Resume(ctx) + } + index := v1.Index{ Versioned: is.Versioned{ SchemaVersion: 2,