311 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			311 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| package pods
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"text/tabwriter"
 | |
| 	"text/template"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containers/common/pkg/completion"
 | |
| 	"github.com/containers/common/pkg/report"
 | |
| 	"github.com/containers/podman/v3/cmd/podman/common"
 | |
| 	"github.com/containers/podman/v3/cmd/podman/parse"
 | |
| 	"github.com/containers/podman/v3/cmd/podman/registry"
 | |
| 	"github.com/containers/podman/v3/cmd/podman/validate"
 | |
| 	"github.com/containers/podman/v3/pkg/domain/entities"
 | |
| 	"github.com/docker/go-units"
 | |
| 	"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  [options]",
 | |
| 		Aliases:           []string{"ls", "list"},
 | |
| 		Short:             "List pods",
 | |
| 		Long:              psDescription,
 | |
| 		RunE:              pods,
 | |
| 		Args:              validate.NoArgs,
 | |
| 		ValidArgsFunction: completion.AutocompleteNone,
 | |
| 	}
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	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 [] ?
 | |
| 
 | |
| 	filterFlagName := "filter"
 | |
| 	flags.StringSliceVarP(&inputFilters, filterFlagName, "f", []string{}, "Filter output based on conditions given")
 | |
| 	_ = psCmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePodPsFilters)
 | |
| 
 | |
| 	formatFlagName := "format"
 | |
| 	flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template")
 | |
| 	_ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat)
 | |
| 
 | |
| 	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")
 | |
| 
 | |
| 	sortFlagName := "sort"
 | |
| 	flags.StringVar(&psInput.Sort, sortFlagName, "created", "Sort output by created, id, name, or number")
 | |
| 	_ = psCmd.RegisterFlagCompletionFunc(sortFlagName, common.AutocompletePodPsSort)
 | |
| 
 | |
| 	validate.AddLatestFlag(psCmd, &psInput.Latest)
 | |
| }
 | |
| 
 | |
