diff options
Diffstat (limited to 'cmd/kpod/top.go')
-rw-r--r-- | cmd/kpod/top.go | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/cmd/kpod/top.go b/cmd/kpod/top.go new file mode 100644 index 000000000..9c2fcd34e --- /dev/null +++ b/cmd/kpod/top.go @@ -0,0 +1,258 @@ +package main + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/projectatomic/libpod/cmd/kpod/formats" + "github.com/projectatomic/libpod/libpod" + "github.com/urfave/cli" +) + +var ( + topFlags = []cli.Flag{ + cli.StringFlag{ + Name: "format", + Usage: "Change the output to JSON", + }, + } + topDescription = ` + kpod top + + Display the running processes of the container. +` + + topCommand = cli.Command{ + Name: "top", + Usage: "Display the running processes of a container", + Description: topDescription, + Flags: topFlags, + Action: topCmd, + ArgsUsage: "CONTAINER-NAME", + SkipArgReorder: true, + } +) + +func topCmd(c *cli.Context) error { + doJSON := false + if c.IsSet("format") { + if strings.ToUpper(c.String("format")) == "JSON" { + doJSON = true + } else { + return errors.Errorf("only 'json' is supported for a format option") + } + } + args := c.Args() + var psArgs []string + psOpts := []string{"-o", "uid,pid,ppid,c,stime,tname,time,cmd"} + if len(args) < 1 { + return errors.Errorf("you must provide the name or id of a running container") + } + if err := validateFlags(c, topFlags); err != nil { + return err + } + + runtime, err := getRuntime(c) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + defer runtime.Shutdown(false) + if len(args) > 1 { + psOpts = args[1:] + } + + container, err := runtime.LookupContainer(args[0]) + if err != nil { + return errors.Wrapf(err, "unable to lookup %s", args[0]) + } + conStat, err := container.State() + if err != nil { + return errors.Wrapf(err, "unable to look up state for %s", args[0]) + } + if conStat != libpod.ContainerStateRunning { + return errors.Errorf("top can only be used on running containers") + } + + psArgs = append(psArgs, psOpts...) + + results, err := container.GetContainerPidInformation(psArgs) + if err != nil { + return err + } + headers := getHeaders(results[0]) + format := genTopFormat(headers) + var out formats.Writer + psParams, err := psDataToPSParams(results[1:], headers) + if err != nil { + return errors.Wrap(err, "unable to convert ps data to proper structure") + } + if doJSON { + out = formats.JSONStructArray{Output: topToGeneric(psParams)} + } else { + out = formats.StdoutTemplateArray{Output: topToGeneric(psParams), Template: format, Fields: createTopHeaderMap(headers)} + } + formats.Writer(out).Out() + return nil +} + +func getHeaders(s string) []string { + var headers []string + tmpHeaders := strings.Fields(s) + for _, header := range tmpHeaders { + headers = append(headers, strings.Replace(header, "%", "", -1)) + } + return headers +} + +func genTopFormat(headers []string) string { + format := "table " + for _, header := range headers { + format = fmt.Sprintf("%s{{.%s}}\t", format, header) + } + return format +} + +// imagesToGeneric creates an empty array of interfaces for output +func topToGeneric(templParams []PSParams) (genericParams []interface{}) { + for _, v := range templParams { + genericParams = append(genericParams, interface{}(v)) + } + return +} + +// generate the header based on the template provided +func createTopHeaderMap(v []string) map[string]string { + values := make(map[string]string) + for _, key := range v { + value := key + if value == "CPU" { + value = "%CPU" + } else if value == "MEM" { + value = "%MEM" + } + values[key] = strings.ToUpper(splitCamelCase(value)) + } + return values +} + +// PSDataToParams converts a string array of data and its headers to an +// arra if PSParams +func psDataToPSParams(data []string, headers []string) ([]PSParams, error) { + var params []PSParams + for _, line := range data { + tmpMap := make(map[string]string) + tmpArray := strings.Fields(line) + if len(tmpArray) == 0 { + continue + } + for index, v := range tmpArray { + header := headers[index] + tmpMap[header] = v + } + jsonData, _ := json.Marshal(tmpMap) + var r PSParams + err := json.Unmarshal(jsonData, &r) + if err != nil { + return []PSParams{}, err + } + params = append(params, r) + } + return params, nil +} + +//PSParams is a list of options that the command line ps recognizes +type PSParams struct { + CPU string `json: "%CPU"` + MEM string `json: "%MEM"` + COMMAND string + BLOCKED string + START string + TIME string + C string + CAUGHT string + CGROUP string + CLSCLS string + CLS string + CMD string + CP string + DRS string + EGID string + EGROUP string + EIP string + ESP string + ELAPSED string + EUIDE string + USER string + F string + FGID string + FGROUP string + FUID string + FUSER string + GID string + GROUP string + IGNORED string + IPCNS string + LABEL string + STARTED string + SESSION string + LWP string + MACHINE string + MAJFLT string + MINFLT string + MNTNS string + NETNS string + NI string + NLWP string + OWNER string + PENDING string + PGID string + PGRP string + PID string + PIDNS string + POL string + PPID string + PRI string + PSR string + RGID string + RGROUP string + RSS string + RSZ string + RTPRIO string + RUID string + RUSER string + S string + SCH string + SEAT string + SESS string + P string + SGID string + SGROUP string + SID string + SIZE string + SLICE string + SPID string + STACKP string + STIME string + SUID string + SUPGID string + SUPGRP string + SUSER string + SVGID string + SZ string + TGID string + THCNT string + TID string + TTY string + TPGID string + TRS string + TT string + UID string + UNIT string + USERNS string + UTSNS string + UUNIT string + VSZ string + WCHAN string +} |