diff options
Diffstat (limited to 'cmd/podmanV2/images')
-rw-r--r-- | cmd/podmanV2/images/import.go | 87 | ||||
-rw-r--r-- | cmd/podmanV2/images/inspect.go | 141 | ||||
-rw-r--r-- | cmd/podmanV2/images/list.go | 2 | ||||
-rw-r--r-- | cmd/podmanV2/images/load.go | 61 | ||||
-rw-r--r-- | cmd/podmanV2/images/pull.go | 140 | ||||
-rw-r--r-- | cmd/podmanV2/images/push.go | 127 | ||||
-rw-r--r-- | cmd/podmanV2/images/tag.go | 34 | ||||
-rw-r--r-- | cmd/podmanV2/images/untag.go | 33 |
8 files changed, 546 insertions, 79 deletions
diff --git a/cmd/podmanV2/images/import.go b/cmd/podmanV2/images/import.go new file mode 100644 index 000000000..09a15585f --- /dev/null +++ b/cmd/podmanV2/images/import.go @@ -0,0 +1,87 @@ +package images + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + importDescription = `Create a container image from the contents of the specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz). + + Note remote tar balls can be specified, via web address. + Optionally tag the image. You can specify the instructions using the --change option.` + importCommand = &cobra.Command{ + Use: "import [flags] PATH [REFERENCE]", + Short: "Import a tarball to create a filesystem image", + Long: importDescription, + RunE: importCon, + PersistentPreRunE: preRunE, + Example: `podman import http://example.com/ctr.tar url-image + cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported + cat ctr.tar | podman import -`, + } +) + +var ( + importOpts entities.ImageImportOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: importCommand, + }) + + importCommand.SetHelpTemplate(registry.HelpTemplate()) + importCommand.SetUsageTemplate(registry.UsageTemplate()) + flags := importCommand.Flags() + flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") + flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image") + flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output") +} + +func importCon(cmd *cobra.Command, args []string) error { + var ( + source string + reference string + ) + switch len(args) { + case 0: + return errors.Errorf("need to give the path to the tarball, or must specify a tarball of '-' for stdin") + case 1: + source = args[0] + case 2: + source = args[0] + // TODO when save is merged, we need to process reference + // like it is done in there or we end up with docker.io prepends + // instead of the localhost ones + reference = args[1] + default: + return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]") + } + errFileName := parse.ValidateFileName(source) + errURL := parse.ValidURL(source) + if errURL == nil { + importOpts.SourceIsURL = true + } + if errFileName != nil && errURL != nil { + return multierror.Append(errFileName, errURL) + } + + importOpts.Source = source + importOpts.Reference = reference + + response, err := registry.ImageEngine().Import(context.Background(), importOpts) + if err != nil { + return err + } + fmt.Println(response.Id) + return nil +} diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go index f8fd44571..d7f6b0ee1 100644 --- a/cmd/podmanV2/images/inspect.go +++ b/cmd/podmanV2/images/inspect.go @@ -1,71 +1,44 @@ package images import ( + "context" + "encoding/json" + "fmt" + "os" "strings" + "text/tabwriter" + "text/template" "github.com/containers/buildah/pkg/formats" + "github.com/containers/libpod/cmd/podmanV2/common" "github.com/containers/libpod/cmd/podmanV2/registry" "github.com/containers/libpod/pkg/domain/entities" - "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) var ( - inspectOpts = entities.ImageInspectOptions{} - // Command: podman image _inspect_ inspectCmd = &cobra.Command{ Use: "inspect [flags] IMAGE", Short: "Display the configuration of an image", Long: `Displays the low-level information on an image identified by name or ID.`, - PreRunE: populateEngines, - RunE: imageInspect, + RunE: inspect, Example: `podman image inspect alpine`, } - - containerEngine entities.ContainerEngine + inspectOpts *entities.InspectOptions ) -// Inspect is unique in that it needs both an ImageEngine and a ContainerEngine -func populateEngines(cmd *cobra.Command, args []string) (err error) { - // Populate registry.ImageEngine - err = preRunE(cmd, args) - if err != nil { - return - } - - // Populate registry.ContainerEngine - containerEngine, err = registry.NewContainerEngine(cmd, args) - return -} - func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: imageCmd, }) - - flags := inspectCmd.Flags() - flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size") - flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template") - - if registry.EngineOptions.EngineMode == entities.ABIMode { - // TODO: This is the same as V1. We could skip creating the flag altogether in V2... - _ = flags.MarkHidden("latest") - } + inspectOpts = common.AddInspectFlagSet(inspectCmd) } -const ( - inspectTypeContainer = "container" - inspectTypeImage = "image" - inspectAll = "all" -) - -func imageInspect(cmd *cobra.Command, args []string) error { - inspectType := inspectTypeImage +func inspect(cmd *cobra.Command, args []string) error { latestContainer := inspectOpts.Latest if len(args) == 0 && !latestContainer { @@ -76,49 +49,61 @@ func imageInspect(cmd *cobra.Command, args []string) error { return errors.Errorf("you cannot provide additional arguments with --latest") } - if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) { - return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll) + results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts) + if err != nil { + return err } - outputFormat := inspectOpts.Format - if strings.Contains(outputFormat, "{{.Id}}") { - outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) - } - // These fields were renamed, so we need to provide backward compat for - // the old names. - if strings.Contains(outputFormat, ".Src") { - outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1) + if len(results.Images) > 0 { + if inspectOpts.Format == "" { + buf, err := json.MarshalIndent(results.Images, "", " ") + if err != nil { + return err + } + fmt.Println(string(buf)) + + for id, e := range results.Errors { + fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error()) + } + return nil + } + + row := inspectFormat(inspectOpts.Format) + format := "{{range . }}" + row + "{{end}}" + tmpl, err := template.New("inspect").Parse(format) + if err != nil { + return err + } + + w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + defer func() { _ = w.Flush() }() + err = tmpl.Execute(w, results) + if err != nil { + return err + } } - if strings.Contains(outputFormat, ".Dst") { - outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1) - } - if strings.Contains(outputFormat, ".ImageID") { - outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1) + + for id, e := range results.Errors { + fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error()) } - _ = outputFormat - // if latestContainer { - // lc, err := ctnrRuntime.GetLatestContainer() - // if err != nil { - // return err - // } - // args = append(args, lc.ID()) - // inspectType = inspectTypeContainer - // } - - // inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType) - // if iterateErr != nil { - // return iterateErr - // } - // - // var out formats.Writer - // if outputFormat != "" && outputFormat != formats.JSONString { - // // template - // out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat} - // } else { - // // default is json output - // out = formats.JSONStructArray{Output: inspectedObjects} - // } - // - // return out.Out() return nil } + +func inspectFormat(row string) string { + r := strings.NewReplacer("{{.Id}}", formats.IDString, + ".Src", ".Source", + ".Dst", ".Destination", + ".ImageID", ".Image", + ) + row = r.Replace(row) + + if !strings.HasSuffix(row, "\n") { + row += "\n" + } + return row +} + +func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error { + inspectOpts = options + return inspect(cmd, args) +} diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go index 9a5b47299..2d6cb3596 100644 --- a/cmd/podmanV2/images/list.go +++ b/cmd/podmanV2/images/list.go @@ -152,7 +152,7 @@ func writeTemplate(imageS []*entities.ImageSummary, err error) error { hdr, row := imageListFormat(listFlag) format := hdr + "{{range . }}" + row + "{{end}}" - tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format)) + tmpl := template.Must(template.New("list").Funcs(report.PodmanTemplateFuncs()).Parse(format)) w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() return tmpl.Execute(w, imgs) diff --git a/cmd/podmanV2/images/load.go b/cmd/podmanV2/images/load.go new file mode 100644 index 000000000..f60dc4908 --- /dev/null +++ b/cmd/podmanV2/images/load.go @@ -0,0 +1,61 @@ +package images + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." + loadCommand = &cobra.Command{ + Use: "load [flags] [NAME[:TAG]]", + Short: "Load an image from container archive", + Long: loadDescription, + RunE: load, + Args: cobra.MaximumNArgs(1), + PersistentPreRunE: preRunE, + } +) + +var ( + loadOpts entities.ImageLoadOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: loadCommand, + }) + + loadCommand.SetHelpTemplate(registry.HelpTemplate()) + loadCommand.SetUsageTemplate(registry.UsageTemplate()) + flags := loadCommand.Flags() + flags.StringVarP(&loadOpts.Input, "input", "i", "", "Read from specified archive file (default: stdin)") + flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output") + flags.StringVar(&loadOpts.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file") + if registry.IsRemote() { + _ = flags.MarkHidden("signature-policy") + } + +} + +func load(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + repo, err := image.NormalizedTag(args[0]) + if err != nil { + return err + } + loadOpts.Name = repo.Name() + } + response, err := registry.ImageEngine().Load(context.Background(), loadOpts) + if err != nil { + return err + } + fmt.Println("Loaded image: " + response.Name) + return nil +} diff --git a/cmd/podmanV2/images/pull.go b/cmd/podmanV2/images/pull.go new file mode 100644 index 000000000..c7e325409 --- /dev/null +++ b/cmd/podmanV2/images/pull.go @@ -0,0 +1,140 @@ +package images + +import ( + "fmt" + + buildahcli "github.com/containers/buildah/pkg/cli" + "github.com/containers/image/v5/types" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// pullOptionsWrapper wraps entities.ImagePullOptions and prevents leaking +// CLI-only fields into the API types. +type pullOptionsWrapper struct { + entities.ImagePullOptions + TLSVerifyCLI bool // CLI only +} + +var ( + pullOptions = pullOptionsWrapper{} + pullDescription = `Pulls an image from a registry and stores it locally. + + An image can be pulled by tag or digest. If a tag is not specified, the image with the 'latest' tag is pulled.` + + // Command: podman pull + pullCmd = &cobra.Command{ + Use: "pull [flags] IMAGE", + Short: "Pull an image from a registry", + Long: pullDescription, + PreRunE: preRunE, + RunE: imagePull, + Example: `podman pull imageName + podman pull fedora:latest`, + } + + // Command: podman image pull + // It's basically a clone of `pullCmd` with the exception of being a + // child of the images command. + imagesPullCmd = &cobra.Command{ + Use: pullCmd.Use, + Short: pullCmd.Short, + Long: pullCmd.Long, + PreRunE: pullCmd.PreRunE, + RunE: pullCmd.RunE, + Example: `podman image pull imageName + podman image pull fedora:latest`, + } +) + +func init() { + // pull + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pullCmd, + }) + + pullCmd.SetHelpTemplate(registry.HelpTemplate()) + pullCmd.SetUsageTemplate(registry.UsageTemplate()) + + flags := pullCmd.Flags() + pullFlags(flags) + + // images pull + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imagesPullCmd, + Parent: imageCmd, + }) + + imagesPullCmd.SetHelpTemplate(registry.HelpTemplate()) + imagesPullCmd.SetUsageTemplate(registry.UsageTemplate()) + imagesPullFlags := imagesPullCmd.Flags() + pullFlags(imagesPullFlags) +} + +// pullFlags set the flags for the pull command. +func pullFlags(flags *pflag.FlagSet) { + flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") + flags.StringVar(&pullOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.StringVar(&pullOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images") + flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images") + flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") + flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + + if registry.IsRemote() { + _ = flags.MarkHidden("authfile") + _ = flags.MarkHidden("cert-dir") + _ = flags.MarkHidden("signature-policy") + _ = flags.MarkHidden("tls-verify") + } +} + +// imagePull is implement the command for pulling images. +func imagePull(cmd *cobra.Command, args []string) error { + // Sanity check input. + if len(args) == 0 { + return errors.Errorf("an image name must be specified") + } + if len(args) > 1 { + return errors.Errorf("too many arguments. Requires exactly 1") + } + + // Start tracing if requested. + if cmd.Flags().Changed("trace") { + span, _ := opentracing.StartSpanFromContext(registry.GetContext(), "pullCmd") + defer span.Finish() + } + + pullOptsAPI := pullOptions.ImagePullOptions + // TLS verification in c/image is controlled via a `types.OptionalBool` + // which allows for distinguishing among set-true, set-false, unspecified + // which is important to implement a sane way of dealing with defaults of + // boolean CLI flags. + if cmd.Flags().Changed("tls-verify") { + pullOptsAPI.TLSVerify = types.NewOptionalBool(pullOptions.TLSVerifyCLI) + } + + // Let's do all the remaining Yoga in the API to prevent us from + // scattering logic across (too) many parts of the code. + pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), args[0], pullOptsAPI) + if err != nil { + return err + } + + if len(pullReport.Images) > 1 { + fmt.Println("Pulled Images:") + } + for _, img := range pullReport.Images { + fmt.Println(img) + } + + return nil +} diff --git a/cmd/podmanV2/images/push.go b/cmd/podmanV2/images/push.go new file mode 100644 index 000000000..82cc0c486 --- /dev/null +++ b/cmd/podmanV2/images/push.go @@ -0,0 +1,127 @@ +package images + +import ( + buildahcli "github.com/containers/buildah/pkg/cli" + "github.com/containers/image/v5/types" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// pushOptionsWrapper wraps entities.ImagepushOptions and prevents leaking +// CLI-only fields into the API types. +type pushOptionsWrapper struct { + entities.ImagePushOptions + TLSVerifyCLI bool // CLI only +} + +var ( + pushOptions = pushOptionsWrapper{} + pushDescription = `Pushes a source image to a specified destination. + + The Image "DESTINATION" uses a "transport":"details" format. See podman-push(1) section "DESTINATION" for the expected format.` + + // Command: podman push + pushCmd = &cobra.Command{ + Use: "push [flags] SOURCE DESTINATION", + Short: "Push an image to a specified destination", + Long: pushDescription, + PreRunE: preRunE, + RunE: imagePush, + Example: `podman push imageID docker://registry.example.com/repository:tag + podman push imageID oci-archive:/path/to/layout:image:tag`, + } + + // Command: podman image push + // It's basically a clone of `pushCmd` with the exception of being a + // child of the images command. + imagePushCmd = &cobra.Command{ + Use: pushCmd.Use, + Short: pushCmd.Short, + Long: pushCmd.Long, + PreRunE: pushCmd.PreRunE, + RunE: pushCmd.RunE, + Example: `podman image push imageID docker://registry.example.com/repository:tag + podman image push imageID oci-archive:/path/to/layout:image:tag`, + } +) + +func init() { + // push + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pushCmd, + }) + + pushCmd.SetHelpTemplate(registry.HelpTemplate()) + pushCmd.SetUsageTemplate(registry.UsageTemplate()) + + flags := pushCmd.Flags() + pushFlags(flags) + + // images push + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imagePushCmd, + Parent: imageCmd, + }) + + imagePushCmd.SetHelpTemplate(registry.HelpTemplate()) + imagePushCmd.SetUsageTemplate(registry.UsageTemplate()) + pushFlags(imagePushCmd.Flags()) +} + +// pushFlags set the flags for the push command. +func pushFlags(flags *pflag.FlagSet) { + flags.StringVar(&pushOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys") + flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") + flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file") + flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)") + flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images") + flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") + flags.StringVar(&pushOptions.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file") + flags.StringVar(&pushOptions.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") + flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + + if registry.IsRemote() { + _ = flags.MarkHidden("authfile") + _ = flags.MarkHidden("cert-dir") + _ = flags.MarkHidden("compress") + _ = flags.MarkHidden("quiet") + _ = flags.MarkHidden("signature-policy") + _ = flags.MarkHidden("tls-verify") + } +} + +// imagePush is implement the command for pushing images. +func imagePush(cmd *cobra.Command, args []string) error { + var source, destination string + switch len(args) { + case 1: + source = args[0] + case 2: + source = args[0] + destination = args[1] + case 0: + fallthrough + default: + return errors.New("push requires at least one image name, or optionally a second to specify a different destination") + } + + pushOptsAPI := pushOptions.ImagePushOptions + // TLS verification in c/image is controlled via a `types.OptionalBool` + // which allows for distinguishing among set-true, set-false, unspecified + // which is important to implement a sane way of dealing with defaults of + // boolean CLI flags. + if cmd.Flags().Changed("tls-verify") { + pushOptsAPI.TLSVerify = types.NewOptionalBool(pushOptions.TLSVerifyCLI) + } + + // Let's do all the remaining Yoga in the API to prevent us from scattering + // logic across (too) many parts of the code. + return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptsAPI) +} diff --git a/cmd/podmanV2/images/tag.go b/cmd/podmanV2/images/tag.go new file mode 100644 index 000000000..f66fe7857 --- /dev/null +++ b/cmd/podmanV2/images/tag.go @@ -0,0 +1,34 @@ +package images + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + tagDescription = "Adds one or more additional names to locally-stored image." + tagCommand = &cobra.Command{ + Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]", + Short: "Add an additional name to a local image", + Long: tagDescription, + RunE: tag, + Args: cobra.MinimumNArgs(2), + Example: `podman tag 0e3bbc2 fedora:latest + podman tag imageID:latest myNewImage:newTag + podman tag httpd myregistryhost:5000/fedora/httpd:v2`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: tagCommand, + }) + tagCommand.SetHelpTemplate(registry.HelpTemplate()) + tagCommand.SetUsageTemplate(registry.UsageTemplate()) +} + +func tag(cmd *cobra.Command, args []string) error { + return registry.ImageEngine().Tag(registry.GetContext(), args[0], args[1:], entities.ImageTagOptions{}) +} diff --git a/cmd/podmanV2/images/untag.go b/cmd/podmanV2/images/untag.go new file mode 100644 index 000000000..c84827bb3 --- /dev/null +++ b/cmd/podmanV2/images/untag.go @@ -0,0 +1,33 @@ +package images + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + untagCommand = &cobra.Command{ + Use: "untag [flags] IMAGE [NAME...]", + Short: "Remove a name from a local image", + Long: "Removes one or more names from a locally-stored image.", + RunE: untag, + Args: cobra.MinimumNArgs(1), + Example: `podman untag 0e3bbc2 + podman untag imageID:latest otherImageName:latest + podman untag httpd myregistryhost:5000/fedora/httpd:v2`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: untagCommand, + }) + untagCommand.SetHelpTemplate(registry.HelpTemplate()) + untagCommand.SetUsageTemplate(registry.UsageTemplate()) +} + +func untag(cmd *cobra.Command, args []string) error { + return registry.ImageEngine().Untag(registry.GetContext(), args[0], args[1:], entities.ImageUntagOptions{}) +} |