package pods import ( "context" "encoding/json" "fmt" "io" "os" "strings" "text/tabwriter" "text/template" "time" "github.com/docker/go-units" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" "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, } ) 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 lpr []ListPodReporter ) 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 } for _, r := range responses { lpr = append(lpr, ListPodReporter{r}) } headers, row := createPodPsOut() 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 } tmpl, err := template.New("listPods").Parse(format) if err != nil { return err } if !psInput.Quiet { w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) } if err := tmpl.Execute(w, lpr); err != nil { return err } if flusher, ok := w.(interface{ Flush() error }); ok { return flusher.Flush() } return nil } func createPodPsOut() (string, string) { var row string headers := defaultHeaders if noTrunc { row += "{{.Id}}" } else { row += "{{slice .Id 0 12}}" } row += "\t{{.Name}}\t{{.Status}}\t{{.Created}}" if psInput.CtrIds { headers += "\tIDS" row += "\t{{.ContainerIds}}" } if psInput.CtrNames { headers += "\tNAMES" row += "\t{{.ContainerNames}}" } if psInput.CtrStatus { headers += "\tSTATUS" row += "\t{{.ContainerStatuses}}" } if psInput.Namespace { headers += "\tCGROUP\tNAMESPACES" row += "\t{{.Cgroup}}\t{{.Namespace}}" } if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds { headers += "\t# OF CONTAINERS" row += "\t{{.NumberOfContainers}}" } headers += "\tINFRA ID\n" if noTrunc { row += "\t{{.InfraId}}\n" } else { row += "\t{{slice .InfraId 0 12}}\n" } return headers, row } // ListPodReporter is a struct for pod ps output type ListPodReporter struct { *entities.ListPodsReport } // Created returns a human readable created time/date func (l ListPodReporter) Created() string { return units.HumanDuration(time.Since(l.ListPodsReport.Created)) + " ago" } // NumberofContainers returns an int representation for // the number of containers belonging to the pod func (l ListPodReporter) NumberOfContainers() int { return len(l.Containers) } // Added for backwards compatibility with podmanv1 func (l ListPodReporter) InfraID() string { return l.InfraId() } // InfraId returns the infra container id for the pod // depending on trunc func (l ListPodReporter) InfraId() string { if noTrunc { return l.ListPodsReport.InfraId } return l.ListPodsReport.InfraId[0:12] } func (l ListPodReporter) ContainerIds() string { var ctrids []string for _, c := range l.Containers { id := c.Id if !noTrunc { id = id[0:12] } ctrids = append(ctrids, id) } return strings.Join(ctrids, ",") } func (l ListPodReporter) ContainerNames() string { var ctrNames []string for _, c := range l.Containers { ctrNames = append(ctrNames, c.Names) } return strings.Join(ctrNames, ",") } func (l ListPodReporter) ContainerStatuses() string { var statuses []string for _, c := range l.Containers { statuses = append(statuses, c.Status) } return strings.Join(statuses, ",") }