593 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			593 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containers/libpod/cmd/podman/formats"
 | |
| 	"github.com/containers/libpod/cmd/podman/libpodruntime"
 | |
| 	"github.com/containers/libpod/cmd/podman/shared"
 | |
| 	"github.com/containers/libpod/libpod"
 | |
| 	"github.com/containers/libpod/pkg/util"
 | |
| 	"github.com/docker/go-units"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/urfave/cli"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	STOPPED      = "Stopped"
 | |
| 	RUNNING      = "Running"
 | |
| 	PAUSED       = "Paused"
 | |
| 	EXITED       = "Exited"
 | |
| 	ERROR        = "Error"
 | |
| 	CREATED      = "Created"
 | |
| 	NUM_CTR_INFO = 10
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	bc_opts shared.PsOptions
 | |
| )
 | |
| 
 | |
| type podPsCtrInfo struct {
 | |
| 	Name   string `"json:name,omitempty"`
 | |
| 	Id     string `"json:id,omitempty"`
 | |
| 	Status string `"json:status,omitempty"`
 | |
| }
 | |
| 
 | |
| type podPsOptions struct {
 | |
| 	NoTrunc            bool
 | |
| 	Format             string
 | |
| 	Sort               string
 | |
| 	Quiet              bool
 | |
| 	NumberOfContainers bool
 | |
| 	Cgroup             bool
 | |
| 	NamesOfContainers  bool
 | |
| 	IdsOfContainers    bool
 | |
| 	StatusOfContainers bool
 | |
| }
 | |
| 
 | |
| type podPsTemplateParams struct {
 | |
| 	Created            string
 | |
| 	ID                 string
 | |
| 	Name               string
 | |
| 	NumberOfContainers int
 | |
| 	Status             string
 | |
| 	Cgroup             string
 | |
| 	ContainerInfo      string
 | |
| 	InfraID            string
 | |
| 	Namespaces         string
 | |
| }
 | |
| 
 | |
| // podPsJSONParams is used as a base structure for the psParams
 | |
| // If template output is requested, podPsJSONParams will be converted to
 | |
| // podPsTemplateParams.
 | |
| // podPsJSONParams will be populated by data from libpod.Container,
 | |
| // the members of the struct are the sama data types as their sources.
 | |
| type podPsJSONParams struct {
 | |
| 	CreatedAt          time.Time      `json:"createdAt"`
 | |
| 	ID                 string         `json:"id"`
 | |
| 	Name               string         `json:"name"`
 | |
| 	NumberOfContainers int            `json:"numberOfContainers"`
 | |
| 	Status             string         `json:"status"`
 | |
| 	CtrsInfo           []podPsCtrInfo `json:"containerInfo,omitempty"`
 | |
| 	Cgroup             string         `json:"cgroup,omitempty"`
 | |
| 	InfraID            string         `json:"infraContainerId,omitempty"`
 | |
| 	Namespaces         []string       `json:"namespaces,omitempty"`
 | |
| }
 | |
| 
 | |
| // Type declaration and functions for sorting the pod PS output
 | |
| type podPsSorted []podPsJSONParams
 | |
| 
 | |
| func (a podPsSorted) Len() int      { return len(a) }
 | |