| func pods(cmd *cobra.Command, _ []string) error {
 | |
| 	if psInput.Quiet && len(psInput.Format) > 0 {
 | |
| 		return errors.New("quiet and format cannot be used together")
 | |
| 	}
 | |
| 	if cmd.Flag("filter").Changed {
 | |
| 		psInput.Filters = make(map[string][]string)
 | |
| 		for _, f := range inputFilters {
 | |
| 			split := strings.SplitN(f, "=", 2)
 | |
| 			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 err := sortPodPsOutput(psInput.Sort, responses); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	switch {
 | |
| 	case report.IsJSON(psInput.Format):
 | |
| 		b, err := json.MarshalIndent(responses, "", "  ")
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		fmt.Println(string(b))
 | |
| 		return nil
 | |
| 	case psInput.Quiet:
 | |
| 		for _, p := range responses {
 | |
| 			fmt.Println(p.Id)
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// Formatted output below
 | |
| 	lpr := make([]ListPodReporter, 0, len(responses))
 | |
| 	for _, r := range responses {
 | |
| 		lpr = append(lpr, ListPodReporter{r})
 | |
| 	}
 | |
| 
 | |
| 	headers := report.Headers(ListPodReporter{}, map[string]string{
 | |
| 		"Id":                 "POD ID",
 | |
| 		"Name":               "NAME",
 | |
| 		"Status":             "STATUS",
 | |
| 		"Labels":             "LABELS",
 | |
| 		"NumberOfContainers": "# OF CONTAINERS",
 | |
| 		"Created":            "CREATED",
 | |
| 		"InfraID":            "INFRA ID",
 | |
| 	})
 | |
| 	renderHeaders := true
 | |
| 	row := podPsFormat()
 | |
| 	if cmd.Flags().Changed("format") {
 | |
| 		renderHeaders = parse.HasTable(psInput.Format)
 | |
| 		row = report.NormalizeFormat(psInput.Format)
 | |
| 	}
 | |
| 	format := parse.EnforceRange(row)
 | |
| 
 | |
| 	tmpl, err := template.New("listPods").Parse(format)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
 | |
| 	defer w.Flush()
 | |
| 
 | |
| 	if renderHeaders {
 | |
| 		if err := tmpl.Execute(w, headers); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return tmpl.Execute(w, lpr)
 | |
| }
 | |
| 
 | |
| func podPsFormat() string {
 | |
| 	row := []string{"{{.Id}}", "{{.Name}}", "{{.Status}}", "{{.Created}}", "{{.InfraID}}"}
 | |
| 
 | |
| 	if psInput.CtrIds {
 | |
| 		row = append(row, "{{.ContainerIds}}")
 | |
| 	}
 | |
| 
 | |
| 	if psInput.CtrNames {
 | |
| 		row = append(row, "{{.ContainerNames}}")
 | |
| 	}
 | |
| 
 | |
| 	if psInput.CtrStatus {
 | |
| 		row = append(row, "{{.ContainerStatuses}}")
 | |
| 	}
 | |
| 
 | |
| 	if psInput.Namespace {
 | |
| 		row = append(row, "{{.Cgroup}}", "{{.Namespace}}")
 | |
| 	}
 | |
| 
 | |
| 	if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
 | |
| 		row = append(row, "{{.NumberOfContainers}}")
 | |
| 	}
 | |
| 	return strings.Join(row, "\t") + "\n"
 | |
| }
 | |
| 
 | |
| // 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"
 | |
| }
 | |
| 
 | |
| // Labels returns a map of the pod's labels
 | |
| func (l ListPodReporter) Labels() map[string]string {
 | |
| 	return l.ListPodsReport.Labels
 | |
| }
 | |
| 
 | |
| // Networks returns the infra container network names in string format
 | |
| func (l ListPodReporter) Networks() string {
 | |
| 	return strings.Join(l.ListPodsReport.Networks, ",")
 | |
| }
 | |
| 
 | |
| // NumberOfContainers returns an int representation for
 | |
| // the number of containers belonging to the pod
 | |
| func (l ListPodReporter) NumberOfContainers() int {
 | |
| 	return len(l.Containers)
 | |
| }
 | |
| 
 | |
| // ID is a wrapper to Id for compat, typos
 | |
| func (l ListPodReporter) ID() string {
 | |
| 	return l.Id()
 | |
| }
 | |
| 
 | |
| // Id returns the Pod id
 | |
| func (l ListPodReporter) Id() string { // nolint
 | |
| 	if noTrunc {
 | |
| 		return l.ListPodsReport.Id
 | |
| 	}
 | |
| 	return l.ListPodsReport.Id[0:12]
 | |
| }
 | |
| 
 | |
| // 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 { // nolint
 | |
| 	if len(l.ListPodsReport.InfraId) == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	if noTrunc {
 | |
| 		return l.ListPodsReport.InfraId
 | |
| 	}
 | |
| 	return l.ListPodsReport.InfraId[0:12]
 | |
| }
 | |
| 
 | |
| func (l ListPodReporter) ContainerIds() string {
 | |
| 	ctrids := make([]string, 0, len(l.Containers))
 | |
| 	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 {
 | |
| 	ctrNames := make([]string, 0, len(l.Containers))
 | |
| 	for _, c := range l.Containers {
 | |
| 		ctrNames = append(ctrNames, c.Names)
 | |
| 	}
 | |
| 	return strings.Join(ctrNames, ",")
 | |
| }
 | |
| 
 | |
| func (l ListPodReporter) ContainerStatuses() string {
 | |
| 	statuses := make([]string, 0, len(l.Containers))
 | |
| 	for _, c := range l.Containers {
 | |
| 		statuses = append(statuses, c.Status)
 | |
| 	}
 | |
| 	return strings.Join(statuses, ",")
 | |
| }
 | |
| 
 | |
| func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error {
 | |
| 	switch sortBy {
 | |
| 	case "created":
 | |
| 		sort.Sort(podPsSortedCreated{lprs})
 | |
| 	case "id":
 | |
| 		sort.Sort(podPsSortedID{lprs})
 | |
| 	case "name":
 | |
| 		sort.Sort(podPsSortedName{lprs})
 | |
| 	case "number":
 | |
| 		sort.Sort(podPsSortedNumber{lprs})
 | |
| 	case "status":
 | |
| 		sort.Sort(podPsSortedStatus{lprs})
 | |
| 	default:
 | |
| 		return errors.Errorf("invalid option for --sort, options are: id, names, or number")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type lprSort []*entities.ListPodsReport
 | |
| 
 | |
| func (a lprSort) Len() int      { return len(a) }
 | |
| func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 | |
| 
 | |
| type podPsSortedCreated struct{ lprSort }
 | |
| 
 | |
| func (a podPsSortedCreated) Less(i, j int) bool {
 | |
| 	return a.lprSort[i].Created.After(a.lprSort[j].Created)
 | |
| }
 | |
| 
 | |
| type podPsSortedID struct{ lprSort }
 | |
| 
 | |
| func (a podPsSortedID) Less(i, j int) bool { return a.lprSort[i].Id < a.lprSort[j].Id }
 | |
| 
 | |
| type podPsSortedNumber struct{ lprSort }
 | |
| 
 | |
| func (a podPsSortedNumber) Less(i, j int) bool {
 | |
| 	return len(a.lprSort[i].Containers) < len(a.lprSort[j].Containers)
 | |
| }
 | |
| 
 | |
| type podPsSortedName struct{ lprSort }
 | |
| 
 | |
| func (a podPsSortedName) Less(i, j int) bool { return a.lprSort[i].Name < a.lprSort[j].Name }
 | |
| 
 | |
| type podPsSortedStatus struct{ lprSort }
 | |
| 
 | |
| func (a podPsSortedStatus) Less(i, j int) bool {
 | |
| 	return a.lprSort[i].Status < a.lprSort[j].Status
 | |
| }
 |