aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorRadostin Stoyanov <rstoyanov@fedoraproject.org>2020-12-18 20:07:08 +0000
committerRadostin Stoyanov <rstoyanov@fedoraproject.org>2021-01-07 07:51:19 +0000
commit288ccc4c84e917df0ff0ee659f47e3ecbc87796a (patch)
tree33b3613a7b300a80cf4ced0182e789acfcb54259 /libpod
parent2b35876c8d03ddc12e8becd2364c39cd621e191d (diff)
downloadpodman-288ccc4c84e917df0ff0ee659f47e3ecbc87796a.tar.gz
podman-288ccc4c84e917df0ff0ee659f47e3ecbc87796a.tar.bz2
podman-288ccc4c84e917df0ff0ee659f47e3ecbc87796a.zip
Include named volumes in container migration
When migrating a container with associated volumes, the content of these volumes should be made available on the destination machine. This patch enables container checkpoint/restore with named volumes by including the content of volumes in checkpoint file. On restore, volumes associated with container are created and their content is restored. The --ignore-volumes option is introduced to disable this feature. Example: # podman container checkpoint --export checkpoint.tar.gz <container> The content of all volumes associated with the container are included in `checkpoint.tar.gz` # podman container checkpoint --export checkpoint.tar.gz --ignore-volumes <container> The content of volumes is not included in `checkpoint.tar.gz`. This is useful, for example, when the checkpoint/restore is performed on the same machine. # podman container restore --import checkpoint.tar.gz The associated volumes will be created and their content will be restored. Podman will exit with an error if volumes with the same name already exist on the system or the content of volumes is not included in checkpoint.tar.gz # podman container restore --ignore-volumes --import checkpoint.tar.gz Volumes associated with container must already exist. Podman will not create them or restore their content. Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_api.go3
-rw-r--r--libpod/container_internal_linux.go73
2 files changed, 74 insertions, 2 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index c3e1a23d2..2c7dc79c9 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -703,6 +703,9 @@ type ContainerCheckpointOptions struct {
// important to be able to restore a container multiple
// times with '--import --name'.
IgnoreStaticMAC bool
+ // IgnoreVolumes tells the API to not export or not to import
+ // the content of volumes associated with the container
+ IgnoreVolumes bool
}
// Checkpoint checkpoints a container
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 9dd39ac8c..f947d1ed9 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -791,8 +791,8 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
}
func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
- if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
- return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
+ if len(c.Dependencies()) > 0 {
+ return errors.Errorf("Cannot export checkpoints of containers with dependencies")
}
logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), options.TargetFile)
@@ -870,6 +870,47 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
}
}
+ // Folder containing archived volumes that will be included in the export
+ expVolDir := filepath.Join(c.bundlePath(), "volumes")
+
+ // Create an archive for each volume associated with the container
+ if !options.IgnoreVolumes {
+ if err := os.MkdirAll(expVolDir, 0700); err != nil {
+ return errors.Wrapf(err, "error creating volumes export directory %q", expVolDir)
+ }
+
+ for _, v := range c.config.NamedVolumes {
+ volumeTarFilePath := filepath.Join("volumes", v.Name+".tar")
+ volumeTarFileFullPath := filepath.Join(c.bundlePath(), volumeTarFilePath)
+
+ volumeTarFile, err := os.Create(volumeTarFileFullPath)
+ if err != nil {
+ return errors.Wrapf(err, "error creating %q", volumeTarFileFullPath)
+ }
+
+ volume, err := c.runtime.GetVolume(v.Name)
+ if err != nil {
+ return err
+ }
+
+ input, err := archive.TarWithOptions(volume.MountPoint(), &archive.TarOptions{
+ Compression: archive.Uncompressed,
+ IncludeSourceDir: true,
+ })
+ if err != nil {
+ return errors.Wrapf(err, "error reading volume directory %q", v.Dest)
+ }
+
+ _, err = io.Copy(volumeTarFile, input)
+ if err != nil {
+ return err
+ }
+ volumeTarFile.Close()
+
+ includeFiles = append(includeFiles, volumeTarFilePath)
+ }
+ }
+
input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
Compression: archive.Gzip,
IncludeSourceDir: true,
@@ -898,6 +939,10 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
os.Remove(rootfsDiffPath)
os.Remove(deleteFilesList)
+ if !options.IgnoreVolumes {
+ os.RemoveAll(expVolDir)
+ }
+
return nil
}
@@ -1193,6 +1238,30 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return err
}
+ // When restoring from an imported archive, allow restoring the content of volumes.
+ // Volumes are created in setupContainer()
+ if options.TargetFile != "" && !options.IgnoreVolumes {
+ for _, v := range c.config.NamedVolumes {
+ volumeFilePath := filepath.Join(c.bundlePath(), "volumes", v.Name+".tar")
+
+ volumeFile, err := os.Open(volumeFilePath)
+ if err != nil {
+ return errors.Wrapf(err, "Failed to open volume file %s", volumeFilePath)
+ }
+ defer volumeFile.Close()
+
+ volume, err := c.runtime.GetVolume(v.Name)
+ if err != nil {
+ return errors.Wrapf(err, "Failed to retrieve volume %s", v.Name)
+ }
+
+ mountPoint := volume.MountPoint()
+ if err := archive.UntarUncompressed(volumeFile, mountPoint, nil); err != nil {
+ return errors.Wrapf(err, "Failed to extract volume %s to %s", volumeFilePath, mountPoint)
+ }
+ }
+ }
+
// Before actually restarting the container, apply the root file-system changes
if !options.IgnoreRootfs {
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")