| func (a podPsSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 | |
| 
 | |
| type podPsSortedCreated struct{ podPsSorted }
 | |
| 
 | |
| func (a podPsSortedCreated) Less(i, j int) bool {
 | |
| 	return a.podPsSorted[i].CreatedAt.After(a.podPsSorted[j].CreatedAt)
 | |
| }
 | |
| 
 | |
| type podPsSortedId struct{ podPsSorted }
 | |
| 
 | |
| func (a podPsSortedId) Less(i, j int) bool { return a.podPsSorted[i].ID < a.podPsSorted[j].ID }
 | |
| 
 | |
| type podPsSortedNumber struct{ podPsSorted }
 | |
| 
 | |
| func (a podPsSortedNumber) Less(i, j int) bool {
 | |
| 	return len(a.podPsSorted[i].CtrsInfo) < len(a.podPsSorted[j].CtrsInfo)
 | |
| }
 | |
| 
 | |
| type podPsSortedName struct{ podPsSorted }
 | |
| 
 | |
| func (a podPsSortedName) Less(i, j int) bool { return a.podPsSorted[i].Name < a.podPsSorted[j].Name }
 | |
| 
 | |
| type podPsSortedStatus struct{ podPsSorted }
 | |
| 
 | |
| func (a podPsSortedStatus) Less(i, j int) bool {
 | |
| 	return a.podPsSorted[i].Status < a.podPsSorted[j].Status
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	podPsFlags = []cli.Flag{
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "ctr-names",
 | |
| 			Usage: "Display the container names",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "ctr-ids",
 | |
| 			Usage: "Display the container UUIDs. If no-trunc is not set they will be truncated",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "ctr-status",
 | |
| 			Usage: "Display the container status",
 | |
| 		},
 | |
| 		cli.StringFlag{
 | |
| 			Name:  "filter, f",
 | |
| 			Usage: "Filter output based on conditions given",
 | |
| 		},
 | |
| 		cli.StringFlag{
 | |
| 			Name:  "format",
 | |
| 			Usage: "Pretty-print pods to JSON or using a Go template",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "latest, l",
 | |
| 			Usage: "Show the latest pod created",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "namespace, ns",
 | |
| 			Usage: "Display namespace information of the pod",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "no-trunc",
 | |
| 			Usage: "Do not truncate pod and container IDs",
 | |
| 		},
 | |
| 		cli.BoolFlag{
 | |
| 			Name:  "quiet, q",
 | |
| 			Usage: "Print the numeric IDs of the pods only",
 | |
| 		},
 | |
| 		cli.StringFlag{
 | |
| 			Name:  "sort",
 | |
| 			Usage: "Sort output by created, id, name, or number",
 | |
| 			Value: "created",
 | |
| 		},
 | |
| 	}
 | |
| 	podPsDescription = "List all pods on system including their names, ids and current state."
 | |
| 	podPsCommand     = cli.Command{
 | |
| 		Name:                   "ps",
 | |
| 		Aliases:                []string{"ls", "list"},
 | |
| 		Usage:                  "List pods",
 | |
| 		Description:            podPsDescription,
 | |
| 		Flags:                  sortFlags(podPsFlags),
 | |
| 		Action:                 podPsCmd,
 | |
| 		UseShortOptionHandling: true,
 | |
| 		OnUsageError:           usageErrorHandler,
 | |
| 	}
 | |
| )
 | |
| 
 | |
| func podPsCmd(c *cli.Context) error {
 | |
| 	if err := validateFlags(c, podPsFlags); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := podPsCheckFlagsPassed(c); err != nil {
 | |
| 		return errors.Wrapf(err, "error with flags passed")
 | |
| 	}
 | |
| 
 | |
| 	runtime, err := libpodruntime.GetRuntime(c)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrapf(err, "error creating libpod runtime")
 | |
| 	}
 | |
| 	defer runtime.Shutdown(false)
 | |
| 
 | |
| 	if len(c.Args()) > 0 {
 | |
| 		return errors.Errorf("too many arguments, ps takes no arguments")
 | |
| 	}
 | |
| 
 | |
| 	opts := podPsOptions{
 | |
| 		NoTrunc:            c.Bool("no-trunc"),
 | |
| 		Quiet:              c.Bool("quiet"),
 | |
| 		Sort:               c.String("sort"),
 | |
| 		IdsOfContainers:    c.Bool("ctr-ids"),
 | |
| 		NamesOfContainers:  c.Bool("ctr-names"),
 | |
| 		StatusOfContainers: c.Bool("ctr-status"),
 | |
| 	}
 | |
| 
 | |
| 	opts.Format = genPodPsFormat(c)
 | |
| 
 | |
| 	var filterFuncs []libpod.PodFilter
 | |
