aboutsummaryrefslogtreecommitdiff
path: root/cmd/podman/top.go
blob: 5b1dbd05c771fc883179310cf0e042d809fc260d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/pkg/errors"
	"github.com/projectatomic/libpod/cmd/podman/formats"
	"github.com/projectatomic/libpod/libpod"
	"github.com/urfave/cli"
)

var (
	topFlags = []cli.Flag{
		cli.StringFlag{
			Name:  "format",
			Usage: "Change the output to JSON",
		}, LatestFlag,
	}
	topDescription = `
   podman 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 {
	var container *libpod.Container
	var err 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 && !c.Bool("latest") {
		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:]
	}

	if c.Bool("latest") {
		container, err = runtime.GetLatestContainer()
	} else {
		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
	MEM     string
	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
}