From f4401567cdf6d7ccc1ef9f50345c865bc6262c22 Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Sun, 20 Nov 2022 20:48:38 +0000 Subject: [PATCH] Enable 'podman run' for checkpoint images This patch extends the podman run command with support for checkpoint images. When `podman run` is invoked with an image that contains a checkpoint, it would restore the container from that checkpoint. Example: podman run -d --name looper busybox /bin/sh -c \ 'i=0; while true; do echo $i; i=$(expr $i + 1); sleep 1; done' podman container checkpoint --create-image checkpoint-image-1 looper podman run checkpoint-image-1 Signed-off-by: Radostin Stoyanov --- pkg/domain/infra/abi/containers.go | 39 +++++++++++++++++++++++++++ pkg/domain/infra/tunnel/containers.go | 25 +++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index d34d69b1ee..e4aead7fa3 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "reflect" "strconv" "sync" "time" @@ -1110,6 +1111,44 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta fmt.Fprintf(os.Stderr, "%s\n", w) } + if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() { + // If this is a checkpoint image, restore it. + img, resolvedImageName := opts.Spec.GetImage() + if img != nil && resolvedImageName != "" { + imgData, err := img.Inspect(ctx, nil) + if err != nil { + return nil, err + } + if imgData != nil { + _, isCheckpointImage := imgData.Annotations[define.CheckpointAnnotationRuntimeName] + if isCheckpointImage { + var restoreOptions entities.RestoreOptions + restoreOptions.Name = opts.Spec.Name + restoreOptions.Pod = opts.Spec.Pod + responses, err := ic.ContainerRestore(ctx, []string{resolvedImageName}, restoreOptions) + if err != nil { + return nil, err + } + + report := entities.ContainerRunReport{} + for _, r := range responses { + report.Id = r.Id + report.ExitCode = 0 + if r.Err != nil { + logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.Err) + report.ExitCode = 126 + } + if r.RawInput != "" { + logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.RawInput) + report.ExitCode = 126 + } + } + return &report, nil + } + } + } + } + rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec, false, nil) if err != nil { return nil, err diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index f3e66982d8..e8a09a7559 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "reflect" "strconv" "strings" "sync" @@ -790,6 +791,30 @@ func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entitie } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() && opts.Spec.RawImageName != "" { + // If this is a checkpoint image, restore it. + getImageOptions := new(images.GetOptions).WithSize(false) + inspectReport, err := images.GetImage(ic.ClientCtx, opts.Spec.RawImageName, getImageOptions) + if err != nil { + return nil, fmt.Errorf("no such container or image: %s", opts.Spec.RawImageName) + } + if inspectReport != nil { + _, isCheckpointImage := inspectReport.Annotations[define.CheckpointAnnotationRuntimeName] + if isCheckpointImage { + restoreOptions := new(containers.RestoreOptions) + restoreOptions.WithName(opts.Spec.Name) + restoreOptions.WithPod(opts.Spec.Pod) + + restoreReport, err := containers.Restore(ic.ClientCtx, inspectReport.ID, restoreOptions) + if err != nil { + return nil, err + } + runReport := entities.ContainerRunReport{Id: restoreReport.Id} + return &runReport, nil + } + } + } + con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil) if err != nil { return nil, err