github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/save.go (about)

     1  package main
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  
     7  	"github.com/containers/libpod/cmd/podman/cliconfig"
     8  	"github.com/containers/libpod/cmd/podman/shared/parse"
     9  	"github.com/containers/libpod/pkg/adapter"
    10  	"github.com/containers/libpod/pkg/util"
    11  	"github.com/pkg/errors"
    12  	"github.com/spf13/cobra"
    13  	"golang.org/x/crypto/ssh/terminal"
    14  )
    15  
    16  const (
    17  	ociManifestDir  = "oci-dir"
    18  	ociArchive      = "oci-archive"
    19  	v2s2ManifestDir = "docker-dir"
    20  	v2s2Archive     = "docker-archive"
    21  )
    22  
    23  var validFormats = []string{ociManifestDir, ociArchive, v2s2ManifestDir, v2s2Archive}
    24  
    25  var (
    26  	saveCommand     cliconfig.SaveValues
    27  	saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.`
    28  
    29  	_saveCommand = &cobra.Command{
    30  		Use:   "save [flags] IMAGE",
    31  		Short: "Save image to an archive",
    32  		Long:  saveDescription,
    33  		RunE: func(cmd *cobra.Command, args []string) error {
    34  			saveCommand.InputArgs = args
    35  			saveCommand.GlobalFlags = MainGlobalOpts
    36  			saveCommand.Remote = remoteclient
    37  			return saveCmd(&saveCommand)
    38  		},
    39  		Args: func(cmd *cobra.Command, args []string) error {
    40  			format, err := cmd.Flags().GetString("format")
    41  			if err != nil {
    42  				return err
    43  			}
    44  			if !util.StringInSlice(format, validFormats) {
    45  				return errors.Errorf("format value must be one of %s", strings.Join(validFormats, " "))
    46  			}
    47  			return nil
    48  		},
    49  		Example: `podman save --quiet -o myimage.tar imageID
    50    podman save --format docker-dir -o ubuntu-dir ubuntu
    51    podman save > alpine-all.tar alpine:latest`,
    52  	}
    53  )
    54  
    55  func init() {
    56  	saveCommand.Command = _saveCommand
    57  	saveCommand.SetHelpTemplate(HelpTemplate())
    58  	saveCommand.SetUsageTemplate(UsageTemplate())
    59  	flags := saveCommand.Flags()
    60  	flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
    61  	flags.StringVar(&saveCommand.Format, "format", v2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
    62  	flags.StringVarP(&saveCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
    63  	flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output")
    64  }
    65  
    66  // saveCmd saves the image to either docker-archive or oci
    67  func saveCmd(c *cliconfig.SaveValues) error {
    68  	args := c.InputArgs
    69  	if len(args) == 0 {
    70  		return errors.Errorf("need at least 1 argument")
    71  	}
    72  
    73  	runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
    74  	if err != nil {
    75  		return errors.Wrapf(err, "could not create runtime")
    76  	}
    77  	defer runtime.DeferredShutdown(false)
    78  
    79  	if c.Flag("compress").Changed && (c.Format != ociManifestDir && c.Format != v2s2ManifestDir && c.Format == "") {
    80  		return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
    81  	}
    82  
    83  	if len(c.Output) == 0 {
    84  		fi := os.Stdout
    85  		if terminal.IsTerminal(int(fi.Fd())) {
    86  			return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
    87  		}
    88  		c.Output = "/dev/stdout"
    89  	}
    90  	if err := parse.ValidateFileName(c.Output); err != nil {
    91  		return err
    92  	}
    93  	return runtime.SaveImage(getContext(), c)
    94  }