From edddfe8c4f7761b12dc64ea4aa0a83b755aa124f Mon Sep 17 00:00:00 2001 From: flouthoc Date: Fri, 20 Aug 2021 14:22:54 +0530 Subject: volumes: Add support for exporting volumes to external tar Adds support for transferring data between systems and backing up systems. Use cases: recover from disasters or move data between machines. Signed-off-by: flouthoc --- cmd/podman/volumes/export.go | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 cmd/podman/volumes/export.go (limited to 'cmd/podman') diff --git a/cmd/podman/volumes/export.go b/cmd/podman/volumes/export.go new file mode 100644 index 000000000..9e4fecdfa --- /dev/null +++ b/cmd/podman/volumes/export.go @@ -0,0 +1,96 @@ +package volumes + +import ( + "context" + "fmt" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/inspect" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/utils" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + volumeExportDescription = ` +podman volume export + +Allow content of volume to be exported into external tar.` + exportCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "export [options] VOLUME", + Short: "Export volumes", + Args: cobra.ExactArgs(1), + Long: volumeExportDescription, + RunE: export, + ValidArgsFunction: common.AutocompleteVolumes, + } +) + +var ( + // Temporary struct to hold cli values. + cliExportOpts = struct { + Output string + }{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: exportCommand, + Parent: volumeCmd, + }) + flags := exportCommand.Flags() + + outputFlagName := "output" + flags.StringVarP(&cliExportOpts.Output, outputFlagName, "o", "/dev/stdout", "Write to a specified file (default: stdout, which must be redirected)") + _ = exportCommand.RegisterFlagCompletionFunc(outputFlagName, completion.AutocompleteDefault) +} + +func export(cmd *cobra.Command, args []string) error { + var inspectOpts entities.InspectOptions + containerEngine := registry.ContainerEngine() + ctx := context.Background() + + if cliExportOpts.Output == "" { + return errors.New("expects output path, use --output=[path]") + } + inspectOpts.Type = inspect.VolumeType + volumeData, _, err := containerEngine.VolumeInspect(ctx, args, inspectOpts) + if err != nil { + return err + } + if len(volumeData) < 1 { + return errors.New("no volume data found") + } + mountPoint := volumeData[0].VolumeConfigResponse.Mountpoint + driver := volumeData[0].VolumeConfigResponse.Driver + volumeOptions := volumeData[0].VolumeConfigResponse.Options + volumeMountStatus, err := containerEngine.VolumeMounted(ctx, args[0]) + if err != nil { + return err + } + if mountPoint == "" { + return errors.New("volume is not mounted anywhere on host") + } + // Check if volume is using external plugin and export only if volume is mounted + if driver != "" && driver != "local" { + if !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + // Check if volume is using `local` driver and has mount options type other than tmpfs + if driver == "local" { + if mountOptionType, ok := volumeOptions["type"]; ok { + if mountOptionType != "tmpfs" && !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + } + logrus.Debugf("Exporting volume data from %s to %s", mountPoint, cliExportOpts.Output) + err = utils.CreateTarFromSrc(mountPoint, cliExportOpts.Output) + return err +} -- cgit v1.2.3-54-g00ecf