summaryrefslogtreecommitdiff
path: root/cmd/podman/save.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/save.go')
-rw-r--r--cmd/podman/save.go183
1 files changed, 47 insertions, 136 deletions
diff --git a/cmd/podman/save.go b/cmd/podman/save.go
index 325140b76..161540deb 100644
--- a/cmd/podman/save.go
+++ b/cmd/podman/save.go
@@ -1,92 +1,85 @@
package main
import (
- "fmt"
- "io"
"os"
"strings"
- "github.com/containers/image/directory"
- dockerarchive "github.com/containers/image/docker/archive"
- "github.com/containers/image/docker/reference"
- "github.com/containers/image/manifest"
- ociarchive "github.com/containers/image/oci/archive"
- "github.com/containers/image/types"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- libpodImage "github.com/containers/libpod/libpod/image"
- imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "github.com/urfave/cli"
+ "github.com/spf13/cobra"
)
const (
ociManifestDir = "oci-dir"
+ ociArchive = "oci-archive"
v2s2ManifestDir = "docker-dir"
+ v2s2Archive = "docker-archive"
)
+var validFormats = []string{ociManifestDir, ociArchive, v2s2ManifestDir, v2s2Archive}
+
var (
- saveFlags = []cli.Flag{
- cli.BoolFlag{
- Name: "compress",
- Usage: "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)",
- },
- cli.StringFlag{
- Name: "output, o",
- Usage: "Write to a file, default is STDOUT",
- Value: "/dev/stdout",
- },
- cli.BoolFlag{
- Name: "quiet, q",
- Usage: "Suppress the output",
- },
- cli.StringFlag{
- Name: "format",
- Usage: "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-dir (directory with v2s2 manifest type)",
- },
- }
+ saveCommand cliconfig.SaveValues
saveDescription = `
Save an image to docker-archive or oci-archive on the local machine.
Default is docker-archive`
- saveCommand = cli.Command{
- Name: "save",
- Usage: "Save image to an archive",
- Description: saveDescription,
- Flags: sortFlags(saveFlags),
- Action: saveCmd,
- ArgsUsage: "",
- SkipArgReorder: true,
- OnUsageError: usageErrorHandler,
+ _saveCommand = &cobra.Command{
+ Use: "save",
+ Short: "Save image to an archive",
+ Long: saveDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ saveCommand.InputArgs = args
+ saveCommand.GlobalFlags = MainGlobalOpts
+ return saveCmd(&saveCommand)
+ },
+ Args: func(cmd *cobra.Command, args []string) error {
+ format, err := cmd.Flags().GetString("format")
+ if err != nil {
+ return err
+ }
+ if !util.StringInSlice(format, validFormats) {
+ return errors.Errorf("format value must be one of %s", strings.Join(validFormats, " "))
+ }
+ return nil
+ },
+ Example: `podman save --quiet -o myimage.tar imageID
+ podman save --format docker-dir -o ubuntu-dir ubuntu
+ podman save > alpine-all.tar alpine:latest`,
}
)
+func init() {
+ saveCommand.Command = _saveCommand
+ saveCommand.SetUsageTemplate(UsageTemplate())
+ flags := saveCommand.Flags()
+ 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)")
+ 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)")
+ flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
+ flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output")
+}
+
// saveCmd saves the image to either docker-archive or oci
-func saveCmd(c *cli.Context) error {
- args := c.Args()
+func saveCmd(c *cliconfig.SaveValues) error {
+ args := c.InputArgs
if len(args) == 0 {
return errors.Errorf("need at least 1 argument")
}
- if err := validateFlags(c, saveFlags); err != nil {
- return err
- }
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
defer runtime.Shutdown(false)
- if c.IsSet("compress") && (c.String("format") != ociManifestDir && c.String("format") != v2s2ManifestDir && c.String("format") == "") {
+ if c.Flag("compress").Changed && (c.Format != ociManifestDir && c.Format != v2s2ManifestDir && c.Format == "") {
return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
}
- var writer io.Writer
- if !c.Bool("quiet") {
- writer = os.Stderr
- }
-
- output := c.String("output")
+ output := c.Output
if output == "/dev/stdout" {
fi := os.Stdout
if logrus.IsTerminal(fi) {
@@ -96,87 +89,5 @@ func saveCmd(c *cli.Context) error {
if err := validateFileName(output); err != nil {
return err
}
-
- source := args[0]
- newImage, err := runtime.ImageRuntime().NewFromLocal(source)
- if err != nil {
- return err
- }
-
- var destRef types.ImageReference
- var manifestType string
- switch c.String("format") {
- case "oci-archive":
- destImageName := imageNameForSaveDestination(newImage, source)
- destRef, err = ociarchive.NewReference(output, destImageName) // destImageName may be ""
- if err != nil {
- return errors.Wrapf(err, "error getting OCI archive ImageReference for (%q, %q)", output, destImageName)
- }
- case "oci-dir":
- destRef, err = directory.NewReference(output)
- if err != nil {
- return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
- }
- manifestType = imgspecv1.MediaTypeImageManifest
- case "docker-dir":
- destRef, err = directory.NewReference(output)
- if err != nil {
- return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
- }
- manifestType = manifest.DockerV2Schema2MediaType
- case "docker-archive", "":
- dst := output
- destImageName := imageNameForSaveDestination(newImage, source)
- if destImageName != "" {
- dst = fmt.Sprintf("%s:%s", dst, destImageName)
- }
- destRef, err = dockerarchive.ParseReference(dst) // FIXME? Add dockerarchive.NewReference
- if err != nil {
- return errors.Wrapf(err, "error getting Docker archive ImageReference for %q", dst)
- }
- default:
- return errors.Errorf("unknown format option %q", c.String("format"))
- }
-
- // supports saving multiple tags to the same tar archive
- var additionaltags []reference.NamedTagged
- if len(args) > 1 {
- additionaltags, err = libpodImage.GetAdditionalTags(args[1:])
- if err != nil {
- return err
- }
- }
- if err := newImage.PushImageToReference(getContext(), destRef, manifestType, "", "", writer, c.Bool("compress"), libpodImage.SigningOptions{}, &libpodImage.DockerRegistryOptions{}, additionaltags); err != nil {
- if err2 := os.Remove(output); err2 != nil {
- logrus.Errorf("error deleting %q: %v", output, err)
- }
- return errors.Wrapf(err, "unable to save %q", args)
- }
-
- return nil
-}
-
-// imageNameForSaveDestination returns a Docker-like reference appropriate for saving img,
-// which the user referred to as imgUserInput; or an empty string, if there is no appropriate
-// reference.
-func imageNameForSaveDestination(img *libpodImage.Image, imgUserInput string) string {
- if strings.Contains(img.ID(), imgUserInput) {
- return ""
- }
-
- prepend := ""
- localRegistryPrefix := fmt.Sprintf("%s/", libpodImage.DefaultLocalRegistry)
- if !strings.HasPrefix(imgUserInput, localRegistryPrefix) {
- // we need to check if localhost was added to the image name in NewFromLocal
- for _, name := range img.Names() {
- // If the user is saving an image in the localhost registry, getLocalImage need
- // a name that matches the format localhost/<tag1>:<tag2> or localhost/<tag>:latest to correctly
- // set up the manifest and save.
- if strings.HasPrefix(name, localRegistryPrefix) && (strings.HasSuffix(name, imgUserInput) || strings.HasSuffix(name, fmt.Sprintf("%s:latest", imgUserInput))) {
- prepend = localRegistryPrefix
- break
- }
- }
- }
- return fmt.Sprintf("%s%s", prepend, imgUserInput)
+ return runtime.SaveImage(getContext(), c)
}