| 	if c.String("filter") != "" {
 | |
| 		filters := strings.Split(c.String("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], runtime)
 | |
| 			if err != nil {
 | |
| 				return errors.Wrapf(err, "invalid filter")
 | |
| 			}
 | |
| 			filterFuncs = append(filterFuncs, generatedFunc)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var pods []*libpod.Pod
 | |
| 	if c.IsSet("latest") {
 | |
| 		pod, err := runtime.GetLatestPod()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		pods = append(pods, pod)
 | |
| 	} else {
 | |
| 		pods, err = runtime.GetAllPods()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	podsFiltered := make([]*libpod.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, runtime)
 | |
| }
 | |
| 
 | |
| // podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
 | |
| func podPsCheckFlagsPassed(c *cli.Context) error {
 | |
| 	// quiet, and format with Go template are mutually exclusive
 | |
| 	flags := 0
 | |
| 	if c.Bool("quiet") {
 | |
| 		flags++
 | |
| 	}
 | |
| 	if c.IsSet("format") && c.String("format") != formats.JSONString {
 | |
| 		flags++
 | |
| 	}
 | |
| 	if flags > 1 {
 | |
| 		return errors.Errorf("quiet and format with Go template are mutually exclusive")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (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 == libpod.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 := shared.GetPodStatus(p)
 | |
| 			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 *cli.Context) string {
 | |
| 	format := ""
 | |
| 	if c.String("format") != "" {
 | |
| 		// "\t" from the command line is not being recognized as a tab
 | |
| 		// replacing the string "\t" to a tab character if the user passes in "\t"
 | |
| 		format = strings.Replace(c.String("format"), `\t`, "\t", -1)
 | |
| 	} else if c.Bool("quiet") {
 | |
| 		format = formats.IDString
 | |
| 	} else {
 | |
| 		format = "table {{.ID}}\t{{.Name}}\t{{.Status}}\t{{.Created}}"
 | |
| 		if c.Bool("namespace") {
 | |
| 			format += "\t{{.Cgroup}}\t{{.Namespaces}}"
 | |
| 		}
 | |
| 		if c.Bool("ctr-names") || c.Bool("ctr-ids") || c.Bool("ctr-status") {
 | |
| 			format += "\t{{.ContainerInfo}}"
 | |
| 		} else {
 | |
| 			format += "\t{{.NumberOfContainers}}"
 | |
| 		}
 | |
| 		format += "\t{{.InfraID}}"
 | |
| 	}
 | |
| 	return format
 | |
| }
 | |
| 
 | |
| func podPsToGeneric(templParams []podPsTemplateParams, JSONParams []podPsJSONParams) (genericParams []interface{}) {
 | |
| 	if len(templParams) > 0 {
 | |
| 		for _, v := range templParams {
 | |
| 			genericParams = append(genericParams, interface{}(v))
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 	for _, v := range JSONParams {
 | |
| 		genericParams = append(genericParams, interface{}(v))
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // generate the accurate header based on template given
 | |
| func (p *podPsTemplateParams) podHeaderMap() map[string]string {
 | |
| 	v := reflect.Indirect(reflect.ValueOf(p))
 | |
| 	values := make(map[string]string)
 | |
| 
 | |
| 	for i := 0; i < v.NumField(); i++ {
 | |
| 		key := v.Type().Field(i).Name
 | |
| 		value := key
 | |
| 		if value == "ID" {
 | |
| 			value = "Pod" + value
 | |
| 		}
 | |
| 		if value == "NumberOfContainers" {
 | |
| 			value = "#OfContainers"
 | |
| 		}
 | |
| 		values[key] = strings.ToUpper(splitCamelCase(value))
 | |
| 	}
 | |
| 	return values
 | |
| }
 | |
| 
 | |
| func sortPodPsOutput(sortBy string, psOutput podPsSorted) (podPsSorted, error) {
 | |
| 	switch sortBy {
 | |
| 	case "created":
 | |
| 		sort.Sort(podPsSortedCreated{psOutput})
 | |
| 	case "id":
 | |
| 		sort.Sort(podPsSortedId{psOutput})
 | |
| 	case "name":
 | |
| 		sort.Sort(podPsSortedName{psOutput})
 | |
| 	case "number":
 | |
| 		sort.Sort(podPsSortedNumber{psOutput})
 | |
| 	case "status":
 | |
| 		sort.Sort(podPsSortedStatus{psOutput})
 | |
| 	default:
 | |
| 		return nil, errors.Errorf("invalid option for --sort, options are: id, names, or number")
 | |
| 	}
 | |
| 	return psOutput, nil
 | |
| }
 | |
| 
 | |
| // getPodTemplateOutput returns the modified container information
 | |
| func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podPsTemplateParams, error) {
 | |
| 	var (
 | |
| 		psOutput []podPsTemplateParams
 | |
| 	)
 | |
| 
 | |
| 	for _, psParam := range psParams {
 | |
| 		podID := psParam.ID
 | |
| 		infraID := psParam.InfraID
 | |
| 		var ctrStr string
 | |
| 
 | |
| 		truncated := ""
 | |
| 		if !opts.NoTrunc {
 | |
| 			podID = shortID(podID)
 | |
| 			if len(psParam.CtrsInfo) > NUM_CTR_INFO {
 | |
| 				psParam.CtrsInfo = psParam.CtrsInfo[:NUM_CTR_INFO]
 | |
| 				truncated = "..."
 | |
| 			}
 | |
| 			infraID = shortID(infraID)
 | |
| 		}
 | |
| 		for _, ctrInfo := range psParam.CtrsInfo {
 | |
| 			infoSlice := make([]string, 0)
 | |
| 			if opts.IdsOfContainers {
 | |
| 				if opts.NoTrunc {
 | |
| 					infoSlice = append(infoSlice, ctrInfo.Id)
 | |
| 				} else {
 | |
| 					infoSlice = append(infoSlice, shortID(ctrInfo.Id))
 | |
| 				}
 | |
| 			}
 | |
| 			if opts.NamesOfContainers {
 | |
| 				infoSlice = append(infoSlice, ctrInfo.Name)
 | |
| 			}
 | |
| 			if opts.StatusOfContainers {
 | |
| 				infoSlice = append(infoSlice, ctrInfo.Status)
 | |
| 			}
 | |
| 			if len(infoSlice) != 0 {
 | |
| 				ctrStr += fmt.Sprintf("[%s] ", strings.Join(infoSlice, ","))
 | |
| 			}
 | |
| 		}
 | |
| 		ctrStr += truncated
 | |
| 		params := podPsTemplateParams{
 | |
| 			Created:            units.HumanDuration(time.Since(psParam.CreatedAt)) + " ago",
 | |
| 			ID:                 podID,
 | |
| 			Name:               psParam.Name,
 | |
| 			Status:             psParam.Status,
 | |
| 			NumberOfContainers: psParam.NumberOfContainers,
 | |
| 			Cgroup:             psParam.Cgroup,
 | |
| 			ContainerInfo:      ctrStr,
 | |
| 			InfraID:            infraID,
 | |
| 			Namespaces:         strings.Join(psParam.Namespaces, ","),
 | |
| 		}
 | |
| 
 | |
| 		psOutput = append(psOutput, params)
 | |
| 	}
 | |
| 
 | |
| 	return psOutput, nil
 | |
| }
 | |
| 
 | |
| func getNamespaces(pod *libpod.Pod) []string {
 | |
| 	var shared []string
 | |
| 	if pod.SharesPID() {
 | |
| 		shared = append(shared, "pid")
 | |
| 	}
 | |
| 	if pod.SharesNet() {
 | |
| 		shared = append(shared, "net")
 | |
| 	}
 | |
| 	if pod.SharesMount() {
 | |
| 		shared = append(shared, "mnt")
 | |
| 	}
 | |
| 	if pod.SharesIPC() {
 | |
| 		shared = append(shared, "ipc")
 | |
| 	}
 | |
| 	if pod.SharesUser() {
 | |
| 		shared = append(shared, "user")
 | |
| 	}
 | |
| 	if pod.SharesCgroup() {
 | |
| 		shared = append(shared, "cgroup")
 | |
| 	}
 | |
| 	if pod.SharesUTS() {
 | |
| 		shared = append(shared, "uts")
 | |
| 	}
 | |
| 	return shared
 | |
| }
 | |
| 
 | |
| // getAndSortPodJSONOutput returns the container info in its raw, sorted form
 | |
| func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) {
 | |
| 	var (
 | |
| 		psOutput []podPsJSONParams
 | |
| 	)
 | |
| 
 | |
| 	for _, pod := range pods {
 | |
| 		ctrs, err := pod.AllContainers()
 | |
| 		ctrsInfo := make([]podPsCtrInfo, 0)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		ctrNum := len(ctrs)
 | |
| 		status, err := shared.GetPodStatus(pod)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		infraID, err := pod.InfraContainerID()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		for _, ctr := range ctrs {
 | |
| 			batchInfo, err := shared.BatchContainerOp(ctr, bc_opts)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			var status string
 | |
| 			switch batchInfo.ConState {
 | |
| 			case libpod.ContainerStateExited:
 | |
| 				fallthrough
 | |
| 			case libpod.ContainerStateStopped:
 | |
| 				status = EXITED
 | |
| 			case libpod.ContainerStateRunning:
 | |
| 				status = RUNNING
 | |
| 			case libpod.ContainerStatePaused:
 | |
| 				status = PAUSED
 | |
| 			case libpod.ContainerStateCreated, libpod.ContainerStateConfigured:
 | |
| 				status = CREATED
 | |
| 			default:
 | |
| 				status = ERROR
 | |
| 			}
 | |
| 			ctrsInfo = append(ctrsInfo, podPsCtrInfo{
 | |
| 				Name:   batchInfo.ConConfig.Name,
 | |
| 				Id:     ctr.ID(),
 | |
| 				Status: status,
 | |
| 			})
 | |
| 		}
 | |
| 		params := podPsJSONParams{
 | |
| 			CreatedAt:          pod.CreatedTime(),
 | |
| 			ID:                 pod.ID(),
 | |
| 			Name:               pod.Name(),
 | |
| 			Status:             status,
 | |
| 			Cgroup:             pod.CgroupParent(),
 | |
| 			NumberOfContainers: ctrNum,
 | |
| 			CtrsInfo:           ctrsInfo,
 | |
| 			Namespaces:         getNamespaces(pod),
 | |
| 			InfraID:            infraID,
 | |
| 		}
 | |
| 
 | |
| 		psOutput = append(psOutput, params)
 | |
| 	}
 | |
| 	return sortPodPsOutput(opts.Sort, psOutput)
 | |
| }
 | |
| 
 | |
| func generatePodPsOutput(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) error {
 | |
| 	if len(pods) == 0 && opts.Format != formats.JSONString {
 | |
| 		return nil
 | |
| 	}
 | |
| 	psOutput, err := getAndSortPodJSONParams(pods, opts, runtime)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	var out formats.Writer
 | |
| 
 | |
| 	switch opts.Format {
 | |
| 	case formats.JSONString:
 | |
| 		if err != nil {
 | |
| 			return errors.Wrapf(err, "unable to create JSON for output")
 | |
| 		}
 | |
| 		out = formats.JSONStructArray{Output: podPsToGeneric([]podPsTemplateParams{}, psOutput)}
 | |
| 	default:
 | |
| 		psOutput, err := getPodTemplateOutput(psOutput, opts)
 | |
| 		if err != nil {
 | |
| 			return errors.Wrapf(err, "unable to create output")
 | |
| 		}
 | |
| 		out = formats.StdoutTemplateArray{Output: podPsToGeneric(psOutput, []podPsJSONParams{}), Template: opts.Format, Fields: psOutput[0].podHeaderMap()}
 | |
| 	}
 | |
| 
 | |
| 	return formats.Writer(out).Out()
 | |
| }
 |