diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/pod_ps.go | 126 | ||||
-rw-r--r-- | cmd/podman/shared/pod.go | 126 | ||||
-rw-r--r-- | cmd/podmanV2/README.md | 113 | ||||
-rw-r--r-- | cmd/podmanV2/containers/container.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/containers/inspect.go | 42 | ||||
-rw-r--r-- | cmd/podmanV2/containers/list.go | 34 | ||||
-rw-r--r-- | cmd/podmanV2/containers/ps.go | 29 | ||||
-rw-r--r-- | cmd/podmanV2/images/history.go | 79 | ||||
-rw-r--r-- | cmd/podmanV2/images/image.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/images/images.go | 46 | ||||
-rw-r--r-- | cmd/podmanV2/images/inspect.go | 124 | ||||
-rw-r--r-- | cmd/podmanV2/images/list.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/main.go | 84 | ||||
-rw-r--r-- | cmd/podmanV2/networks/network.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/pods/pod.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/pods/ps.go | 32 | ||||
-rw-r--r-- | cmd/podmanV2/registry/registry.go | 96 | ||||
-rw-r--r-- | cmd/podmanV2/report/templates.go | 61 | ||||
-rw-r--r-- | cmd/podmanV2/root.go | 35 | ||||
-rw-r--r-- | cmd/podmanV2/system/system.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/volumes/volume.go | 33 |
21 files changed, 1143 insertions, 115 deletions
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index d7731e983..7acbd6888 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -4,7 +4,6 @@ import ( "fmt" "reflect" "sort" - "strconv" "strings" "time" @@ -13,7 +12,6 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" - "github.com/containers/libpod/pkg/util" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -29,8 +27,6 @@ const ( NUM_CTR_INFO = 10 ) -type PodFilter func(*adapter.Pod) bool - var ( bc_opts shared.PsOptions ) @@ -174,29 +170,23 @@ func podPsCmd(c *cliconfig.PodPsValues) error { opts.Format = genPodPsFormat(c) - var filterFuncs []PodFilter - if c.Filter != "" { - filters := strings.Split(c.Filter, ",") - for _, f := range filters { - filterSplit := strings.Split(f, "=") - if len(filterSplit) < 2 { - return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1]) - if err != nil { - return errors.Wrapf(err, "invalid filter") - } - filterFuncs = append(filterFuncs, generatedFunc) - } - } - var pods []*adapter.Pod + + // If latest is set true filters are ignored. if c.Latest { pod, err := runtime.GetLatestPod() if err != nil { return err } pods = append(pods, pod) + return generatePodPsOutput(pods, opts) + } + + if c.Filter != "" { + pods, err = runtime.GetPodsWithFilters(c.Filter) + if err != nil { + return err + } } else { pods, err = runtime.GetAllPods() if err != nil { @@ -204,19 +194,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error { } } - podsFiltered := make([]*adapter.Pod, 0, len(pods)) - for _, pod := range pods { - include := true - for _, filter := range filterFuncs { - include = include && filter(pod) - } - - if include { - podsFiltered = append(podsFiltered, pod) - } - } - - return generatePodPsOutput(podsFiltered, opts) + return generatePodPsOutput(pods, opts) } // podPsCheckFlagsPassed checks if mutually exclusive flags are passed together @@ -235,88 +213,6 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error { return nil } -func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) { - switch filter { - case "ctr-ids": - return func(p *adapter.Pod) bool { - ctrIds, err := p.AllContainersByID() - if err != nil { - return false - } - return util.StringInSlice(filterValue, ctrIds) - }, nil - case "ctr-names": - return func(p *adapter.Pod) bool { - ctrs, err := p.AllContainers() - if err != nil { - return false - } - for _, ctr := range ctrs { - if filterValue == ctr.Name() { - return true - } - } - return false - }, nil - case "ctr-number": - return func(p *adapter.Pod) bool { - ctrIds, err := p.AllContainersByID() - if err != nil { - return false - } - - fVint, err2 := strconv.Atoi(filterValue) - if err2 != nil { - return false - } - return len(ctrIds) == fVint - }, nil - case "ctr-status": - if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) { - return nil, errors.Errorf("%s is not a valid status", filterValue) - } - return func(p *adapter.Pod) bool { - ctr_statuses, err := p.Status() - if err != nil { - return false - } - for _, ctr_status := range ctr_statuses { - state := ctr_status.String() - if ctr_status == define.ContainerStateConfigured { - state = "created" - } - if state == filterValue { - return true - } - } - return false - }, nil - case "id": - return func(p *adapter.Pod) bool { - return strings.Contains(p.ID(), filterValue) - }, nil - case "name": - return func(p *adapter.Pod) bool { - return strings.Contains(p.Name(), filterValue) - }, nil - case "status": - if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) { - return nil, errors.Errorf("%s is not a valid pod status", filterValue) - } - return func(p *adapter.Pod) bool { - status, err := p.GetPodStatus() - if err != nil { - return false - } - if strings.ToLower(status) == filterValue { - return true - } - return false - }, nil - } - return nil, errors.Errorf("%s is an invalid filter", filter) -} - // generate the template based on conditions given func genPodPsFormat(c *cliconfig.PodPsValues) string { format := "" diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go index 7b0b497fc..3046953b5 100644 --- a/cmd/podman/shared/pod.go +++ b/cmd/podman/shared/pod.go @@ -2,9 +2,11 @@ package shared import ( "strconv" + "strings" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-connections/nat" "github.com/pkg/errors" @@ -134,4 +136,128 @@ func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) { return portBindings, nil } +// GetPodsWithFilters uses the cliconfig to categorize if the latest pod is required. +func GetPodsWithFilters(r *libpod.Runtime, filters string) ([]*libpod.Pod, error) { + filterFuncs, err := GenerateFilterFunction(r, strings.Split(filters, ",")) + if err != nil { + return nil, err + } + return FilterAllPodsWithFilterFunc(r, filterFuncs...) +} + +// FilterAllPodsWithFilterFunc retrieves all pods +// Filters can be provided which will determine which pods are included in the +// output. Multiple filters are handled by ANDing their output, so only pods +// matching all filters are returned +func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter) ([]*libpod.Pod, error) { + pods, err := r.Pods(filters...) + if err != nil { + return nil, err + } + return pods, nil +} + +// GenerateFilterFunction basically gets the filters based on the input by the user +// and filter the pod list based on the criteria. +func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) { + var filterFuncs []libpod.PodFilter + for _, f := range filters { + filterSplit := strings.Split(f, "=") + if len(filterSplit) < 2 { + return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1]) + if err != nil { + return nil, errors.Wrapf(err, "invalid filter") + } + filterFuncs = append(filterFuncs, generatedFunc) + } + + return filterFuncs, nil +} +func generatePodFilterFuncs(filter, filterValue string) ( + func(pod *libpod.Pod) bool, error) { + switch filter { + case "ctr-ids": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + return util.StringInSlice(filterValue, ctrIds) + }, nil + case "ctr-names": + return func(p *libpod.Pod) bool { + ctrs, err := p.AllContainers() + if err != nil { + return false + } + for _, ctr := range ctrs { + if filterValue == ctr.Name() { + return true + } + } + return false + }, nil + case "ctr-number": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + + fVint, err2 := strconv.Atoi(filterValue) + if err2 != nil { + return false + } + return len(ctrIds) == fVint + }, nil + case "ctr-status": + if !util.StringInSlice(filterValue, + []string{"created", "restarting", "running", "paused", + "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } + return func(p *libpod.Pod) bool { + ctr_statuses, err := p.Status() + if err != nil { + return false + } + for _, ctr_status := range ctr_statuses { + state := ctr_status.String() + if ctr_status == define.ContainerStateConfigured { + state = "created" + } + if state == filterValue { + return true + } + } + return false + }, nil + case "id": + return func(p *libpod.Pod) bool { + return strings.Contains(p.ID(), filterValue) + }, nil + case "name": + return func(p *libpod.Pod) bool { + return strings.Contains(p.Name(), filterValue) + }, nil + case "status": + if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) { + return nil, errors.Errorf("%s is not a valid pod status", filterValue) + } + return func(p *libpod.Pod) bool { + status, err := p.GetPodStatus() + if err != nil { + return false + } + if strings.ToLower(status) == filterValue { + return true + } + return false + }, nil + } + return nil, errors.Errorf("%s is an invalid filter", filter) +} + var DefaultKernelNamespaces = "cgroup,ipc,net,uts" diff --git a/cmd/podmanV2/README.md b/cmd/podmanV2/README.md new file mode 100644 index 000000000..a17e6f850 --- /dev/null +++ b/cmd/podmanV2/README.md @@ -0,0 +1,113 @@ +# Adding a podman V2 commands + +## Build podman V2 + +```shell script +$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2 +``` +If you wish to include the libpod library in your program, +```shell script +$ go build -tags 'ABISupport' . +``` +The `--remote` flag may be used to connect to the Podman service using the API. +Otherwise, direct calls will be made to the Libpod library. +```shell script +$ go build -tags '!ABISupport' . +``` +The Libpod library is not linked into the executable. +All calls are made via the API and `--remote=False` is an error condition. + +## Adding a new command `podman manifests` +```shell script +$ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests +``` +Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/manifest.go``` +```go +package manifests + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman _manifests_ + manifestCmd = &cobra.Command{ + Use: "manifest", + Short: "Manage manifests", + Long: "Manage manifests", + Example: "podman manifests IMAGE", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, // Report error if there is no sub command given + } +) +func init() { + // Subscribe command to podman + registry.Commands = append(registry.Commands, registry.CliCommand{ + // _podman manifest_ will support both ABIMode and TunnelMode + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + // The definition for this command + Command: manifestCmd, + }) + // Setup cobra templates, sub commands will inherit + manifestCmd.SetHelpTemplate(registry.HelpTemplate()) + manifestCmd.SetUsageTemplate(registry.UsageTemplate()) +} + +// preRunE populates the image engine for sub commands +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewImageEngine(cmd, args) + return err +} +``` +To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add: +```go +package main + +import _ "github.com/containers/libpod/cmd/podmanV2/manifests" +``` + +## Adding a new sub command `podman manifests list` +Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/inspect.go``` +```go +package manifests + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman manifests _inspect_ + inspectCmd = &cobra.Command{ + Use: "inspect IMAGE", + Short: "Display manifest from image", + Long: "Displays the low-level information on a manifest identified by image name or ID", + RunE: inspect, + Example: "podman manifest DEADBEEF", + } +) + +func init() { + // Subscribe inspect sub command to manifest command + registry.Commands = append(registry.Commands, registry.CliCommand{ + // _podman manifest inspect_ will support both ABIMode and TunnelMode + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + // The definition for this command + Command: inspectCmd, + Parent: manifestCmd, + }) + + // This is where you would configure the cobra flags using inspectCmd.Flags() +} + +// Business logic: cmd is inspectCmd, args is the positional arguments from os.Args +func inspect(cmd *cobra.Command, args []string) error { + // Business logic using registry.ImageEngine + // Do not pull from libpod directly use the domain objects and types + return nil +} +``` diff --git a/cmd/podmanV2/containers/container.go b/cmd/podmanV2/containers/container.go new file mode 100644 index 000000000..6b44f2a3e --- /dev/null +++ b/cmd/podmanV2/containers/container.go @@ -0,0 +1,33 @@ +package containers + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Command: podman _container_ + containerCmd = &cobra.Command{ + Use: "container", + Short: "Manage containers", + Long: "Manage containers", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: containerCmd, + }) + containerCmd.SetHelpTemplate(registry.HelpTemplate()) + containerCmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go new file mode 100644 index 000000000..635be4789 --- /dev/null +++ b/cmd/podmanV2/containers/inspect.go @@ -0,0 +1,42 @@ +package containers + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman container _inspect_ + inspectCmd = &cobra.Command{ + Use: "inspect [flags] CONTAINER", + Short: "Display the configuration of a container", + Long: `Displays the low-level information on a container identified by name or ID.`, + PreRunE: inspectPreRunE, + RunE: inspect, + Example: `podman container inspect myCtr + podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: inspectCmd, + Parent: containerCmd, + }) +} + +func inspectPreRunE(cmd *cobra.Command, args []string) (err error) { + err = preRunE(cmd, args) + if err != nil { + return + } + + _, err = registry.NewImageEngine(cmd, args) + return err +} + +func inspect(cmd *cobra.Command, args []string) error { + return nil +} diff --git a/cmd/podmanV2/containers/list.go b/cmd/podmanV2/containers/list.go new file mode 100644 index 000000000..630d9bbc7 --- /dev/null +++ b/cmd/podmanV2/containers/list.go @@ -0,0 +1,34 @@ +package containers + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman container _list_ + listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, + Short: "List containers", + Long: "Prints out information about the containers", + RunE: containers, + Example: `podman container list -a + podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" + podman container list --size --sort names`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: listCmd, + Parent: containerCmd, + }) +} + +func containers(cmd *cobra.Command, args []string) error { + return nil +} diff --git a/cmd/podmanV2/containers/ps.go b/cmd/podmanV2/containers/ps.go new file mode 100644 index 000000000..ce3d66c51 --- /dev/null +++ b/cmd/podmanV2/containers/ps.go @@ -0,0 +1,29 @@ +package containers + +import ( + "strings" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman _ps_ + psCmd = &cobra.Command{ + Use: "ps", + Args: cobra.NoArgs, + Short: listCmd.Short, + Long: listCmd.Long, + PersistentPreRunE: preRunE, + RunE: containers, + Example: strings.Replace(listCmd.Example, "container list", "ps", -1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: psCmd, + }) +} diff --git a/cmd/podmanV2/images/history.go b/cmd/podmanV2/images/history.go new file mode 100644 index 000000000..c75ae6ddc --- /dev/null +++ b/cmd/podmanV2/images/history.go @@ -0,0 +1,79 @@ +package images + +import ( + "context" + "fmt" + "os" + "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" +) + +var ( + long = `Displays the history of an image. + + The information can be printed out in an easy to read, or user specified format, and can be truncated.` + + // podman _history_ + historyCmd = &cobra.Command{ + Use: "history [flags] IMAGE", + Short: "Show history of a specified image", + Long: long, + Example: "podman history quay.io/fedora/fedora", + Args: cobra.ExactArgs(1), + PersistentPreRunE: preRunE, + RunE: history, + } +) + +var cmdFlags = struct { + Human bool + NoTrunc bool + Quiet bool + Format string +}{} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: historyCmd, + }) + + historyCmd.SetHelpTemplate(registry.HelpTemplate()) + historyCmd.SetUsageTemplate(registry.UsageTemplate()) + flags := historyCmd.Flags() + flags.StringVar(&cmdFlags.Format, "format", "", "Change the output to JSON or a Go template") + flags.BoolVarP(&cmdFlags.Human, "human", "H", true, "Display sizes and dates in human readable format") + flags.BoolVar(&cmdFlags.NoTrunc, "no-trunc", false, "Do not truncate the output") + flags.BoolVar(&cmdFlags.NoTrunc, "notruncate", false, "Do not truncate the output") + flags.BoolVarP(&cmdFlags.Quiet, "quiet", "q", false, "Display the numeric IDs only") +} + +func history(cmd *cobra.Command, args []string) error { + results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{}) + if err != nil { + return err + } + + row := "{{slice $x.ID 0 12}}\t{{toRFC3339 $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{$x.Size}}\t{{$x.Comment}}\n" + if cmdFlags.Human { + row = "{{slice $x.ID 0 12}}\t{{toHumanDuration $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{toHumanSize $x.Size}}\t{{$x.Comment}}\n" + } + format := "{{range $y, $x := . }}" + row + "{{end}}" + + tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format)) + w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + + _, _ = w.Write(report.ReportHeader("id", "created", "created by", "size", "comment")) + err = tmpl.Execute(w, results.Layers) + if err != nil { + fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Failed to print report")) + } + w.Flush() + return nil +} diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go new file mode 100644 index 000000000..a15c3e826 --- /dev/null +++ b/cmd/podmanV2/images/image.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 ( + // Command: podman _image_ + imageCmd = &cobra.Command{ + Use: "image", + Short: "Manage images", + Long: "Manage images", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageCmd, + }) + imageCmd.SetHelpTemplate(registry.HelpTemplate()) + imageCmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewImageEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go new file mode 100644 index 000000000..a1e56396a --- /dev/null +++ b/cmd/podmanV2/images/images.go @@ -0,0 +1,46 @@ +package images + +import ( + "strings" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // podman _images_ + imagesCmd = &cobra.Command{ + Use: strings.Replace(listCmd.Use, "list", "images", 1), + Short: listCmd.Short, + Long: listCmd.Long, + PersistentPreRunE: preRunE, + RunE: images, + Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), + } + + imagesOpts = entities.ImageListOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imagesCmd, + }) + imagesCmd.SetHelpTemplate(registry.HelpTemplate()) + imagesCmd.SetUsageTemplate(registry.UsageTemplate()) + + flags := imagesCmd.Flags() + flags.BoolVarP(&imagesOpts.All, "all", "a", false, "Show all images (default hides intermediate images)") + flags.BoolVar(&imagesOpts.Digests, "digests", false, "Show digests") + flags.StringSliceVarP(&imagesOpts.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") + flags.StringVar(&imagesOpts.Format, "format", "", "Change the output format to JSON or a Go template") + flags.BoolVarP(&imagesOpts.Noheading, "noheading", "n", false, "Do not print column headings") + // TODO Need to learn how to deal with second name being a string instead of a char. + // This needs to be "no-trunc, notruncate" + flags.BoolVar(&imagesOpts.NoTrunc, "no-trunc", false, "Do not truncate output") + flags.BoolVar(&imagesOpts.NoTrunc, "notruncate", false, "Do not truncate output") + flags.BoolVarP(&imagesOpts.Quiet, "quiet", "q", false, "Display only image IDs") + flags.StringVar(&imagesOpts.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") + flags.BoolVarP(&imagesOpts.History, "history", "", false, "Display the image name history") +} diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go new file mode 100644 index 000000000..9c44cea35 --- /dev/null +++ b/cmd/podmanV2/images/inspect.go @@ -0,0 +1,124 @@ +package images + +import ( + "strings" + + "github.com/containers/buildah/pkg/formats" + "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, + Example: `podman image inspect alpine`, + } + + containerEngine entities.ContainerEngine +) + +// 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.GlobalFlags.EngineMode == entities.ABIMode { + // TODO: This is the same as V1. We could skip creating the flag altogether in V2... + _ = flags.MarkHidden("latest") + } +} + +const ( + inspectTypeContainer = "container" + inspectTypeImage = "image" + inspectAll = "all" +) + +func imageInspect(cmd *cobra.Command, args []string) error { + inspectType := inspectTypeImage + latestContainer := inspectOpts.Latest + + if len(args) == 0 && !latestContainer { + return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name") + } + + if len(args) > 0 && latestContainer { + 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) + } + + 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 strings.Contains(outputFormat, ".Dst") { + outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1) + } + if strings.Contains(outputFormat, ".ImageID") { + outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1) + } + _ = 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 +} diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go new file mode 100644 index 000000000..cfdfaaed2 --- /dev/null +++ b/cmd/podmanV2/images/list.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 ( + // Command: podman image _list_ + listCmd = &cobra.Command{ + Use: "list [flag] [IMAGE]", + Aliases: []string{"ls"}, + Short: "List images in local storage", + Long: "Lists images previously pulled to the system or created on the system.", + RunE: images, + Example: `podman image list --format json + podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" + podman image list --filter dangling=true`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: listCmd, + Parent: imageCmd, + }) +} + +func images(cmd *cobra.Command, args []string) error { + return nil +} diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go new file mode 100644 index 000000000..0df086352 --- /dev/null +++ b/cmd/podmanV2/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + "reflect" + "runtime" + + _ "github.com/containers/libpod/cmd/podmanV2/containers" + _ "github.com/containers/libpod/cmd/podmanV2/images" + _ "github.com/containers/libpod/cmd/podmanV2/networks" + _ "github.com/containers/libpod/cmd/podmanV2/pods" + "github.com/containers/libpod/cmd/podmanV2/registry" + _ "github.com/containers/libpod/cmd/podmanV2/volumes" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func init() { + if err := libpod.SetXdgDirs(); err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + initCobra() +} + +func initCobra() { + switch runtime.GOOS { + case "darwin": + fallthrough + case "windows": + registry.GlobalFlags.EngineMode = entities.TunnelMode + case "linux": + registry.GlobalFlags.EngineMode = entities.ABIMode + default: + logrus.Errorf("%s is not a supported OS", runtime.GOOS) + os.Exit(1) + } + + // TODO: Is there a Cobra way to "peek" at os.Args? + if ok := Contains("--remote", os.Args); ok { + registry.GlobalFlags.EngineMode = entities.TunnelMode + } + + cobra.OnInitialize(func() {}) +} + +func main() { + fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands)) + for _, c := range registry.Commands { + if Contains(registry.GlobalFlags.EngineMode, c.Mode) { + parent := rootCmd + if c.Parent != nil { + parent = c.Parent + } + parent.AddCommand(c.Command) + } + } + + Execute() + os.Exit(0) +} + +func Contains(item interface{}, slice interface{}) bool { + s := reflect.ValueOf(slice) + + switch s.Kind() { + case reflect.Array: + fallthrough + case reflect.Slice: + break + default: + return false + } + + for i := 0; i < s.Len(); i++ { + if s.Index(i).Interface() == item { + return true + } + } + return false +} diff --git a/cmd/podmanV2/networks/network.go b/cmd/podmanV2/networks/network.go new file mode 100644 index 000000000..fc92d2321 --- /dev/null +++ b/cmd/podmanV2/networks/network.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 ( + // Command: podman _network_ + cmd = &cobra.Command{ + Use: "network", + Short: "Manage networks", + Long: "Manage networks", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: cmd, + }) + cmd.SetHelpTemplate(registry.HelpTemplate()) + cmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go new file mode 100644 index 000000000..81c0d33e1 --- /dev/null +++ b/cmd/podmanV2/pods/pod.go @@ -0,0 +1,33 @@ +package pods + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Command: podman _pod_ + podCmd = &cobra.Command{ + Use: "pod", + Short: "Manage pods", + Long: "Manage pods", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: podCmd, + }) + podCmd.SetHelpTemplate(registry.HelpTemplate()) + podCmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go new file mode 100644 index 000000000..d4c625b2e --- /dev/null +++ b/cmd/podmanV2/pods/ps.go @@ -0,0 +1,32 @@ +package pods + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + psDescription = "List all pods on system including their names, ids and current state." + + // Command: podman pod _ps_ + psCmd = &cobra.Command{ + Use: "ps", + Aliases: []string{"ls", "list"}, + Short: "list pods", + Long: psDescription, + RunE: pods, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: psCmd, + Parent: podCmd, + }) +} + +func pods(cmd *cobra.Command, args []string) error { + return nil +} diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go new file mode 100644 index 000000000..fa51d6535 --- /dev/null +++ b/cmd/podmanV2/registry/registry.go @@ -0,0 +1,96 @@ +package registry + +import ( + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/domain/infra" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +type CliCommand struct { + Mode []entities.EngineMode + Command *cobra.Command + Parent *cobra.Command +} + +var ( + Commands []CliCommand + GlobalFlags entities.EngineFlags + imageEngine entities.ImageEngine + containerEngine entities.ContainerEngine + PodmanTunnel bool +) + +// HelpTemplate returns the help template for podman commands +// This uses the short and long options. +// command should not use this. +func HelpTemplate() string { + return `{{.Short}} + +Description: + {{.Long}} + +{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` +} + +// UsageTemplate returns the usage template for podman commands +// This blocks the displaying of the global options. The main podman +// command should not use this. +func UsageTemplate() string { + return `Usage(v2):{{if (and .Runnable (not .HasAvailableSubCommands))}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: + {{.Example}}{{end}}{{if .HasAvailableSubCommands}} + +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} +{{end}} +` +} + +func ImageEngine() entities.ImageEngine { + return imageEngine +} + +// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions +func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) { + if imageEngine == nil { + engine, err := infra.NewImageEngine(GlobalFlags.EngineMode, entities.EngineOptions{}) + if err != nil { + return nil, err + } + imageEngine = engine + } + return imageEngine, nil +} + +func ContainerEngine() entities.ContainerEngine { + return containerEngine +} + +// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions +func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) { + if containerEngine == nil { + engine, err := infra.NewContainerEngine(GlobalFlags.EngineMode, entities.EngineOptions{}) + if err != nil { + return nil, err + } + containerEngine = engine + } + return containerEngine, nil +} + +func SubCommandExists(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0]) + } + return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath()) +} diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go new file mode 100644 index 000000000..dc43d4f9b --- /dev/null +++ b/cmd/podmanV2/report/templates.go @@ -0,0 +1,61 @@ +package report + +import ( + "strings" + "text/template" + "time" + + "github.com/docker/go-units" +) + +var defaultFuncMap = template.FuncMap{ + "ellipsis": func(s string, length int) string { + if len(s) > length { + return s[:length-3] + "..." + } + return s + }, + // TODO: Remove on Go 1.14 port + "slice": func(s string, i, j int) string { + if i > j || len(s) < i { + return s + } + if len(s) < j { + return s[i:] + } + return s[i:j] + }, + "toRFC3339": func(t int64) string { + return time.Unix(t, 0).Format(time.RFC3339) + }, + "toHumanDuration": func(t int64) string { + return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago" + }, + "toHumanSize": func(sz int64) string { + return units.HumanSize(float64(sz)) + }, +} + +func ReportHeader(columns ...string) []byte { + hdr := make([]string, len(columns)) + for i, h := range columns { + hdr[i] = strings.ToUpper(h) + } + return []byte(strings.Join(hdr, "\t") + "\n") +} + +func AppendFuncMap(funcMap template.FuncMap) template.FuncMap { + merged := PodmanTemplateFuncs() + for k, v := range funcMap { + merged[k] = v + } + return merged +} + +func PodmanTemplateFuncs() template.FuncMap { + merged := make(template.FuncMap) + for k, v := range defaultFuncMap { + merged[k] = v + } + return merged +} diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go new file mode 100644 index 000000000..778184f28 --- /dev/null +++ b/cmd/podmanV2/root.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "os" + "path" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/version" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: path.Base(os.Args[0]), + Long: "Manage pods, containers and images", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + RunE: registry.SubCommandExists, + Version: version.Version, +} + +func init() { + // Override default --help information of `--version` global flag} + var dummyVersion bool + rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman") + rootCmd.PersistentFlags().BoolVarP(®istry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel") +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go new file mode 100644 index 000000000..30ed328e8 --- /dev/null +++ b/cmd/podmanV2/system/system.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 ( + // Command: podman _system_ + cmd = &cobra.Command{ + Use: "system", + Short: "Manage podman", + Long: "Manage podman", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: cmd, + }) + cmd.SetHelpTemplate(registry.HelpTemplate()) + cmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} diff --git a/cmd/podmanV2/volumes/volume.go b/cmd/podmanV2/volumes/volume.go new file mode 100644 index 000000000..245c06da0 --- /dev/null +++ b/cmd/podmanV2/volumes/volume.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 ( + // Command: podman _volume_ + cmd = &cobra.Command{ + Use: "volume", + Short: "Manage volumes", + Long: "Volumes are created in and can be shared between containers", + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: cmd, + }) + cmd.SetHelpTemplate(registry.HelpTemplate()) + cmd.SetUsageTemplate(registry.UsageTemplate()) +} + +func preRunE(cmd *cobra.Command, args []string) error { + _, err := registry.NewContainerEngine(cmd, args) + return err +} |