aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/restore.go10
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/podman-container-restore.1.md12
-rw-r--r--libpod/container_api.go4
-rw-r--r--libpod/container_internal_linux.go8
-rw-r--r--libpod/runtime_ctr.go15
-rw-r--r--pkg/adapter/checkpoint_restore.go32
-rw-r--r--pkg/adapter/containers.go2
-rw-r--r--test/e2e/checkpoint_test.go11
10 files changed, 93 insertions, 4 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index d742830ee..466164cc1 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -428,6 +428,7 @@ type RestoreValues struct {
Latest bool
TcpEstablished bool
Import string
+ Name string
}
type RmValues struct {
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index 828ae682f..9c77d4a5e 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -45,6 +45,7 @@ func init() {
flags.BoolVarP(&restoreCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Restore a container with established TCP connections")
flags.StringVarP(&restoreCommand.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)")
+ flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
markFlagHiddenForRemoteClient("latest", flags)
}
@@ -64,6 +65,15 @@ func restoreCmd(c *cliconfig.RestoreValues, cmd *cobra.Command) error {
Keep: c.Keep,
TCPEstablished: c.TcpEstablished,
TargetFile: c.Import,
+ Name: c.Name,
+ }
+
+ if c.Import == "" && c.Name != "" {
+ return errors.Errorf("--name can only used with --import")
+ }
+
+ if c.Name != "" && c.TcpEstablished {
+ return errors.Errorf("--tcp-established cannot be used with --name")
}
if (c.Import != "") && (c.All || c.Latest) {
diff --git a/completions/bash/podman b/completions/bash/podman
index 4972188aa..efb8a6a9b 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -857,6 +857,8 @@ _podman_container_restore() {
local options_with_args="
-i
--import
+ -n
+ --name
"
local boolean_options="
-a
diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md
index 578d34ff3..5efc280fe 100644
--- a/docs/podman-container-restore.1.md
+++ b/docs/podman-container-restore.1.md
@@ -48,6 +48,18 @@ Import a checkpoint tar.gz file, which was exported by Podman. This can be used
to import a checkpointed container from another host. It is not necessary to specify
a container when restoring from an exported checkpoint.
+**--name, -n**
+
+This is only available in combination with **--import, -i**. If a container is restored
+from a checkpoint tar.gz file it is possible to rename it with **--name, -n**. This
+way it is possible to restore a container from a checkpoint multiple times with different
+names.
+
+If the **--name, -n** option is used, Podman will not attempt to assign the same IP
+address to the container it was using before checkpointing as each IP address can only
+be used once and the restored container will have another IP address. This also means
+that **--name, -n** cannot be used in combination with **--tcp-established**.
+
## EXAMPLE
podman container restore mywebserver
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 72c4d7f37..c27cb85ea 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -820,6 +820,10 @@ type ContainerCheckpointOptions struct {
// Import tells the API to read the checkpoint image from
// the filename set in TargetFile
TargetFile string
+ // Name tells the API that during restore from an exported
+ // checkpoint archive a new name should be used for the
+ // restored container
+ Name string
}
// Checkpoint checkpoints a container
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index d471d4191..4acc77afa 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -681,7 +681,13 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// Read network configuration from checkpoint
// Currently only one interface with one IP is supported.
networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status"))
- if err == nil {
+ // If the restored container should get a new name, the IP address of
+ // the container will not be restored. This assumes that if a new name is
+ // specified, the container is restored multiple times.
+ // TODO: This implicit restoring with or without IP depending on an
+ // unrelated restore parameter (--name) does not seem like the
+ // best solution.
+ if err == nil && options.Name == "" {
// The file with the network.status does exist. Let's restore the
// container with the same IP address as during checkpointing.
defer networkStatusFile.Close()
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 9d0bbf7e8..cf1f5701d 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -76,6 +76,14 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
if err := JSONDeepCopy(config, ctr.config); err != nil {
return nil, errors.Wrapf(err, "error copying container config for restore")
}
+ // If the ID is empty a new name for the restored container was requested
+ if ctr.config.ID == "" {
+ ctr.config.ID = stringid.GenerateNonCryptoID()
+ // Fixup ExitCommand with new ID
+ ctr.config.ExitCommand[len(ctr.config.ExitCommand)-1] = ctr.config.ID
+ }
+ // Reset the log path to point to the default
+ ctr.config.LogPath = ""
}
ctr.config.Spec = new(spec.Spec)
@@ -189,11 +197,16 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bo
}
if restore {
- // Remove information about /dev/shm mount
+ // Remove information about bind mount
// for new container from imported checkpoint
g := generate.Generator{Config: ctr.config.Spec}
g.RemoveMount("/dev/shm")
ctr.config.ShmDir = ""
+ g.RemoveMount("/etc/resolv.conf")
+ g.RemoveMount("/etc/hostname")
+ g.RemoveMount("/etc/hosts")
+ g.RemoveMount("/run/.containerenv")
+ g.RemoveMount("/run/secrets")
}
// Set up storage for the container
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go
index 4ca17dd93..97ba5ecf7 100644
--- a/pkg/adapter/checkpoint_restore.go
+++ b/pkg/adapter/checkpoint_restore.go
@@ -41,7 +41,7 @@ func crImportFromJSON(filePath string, v interface{}) error {
// crImportCheckpoint it the function which imports the information
// from checkpoint tarball and re-creates the container from that information
-func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string) ([]*libpod.Container, error) {
+func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
// First get the container definition from the
// tarball to a temporary directory
archiveFile, err := os.Open(input)
@@ -85,6 +85,18 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
return nil, errors.Errorf("Cannot import checkpoints of containers with named volumes or dependencies")
}
+ ctrID := config.ID
+ newName := false
+
+ // Check if the restored container gets a new name
+ if name != "" {
+ config.ID = ""
+ config.Name = name
+ newName = true
+ }
+
+ ctrName := config.Name
+
// The code to load the images is copied from create.go
var writer io.Writer
// In create.go this only set if '--quiet' does not exist.
@@ -110,6 +122,24 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
return nil, nil
}
+ containerConfig := container.Config()
+ if containerConfig.Name != ctrName {
+ return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
+ }
+
+ if newName == false {
+ // Only check ID for a restore with the same name.
+ // Using -n to request a new name for the restored container, will also create a new ID
+ if containerConfig.ID != ctrID {
+ return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
+ }
+ }
+
+ // Check if the ExitCommand points to the correct container ID
+ if containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1] != containerConfig.ID {
+ return nil, errors.Errorf("'ExitCommandID' uses ID %s instead of container ID %s", containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1], containerConfig.ID)
+ }
+
containers = append(containers, container)
return containers, nil
}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index b7f4c272b..29297fbd5 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -539,7 +539,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues,
})
if c.Import != "" {
- containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import)
+ containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
} else if c.All {
containers, err = r.GetContainers(filterFuncs...)
} else {
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index bd9cdcb8d..d452a062b 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -376,9 +376,20 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+ // Restore container a second time with different name
+ result = podmanTest.Podman([]string{"container", "restore", "-i", "/tmp/checkpoint.tar.gz", "-n", "restore_again"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
result = podmanTest.Podman([]string{"rm", "-fa"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ // Remove exported checkpoint
+ os.Remove("/tmp/checkpoint.tar.gz")
})
})