summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/root.go43
-rw-r--r--docs/source/markdown/podman-container-restore.1.md6
-rw-r--r--pkg/checkpoint/checkpoint_restore.go26
-rw-r--r--pkg/checkpoint/crutils/checkpoint_restore_utils.go55
4 files changed, 106 insertions, 24 deletions
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 9e4c8d24d..bccc559ce 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/pkg/checkpoint/crutils"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/parallel"
"github.com/containers/podman/v3/pkg/rootless"
@@ -114,6 +115,48 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
cfg := registry.PodmanConfig()
+ // Currently it is only possible to restore a container with the same runtime
+ // as used for checkpointing. It should be possible to make crun and runc
+ // compatible to restore a container with another runtime then checkpointed.
+ // Currently that does not work.
+ // To make it easier for users we will look into the checkpoint archive and
+ // set the runtime to the one used during checkpointing.
+ if !registry.IsRemote() && cmd.Name() == "restore" {
+ if cmd.Flag("import").Changed {
+ runtime, err := crutils.CRGetRuntimeFromArchive(cmd.Flag("import").Value.String())
+ if err != nil {
+ return errors.Wrapf(
+ err,
+ "failed extracting runtime information from %s",
+ cmd.Flag("import").Value.String(),
+ )
+ }
+ if cfg.RuntimePath == "" {
+ // If the user did not select a runtime, this takes the one from
+ // the checkpoint archives and tells Podman to use it for the restore.
+ runtimeFlag := cmd.Root().Flags().Lookup("runtime")
+ if runtimeFlag == nil {
+ return errors.Errorf(
+ "Unexcpected error setting runtime to '%s' for restore",
+ *runtime,
+ )
+ }
+ runtimeFlag.Value.Set(*runtime)
+ runtimeFlag.Changed = true
+ logrus.Debugf("Checkpoint was created using '%s'. Restore will use the same runtime", *runtime)
+ } else if cfg.RuntimePath != *runtime {
+ // If the user selected a runtime on the command-line this checks if
+ // it is the same then during checkpointing and errors out if not.
+ return errors.Errorf(
+ "checkpoint archive %s was created with runtime '%s' and cannot be restored with runtime '%s'",
+ cmd.Flag("import").Value.String(),
+ *runtime,
+ cfg.RuntimePath,
+ )
+ }
+ }
+ }
+
// --connection is not as "special" as --remote so we can wait and process it here
conn := cmd.Root().LocalFlags().Lookup("connection")
if conn != nil && conn.Changed {
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index 10477fc77..a4630dedf 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -77,6 +77,12 @@ Import a checkpoint tar.gz file, which was exported by Podman. This can be used
to import a checkpointed *container* from another host.\
*IMPORTANT: This OPTION does not need a container name or ID as input argument.*
+During the import of a checkpoint file Podman will select the same container runtime
+which was used during checkpointing. This is especially important if a specific
+(non-default) container runtime was specified during container creation. Podman will
+also abort the restore if the container runtime specified during restore does
+not much the container runtime used for container creation.
+
#### **--import-previous**=*file*
Import a pre-checkpoint tar.gz file which was exported by Podman. This option
diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index 85fe6a77e..c371adf5b 100644
--- a/pkg/checkpoint/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -6,7 +6,6 @@ import (
"os"
metadata "github.com/checkpoint-restore/checkpointctl/lib"
- "github.com/checkpoint-restore/go-criu/v5/stats"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod"
@@ -14,10 +13,8 @@ import (
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
"github.com/containers/podman/v3/pkg/criu"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/specgenutil"
- "github.com/containers/storage/pkg/archive"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -30,24 +27,6 @@ import (
func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) {
// First get the container definition from the
// tarball to a temporary directory
- archiveFile, err := os.Open(restoreOptions.Import)
- if err != nil {
- return nil, errors.Wrap(err, "failed to open checkpoint archive for import")
- }
- defer errorhandling.CloseQuiet(archiveFile)
- options := &archive.TarOptions{
- // Here we only need the files config.dump and spec.dump
- ExcludePatterns: []string{
- "volumes",
- "ctr.log",
- "artifacts",
- stats.StatsDump,
- metadata.RootFsDiffTar,
- metadata.DeletedFilesFile,
- metadata.NetworkStatusFile,
- metadata.CheckpointDirectory,
- },
- }
dir, err := ioutil.TempDir("", "checkpoint")
if err != nil {
return nil, err
@@ -57,9 +36,8 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
logrus.Errorf("Could not recursively remove %s: %q", dir, err)
}
}()
- err = archive.Untar(archiveFile, dir, options)
- if err != nil {
- return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", restoreOptions.Import)
+ if err := crutils.CRImportCheckpointConfigOnly(dir, restoreOptions.Import); err != nil {
+ return nil, err
}
// Load spec.dump from temporary directory
diff --git a/pkg/checkpoint/crutils/checkpoint_restore_utils.go b/pkg/checkpoint/crutils/checkpoint_restore_utils.go
index 3b77368bb..2765d18e8 100644
--- a/pkg/checkpoint/crutils/checkpoint_restore_utils.go
+++ b/pkg/checkpoint/crutils/checkpoint_restore_utils.go
@@ -3,11 +3,13 @@ package crutils
import (
"bytes"
"io"
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
metadata "github.com/checkpoint-restore/checkpointctl/lib"
+ "github.com/checkpoint-restore/go-criu/v5/stats"
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@@ -39,6 +41,36 @@ func CRImportCheckpointWithoutConfig(destination, input string) error {
return nil
}
+// CRImportCheckpointConfigOnly only imports the checkpoint configuration
+// from the checkpoint archive (input) into the directory destination.
+// Only the files "config.dump" and "spec.dump" are extracted.
+func CRImportCheckpointConfigOnly(destination, input string) error {
+ archiveFile, err := os.Open(input)
+ if err != nil {
+ return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
+ }
+
+ defer archiveFile.Close()
+ options := &archive.TarOptions{
+ // Here we only need the files config.dump and spec.dump
+ ExcludePatterns: []string{
+ "volumes",
+ "ctr.log",
+ "artifacts",
+ stats.StatsDump,
+ metadata.RootFsDiffTar,
+ metadata.DeletedFilesFile,
+ metadata.NetworkStatusFile,
+ metadata.CheckpointDirectory,
+ },
+ }
+ if err = archive.Untar(archiveFile, destination, options); err != nil {
+ return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
+ }
+
+ return nil
+}
+
// CRRemoveDeletedFiles loads the list of deleted files and if
// it exists deletes all files listed.
func CRRemoveDeletedFiles(id, baseDirectory, containerRootDirectory string) error {
@@ -200,3 +232,26 @@ func CRRuntimeSupportsPodCheckpointRestore(runtimePath string) bool {
out, _ := cmd.CombinedOutput()
return bytes.Contains(out, []byte("flag needs an argument"))
}
+
+// CRGetRuntimeFromArchive extracts the checkpoint metadata from the
+// given checkpoint archive and returns the runtime used to create
+// the given checkpoint archive.
+func CRGetRuntimeFromArchive(input string) (*string, error) {
+ dir, err := ioutil.TempDir("", "checkpoint")
+ if err != nil {
+ return nil, err
+ }
+ defer os.RemoveAll(dir)
+
+ if err := CRImportCheckpointConfigOnly(dir, input); err != nil {
+ return nil, err
+ }
+
+ // Load config.dump from temporary directory
+ ctrConfig := new(metadata.ContainerConfig)
+ if _, err = metadata.ReadJSONFile(ctrConfig, dir, metadata.ConfigDumpFile); err != nil {
+ return nil, err
+ }
+
+ return &ctrConfig.OCIRuntime, nil
+}