diff options
author | Adrian Reber <areber@redhat.com> | 2019-03-14 07:57:16 +0000 |
---|---|---|
committer | Adrian Reber <areber@redhat.com> | 2019-06-04 14:02:51 +0200 |
commit | bef83c42eaacd83fb5020395f1bbdeb9a6f0f220 (patch) | |
tree | 6cb3dac84824f26b46df353c1956d35fec067c11 | |
parent | 0e072f9a9785c67f38859ab989267397b57154c8 (diff) | |
download | podman-bef83c42eaacd83fb5020395f1bbdeb9a6f0f220.tar.gz podman-bef83c42eaacd83fb5020395f1bbdeb9a6f0f220.tar.bz2 podman-bef83c42eaacd83fb5020395f1bbdeb9a6f0f220.zip |
migration: add possibility to restore a container with a new name
The option to restore a container from an external checkpoint archive
(podman container restore -i /tmp/checkpoint.tar.gz) restores a
container with the same name and same ID as id had before checkpointing.
This commit adds the option '--name,-n' to 'podman container restore'.
With this option the restored container gets the name specified after
'--name,-n' and a new ID. This way it is possible to restore one
container multiple times.
If a container is restored with a new name Podman will not try to
request the same IP address for the container as it had during
checkpointing. This implicitly assumes that if a container is restored
from a checkpoint archive with a different name, that it will be
restored multiple times and restoring a container multiple times with
the same IP address will fail as each IP address can only be used once.
Signed-off-by: Adrian Reber <areber@redhat.com>
-rw-r--r-- | cmd/podman/cliconfig/config.go | 1 | ||||
-rw-r--r-- | cmd/podman/restore.go | 10 | ||||
-rw-r--r-- | completions/bash/podman | 2 | ||||
-rw-r--r-- | docs/podman-container-restore.1.md | 12 | ||||
-rw-r--r-- | libpod/container_api.go | 4 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 8 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 15 | ||||
-rw-r--r-- | pkg/adapter/checkpoint_restore.go | 32 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 2 | ||||
-rw-r--r-- | test/e2e/checkpoint_test.go | 11 |
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") }) }) |