diff options
Diffstat (limited to 'cmd/podman/save.go')
-rw-r--r-- | cmd/podman/save.go | 183 |
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) } |