diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podmanV2/Makefile | 2 | ||||
-rw-r--r-- | cmd/podmanV2/common/inspect.go | 18 | ||||
-rw-r--r-- | cmd/podmanV2/containers/export.go | 57 | ||||
-rw-r--r-- | cmd/podmanV2/containers/inspect.go | 32 | ||||
-rw-r--r-- | cmd/podmanV2/healthcheck/healthcheck.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/healthcheck/run.go | 42 | ||||
-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/tag.go | 34 | ||||
-rw-r--r-- | cmd/podmanV2/images/untag.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/inspect.go | 62 | ||||
-rw-r--r-- | cmd/podmanV2/main.go | 7 | ||||
-rw-r--r-- | cmd/podmanV2/pods/pod.go | 30 | ||||
-rw-r--r-- | cmd/podmanV2/pods/ps.go | 134 | ||||
-rw-r--r-- | cmd/podmanV2/registry/registry.go | 17 | ||||
-rw-r--r-- | cmd/podmanV2/report/templates.go | 3 | ||||
-rw-r--r-- | cmd/podmanV2/root.go | 5 | ||||
-rw-r--r-- | cmd/podmanV2/system/system.go | 2 |
20 files changed, 748 insertions, 107 deletions
diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile index 147a78d9c..f2f7bd73c 100644 --- a/cmd/podmanV2/Makefile +++ b/cmd/podmanV2/Makefile @@ -1,2 +1,2 @@ all: - GO111MODULE=off go build -tags 'ABISupport' + GO111MODULE=off go build -tags 'ABISupport systemd' diff --git a/cmd/podmanV2/common/inspect.go b/cmd/podmanV2/common/inspect.go new file mode 100644 index 000000000..dfc6fe679 --- /dev/null +++ b/cmd/podmanV2/common/inspect.go @@ -0,0 +1,18 @@ +package common + +import ( + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +// AddInspectFlagSet takes a command and adds the inspect flags and returns an InspectOptions object +// Since this cannot live in `package main` it lives here until a better home is found +func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions { + opts := entities.InspectOptions{} + + flags := cmd.Flags() + flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size") + flags.StringVarP(&opts.Format, "format", "f", "", "Change the output format to a Go template") + + return &opts +} diff --git a/cmd/podmanV2/containers/export.go b/cmd/podmanV2/containers/export.go new file mode 100644 index 000000000..b93b60878 --- /dev/null +++ b/cmd/podmanV2/containers/export.go @@ -0,0 +1,57 @@ +package containers + +import ( + "context" + "os" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" +) + +var ( + exportDescription = "Exports container's filesystem contents as a tar archive" + + " and saves it on the local machine." + + exportCommand = &cobra.Command{ + Use: "export [flags] CONTAINER", + Short: "Export container's filesystem contents as a tar archive", + Long: exportDescription, + PersistentPreRunE: preRunE, + RunE: export, + Args: cobra.ExactArgs(1), + Example: `podman export ctrID > myCtr.tar + podman export --output="myCtr.tar" ctrID`, + } +) + +var ( + exportOpts entities.ContainerExportOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: exportCommand, + }) + exportCommand.SetHelpTemplate(registry.HelpTemplate()) + exportCommand.SetUsageTemplate(registry.UsageTemplate()) + flags := exportCommand.Flags() + flags.StringVarP(&exportOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") +} + +func export(cmd *cobra.Command, args []string) error { + if len(exportOpts.Output) == 0 { + file := os.Stdout + if terminal.IsTerminal(int(file.Fd())) { + return errors.Errorf("refusing to export to terminal. Use -o flag or redirect") + } + exportOpts.Output = "/dev/stdout" + } else if err := parse.ValidateFileName(exportOpts.Output); err != nil { + return err + } + return registry.ContainerEngine().ContainerExport(context.Background(), args[0], exportOpts) +} diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go index 648289f0b..3147426cb 100644 --- a/cmd/podmanV2/containers/inspect.go +++ b/cmd/podmanV2/containers/inspect.go @@ -7,9 +7,11 @@ import ( "strings" "text/template" + "github.com/containers/libpod/cmd/podmanV2/common" "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" - jsoniter "github.com/json-iterator/go" + json "github.com/json-iterator/go" "github.com/spf13/cobra" ) @@ -24,10 +26,7 @@ var ( Example: `podman container inspect myCtr podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`, } -) - -var ( - inspectOptions entities.ContainerInspectOptions + inspectOpts *entities.InspectOptions ) func init() { @@ -36,29 +35,29 @@ func init() { Command: inspectCmd, Parent: containerCmd, }) + inspectOpts = common.AddInspectFlagSet(inspectCmd) flags := inspectCmd.Flags() - flags.StringVarP(&inspectOptions.Format, "format", "f", "", "Change the output format to a Go template") - flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVarP(&inspectOptions.Size, "size", "s", false, "Display total file size") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") + + if !registry.IsRemote() { + flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") } + } func inspect(cmd *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, inspectOptions) + responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, *inspectOpts) if err != nil { return err } - if inspectOptions.Format == "" { - b, err := jsoniter.MarshalIndent(responses, "", " ") + if inspectOpts.Format == "" { + b, err := json.MarshalIndent(responses, "", " ") if err != nil { return err } fmt.Println(string(b)) return nil } - format := inspectOptions.Format + format := inspectOpts.Format if !strings.HasSuffix(format, "\n") { format += "\n" } @@ -73,3 +72,8 @@ func inspect(cmd *cobra.Command, args []string) error { } return nil } + +func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error { + inspectOpts = options + return inspect(cmd, args) +} diff --git a/cmd/podmanV2/healthcheck/healthcheck.go b/cmd/podmanV2/healthcheck/healthcheck.go new file mode 100644 index 000000000..2af398ff0 --- /dev/null +++ b/cmd/podmanV2/healthcheck/healthcheck.go @@ -0,0 +1,33 @@ +package healthcheck + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Command: healthcheck + healthCmd = &cobra.Command{ + Use: "healthcheck", + Short: "Manage Healthcheck", + Long: "Manage Healthcheck", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: healthCmd, + }) + healthCmd.SetHelpTemplate(registry.HelpTemplate()) + healthCmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/healthcheck/run.go b/cmd/podmanV2/healthcheck/run.go new file mode 100644 index 000000000..bb2962eaf --- /dev/null +++ b/cmd/podmanV2/healthcheck/run.go @@ -0,0 +1,42 @@ +package healthcheck + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + healthcheckRunDescription = "run the health check of a container" + healthcheckrunCommand = &cobra.Command{ + Use: "run [flags] CONTAINER", + Short: "run the health check of a container", + Long: healthcheckRunDescription, + Example: `podman healthcheck run mywebapp`, + RunE: run, + Args: cobra.ExactArgs(1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: healthcheckrunCommand, + Parent: healthCmd, + }) +} + +func run(cmd *cobra.Command, args []string) error { + response, err := registry.ContainerEngine().HealthCheckRun(context.Background(), args[0], entities.HealthCheckOptions{}) + if err != nil { + return err + } + if response.Status == "unhealthy" { + registry.SetExitCode(1) + } + fmt.Println(response.Status) + return err +} 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/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{}) +} diff --git a/cmd/podmanV2/inspect.go b/cmd/podmanV2/inspect.go new file mode 100644 index 000000000..4975cf632 --- /dev/null +++ b/cmd/podmanV2/inspect.go @@ -0,0 +1,62 @@ +package main + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/containers" + "github.com/containers/libpod/cmd/podmanV2/images" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +// Inspect is one of the out layer commands in that it operates on images/containers/... + +var ( + inspectOpts *entities.InspectOptions + + // Command: podman _inspect_ Object_ID + inspectCmd = &cobra.Command{ + Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", + Args: cobra.ExactArgs(1), + Short: "Display the configuration of object denoted by ID", + Long: "Displays the low-level information on an object identified by name or ID", + TraverseChildren: true, + RunE: inspect, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: inspectCmd, + }) + inspectOpts = common.AddInspectFlagSet(inspectCmd) +} + +func inspect(cmd *cobra.Command, args []string) error { + ie, err := registry.NewImageEngine(cmd, args) + if err != nil { + return err + } + + if found, err := ie.Exists(context.Background(), args[0]); err != nil { + return err + } else if found.Value { + return images.Inspect(cmd, args, inspectOpts) + } + + ce, err := registry.NewContainerEngine(cmd, args) + if err != nil { + return err + } + + if found, err := ce.ContainerExists(context.Background(), args[0]); err != nil { + return err + } else if found.Value { + return containers.Inspect(cmd, args, inspectOpts) + } + return fmt.Errorf("%s not found on system", args[0]) +} diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go index bd9fbb25e..6781a7f06 100644 --- a/cmd/podmanV2/main.go +++ b/cmd/podmanV2/main.go @@ -7,6 +7,7 @@ import ( "strings" _ "github.com/containers/libpod/cmd/podmanV2/containers" + _ "github.com/containers/libpod/cmd/podmanV2/healthcheck" _ "github.com/containers/libpod/cmd/podmanV2/images" _ "github.com/containers/libpod/cmd/podmanV2/networks" _ "github.com/containers/libpod/cmd/podmanV2/pods" @@ -14,6 +15,7 @@ import ( _ "github.com/containers/libpod/cmd/podmanV2/volumes" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/storage/pkg/reexec" "github.com/sirupsen/logrus" ) @@ -44,6 +46,11 @@ func init() { } func main() { + if reexec.Init() { + // We were invoked with a different argv[0] indicating that we + // had a specific job to do as a subprocess, and it's done. + return + } for _, c := range registry.Commands { if Contains(registry.EngineOptions.EngineMode, c.Mode) { parent := rootCmd diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go index 81c0d33e1..3766893bb 100644 --- a/cmd/podmanV2/pods/pod.go +++ b/cmd/podmanV2/pods/pod.go @@ -1,6 +1,9 @@ package pods import ( + "strings" + "text/template" + "github.com/containers/libpod/cmd/podmanV2/registry" "github.com/containers/libpod/pkg/domain/entities" "github.com/spf13/cobra" @@ -18,6 +21,33 @@ var ( } ) +var podFuncMap = template.FuncMap{ + "numCons": func(cons []*entities.ListPodContainer) int { + return len(cons) + }, + "podcids": func(cons []*entities.ListPodContainer) string { + var ctrids []string + for _, c := range cons { + ctrids = append(ctrids, c.Id[:12]) + } + return strings.Join(ctrids, ",") + }, + "podconnames": func(cons []*entities.ListPodContainer) string { + var ctrNames []string + for _, c := range cons { + ctrNames = append(ctrNames, c.Names[:12]) + } + return strings.Join(ctrNames, ",") + }, + "podconstatuses": func(cons []*entities.ListPodContainer) string { + var statuses []string + for _, c := range cons { + statuses = append(statuses, c.Status) + } + return strings.Join(statuses, ",") + }, +} + func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go index d4c625b2e..9546dff9e 100644 --- a/cmd/podmanV2/pods/ps.go +++ b/cmd/podmanV2/pods/ps.go @@ -1,8 +1,19 @@ package pods import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + "text/template" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/report" "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -19,14 +30,137 @@ var ( } ) +var ( + defaultHeaders string = "POD ID\tNAME\tSTATUS\tCREATED" + inputFilters string + noTrunc bool + psInput entities.PodPSOptions +) + func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: psCmd, Parent: podCmd, }) + flags := psCmd.Flags() + flags.BoolVar(&psInput.CtrNames, "ctr-names", false, "Display the container names") + flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated") + flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status") + // TODO should we make this a [] ? + flags.StringVarP(&inputFilters, "filter", "f", "", "Filter output based on conditions given") + flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template") + flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") + flags.BoolVar(&psInput.Namespace, "ns", false, "Display namespace information of the pod") + flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate pod and container IDs") + flags.BoolVarP(&psInput.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only") + flags.StringVar(&psInput.Sort, "sort", "created", "Sort output by created, id, name, or number") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } } func pods(cmd *cobra.Command, args []string) error { + var ( + w io.Writer = os.Stdout + row string + ) + if cmd.Flag("filter").Changed { + for _, f := range strings.Split(inputFilters, ",") { + split := strings.Split(f, "=") + if len(split) < 2 { + return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + psInput.Filters[split[0]] = append(psInput.Filters[split[0]], split[1]) + } + } + responses, err := registry.ContainerEngine().PodPs(context.Background(), psInput) + if err != nil { + return err + } + + if psInput.Format == "json" { + b, err := json.MarshalIndent(responses, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil + } + headers, row := createPodPsOut(cmd) + if psInput.Quiet { + if noTrunc { + row = "{{.Id}}\n" + } else { + row = "{{slice .Id 0 12}}\n" + } + } + if cmd.Flag("format").Changed { + row = psInput.Format + if !strings.HasPrefix(row, "\n") { + row += "\n" + } + } + format := "{{range . }}" + row + "{{end}}" + if !psInput.Quiet && !cmd.Flag("format").Changed { + format = headers + format + } + funcs := report.AppendFuncMap(podFuncMap) + tmpl, err := template.New("listPods").Funcs(funcs).Parse(format) + if err != nil { + return err + } + if !psInput.Quiet { + w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + } + if err := tmpl.Execute(w, responses); err != nil { + return err + } + if flusher, ok := w.(interface{ Flush() error }); ok { + return flusher.Flush() + } return nil } + +func createPodPsOut(cmd *cobra.Command) (string, string) { + var row string + headers := defaultHeaders + if noTrunc { + row += "{{.Id}}" + } else { + row += "{{slice .Id 0 12}}" + } + + row += "\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}" + + //rowFormat string = "{{slice .Id 0 12}}\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}" + if psInput.CtrIds { + headers += "\tIDS" + row += "\t{{podcids .Containers}}" + } + if psInput.CtrNames { + headers += "\tNAMES" + row += "\t{{podconnames .Containers}}" + } + if psInput.CtrStatus { + headers += "\tSTATUS" + row += "\t{{podconstatuses .Containers}}" + } + if psInput.Namespace { + headers += "\tCGROUP\tNAMESPACES" + row += "\t{{.Cgroup}}\t{{.Namespace}}" + } + if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds { + headers += "\t# OF CONTAINERS" + row += "\t{{numCons .Containers}}" + + } + headers += "\tINFRA ID\n" + if noTrunc { + row += "\t{{.InfraId}}\n" + } else { + row += "\t{{slice .InfraId 0 12}}\n" + } + return headers, row +} diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index 5cdb8a840..401f82718 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -3,7 +3,6 @@ package registry import ( "context" - "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra" "github.com/pkg/errors" @@ -18,24 +17,24 @@ type CliCommand struct { Parent *cobra.Command } -var ( - Commands []CliCommand +const ExecErrorCodeGeneric = 125 - imageEngine entities.ImageEngine - containerEngine entities.ContainerEngine +var ( cliCtx context.Context + containerEngine entities.ContainerEngine + exitCode = ExecErrorCodeGeneric + imageEngine entities.ImageEngine + Commands []CliCommand EngineOptions entities.EngineOptions - - ExitCode = define.ExecErrorCodeGeneric ) func SetExitCode(code int) { - ExitCode = code + exitCode = code } func GetExitCode() int { - return ExitCode + return exitCode } // HelpTemplate returns the help template for podman commands diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go index f3bc06405..e46048e97 100644 --- a/cmd/podmanV2/report/templates.go +++ b/cmd/podmanV2/report/templates.go @@ -19,6 +19,9 @@ var defaultFuncMap = template.FuncMap{ "humanDuration": func(t int64) string { return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago" }, + "humanDurationFromTime": func(t time.Time) string { + return units.HumanDuration(time.Since(t)) + " ago" + }, "humanSize": func(sz int64) string { s := units.HumanSizeWithPrecision(float64(sz), 3) i := strings.LastIndexFunc(s, unicode.IsNumber) diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go index cb4cb4e00..6fc12f57e 100644 --- a/cmd/podmanV2/root.go +++ b/cmd/podmanV2/root.go @@ -7,7 +7,6 @@ import ( "path" "github.com/containers/libpod/cmd/podmanV2/registry" - "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/version" "github.com/sirupsen/logrus" @@ -88,8 +87,8 @@ func Execute() { o := registry.NewOptions(rootCmd.Context(), ®istry.EngineOptions) if err := rootCmd.ExecuteContext(o); err != nil { fmt.Fprintln(os.Stderr, "Error:", err.Error()) - } else if registry.GetExitCode() == define.ExecErrorCodeGeneric { - // The exitCode modified from define.ExecErrorCodeGeneric, + } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric { + // The exitCode modified from registry.ExecErrorCodeGeneric, // indicates an application // running inside of a container failed, as opposed to the // podman command failed. Must exit with that exit code diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go index 30ed328e8..4e805c7bd 100644 --- a/cmd/podmanV2/system/system.go +++ b/cmd/podmanV2/system/system.go @@ -1,4 +1,4 @@ -package images +package system import ( "github.com/containers/libpod/cmd/podmanV2/registry" |