Add namespace flag to kpod ps

The namespace flag prints out information about the namespaces

Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
umohnani8 2017-09-08 15:50:33 -04:00
parent 36584e6f34
commit 38e9f07844
4 changed files with 140 additions and 26 deletions

View file

@ -1,6 +1,8 @@
package main package main
import ( import (
"os"
"path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
@ -21,15 +23,16 @@ import (
) )
type psOptions struct { type psOptions struct {
all bool all bool
filter string filter string
format string format string
last int last int
latest bool latest bool
noTrunc bool noTrunc bool
quiet bool quiet bool
size bool size bool
label string label string
namespace bool
} }
type psTemplateParams struct { type psTemplateParams struct {
@ -44,6 +47,14 @@ type psTemplateParams struct {
Names string Names string
Labels string Labels string
Mounts string Mounts string
PID int
Cgroup string
IPC string
MNT string
NET string
PIDNS string
User string
UTS string
} }
// psJSONParams is only used when the JSON format is specified, // psJSONParams is only used when the JSON format is specified,
@ -64,6 +75,18 @@ type psJSONParams struct {
Labels fields.Set `json:"labels"` Labels fields.Set `json:"labels"`
Mounts []specs.Mount `json:"mounts"` Mounts []specs.Mount `json:"mounts"`
ContainerRunning bool `json:"ctrRunning"` ContainerRunning bool `json:"ctrRunning"`
Namespaces namespace `json:"namespace,omitempty"`
}
type namespace struct {
PID string `json:"ctrPID,omitempty"`
Cgroup string `json:"cgroup,omitempty"`
IPC string `json:"ipc,omitempty"`
MNT string `json:"mnt,omitempty"`
NET string `json:"net,omitempty"`
PIDNS string `json:"pid,omitempty"`
User string `json:"user,omitempty"`
UTS string `json:"uts,omitempty"`
} }
const runningState = "running" const runningState = "running"
@ -103,6 +126,10 @@ var (
Name: "size, s", Name: "size, s",
Usage: "Display the total file sizes", Usage: "Display the total file sizes",
}, },
cli.BoolFlag{
Name: "namespace, ns",
Usage: "Display namespace information",
},
} }
psDescription = "Prints out information about the containers" psDescription = "Prints out information about the containers"
psCommand = cli.Command{ psCommand = cli.Command{
@ -132,20 +159,21 @@ func psCmd(c *cli.Context) error {
return errors.Errorf("too many arguments, ps takes no arguments") return errors.Errorf("too many arguments, ps takes no arguments")
} }
format := genPsFormat(c.Bool("quiet"), c.Bool("size")) format := genPsFormat(c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"))
if c.IsSet("format") { if c.IsSet("format") {
format = c.String("format") format = c.String("format")
} }
opts := psOptions{ opts := psOptions{
all: c.Bool("all"), all: c.Bool("all"),
filter: c.String("filter"), filter: c.String("filter"),
format: format, format: format,
last: c.Int("last"), last: c.Int("last"),
latest: c.Bool("latest"), latest: c.Bool("latest"),
noTrunc: c.Bool("no-trunc"), noTrunc: c.Bool("no-trunc"),
quiet: c.Bool("quiet"), quiet: c.Bool("quiet"),
size: c.Bool("size"), size: c.Bool("size"),
namespace: c.Bool("namespace"),
} }
// all, latest, and last are mutually exclusive. Only one flag can be used at a time // all, latest, and last are mutually exclusive. Only one flag can be used at a time
@ -183,10 +211,14 @@ func psCmd(c *cli.Context) error {
} }
// generate the template based on conditions given // generate the template based on conditions given
func genPsFormat(quiet, size bool) (format string) { func genPsFormat(quiet, size, namespace bool) (format string) {
if quiet { if quiet {
return formats.IDString return formats.IDString
} }
if namespace {
format = "table {{.ID}}\t{{.Names}}\t{{.PID}}\t{{.Cgroup}}\t{{.IPC}}\t{{.MNT}}\t{{.NET}}\t{{.PIDNS}}\t{{.User}}\t{{.UTS}}\t"
return
}
format = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.CreatedAt}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}\t" format = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.CreatedAt}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}\t"
if size { if size {
format += "{{.Size}}\t" format += "{{.Size}}\t"
@ -253,13 +285,15 @@ func getTemplateOutput(containers []*libkpod.ContainerData, opts psOptions) (psO
ctrID := ctr.ID ctrID := ctr.ID
runningFor := units.HumanDuration(time.Since(ctr.State.Created)) runningFor := units.HumanDuration(time.Since(ctr.State.Created))
createdAt := runningFor + " ago" createdAt := runningFor + " ago"
command := getCommand(ctr.ImageCreatedBy) command := getStrFromSquareBrackets(ctr.ImageCreatedBy)
imageName := ctr.FromImage imageName := ctr.FromImage
mounts := getMounts(ctr.Mounts, opts.noTrunc) mounts := getMounts(ctr.Mounts, opts.noTrunc)
ports := getPorts(ctr.Config.ExposedPorts) ports := getPorts(ctr.Config.ExposedPorts)
size := units.HumanSize(float64(ctr.SizeRootFs)) size := units.HumanSize(float64(ctr.SizeRootFs))
labels := getLabels(ctr.Labels) labels := getLabels(ctr.Labels)
ns := getNamespaces(ctr.State.Pid)
switch ctr.State.Status { switch ctr.State.Status {
case "stopped": case "stopped":
status = "Exited (" + strconv.FormatInt(int64(ctr.State.ExitCode), 10) + ") " + runningFor + " ago" status = "Exited (" + strconv.FormatInt(int64(ctr.State.ExitCode), 10) + ") " + runningFor + " ago"
@ -286,20 +320,63 @@ func getTemplateOutput(containers []*libkpod.ContainerData, opts psOptions) (psO
Names: ctr.Name, Names: ctr.Name,
Labels: labels, Labels: labels,
Mounts: mounts, Mounts: mounts,
PID: ctr.State.Pid,
Cgroup: ns.Cgroup,
IPC: ns.IPC,
MNT: ns.MNT,
NET: ns.NET,
PIDNS: ns.PID,
User: ns.User,
UTS: ns.UTS,
} }
psOutput = append(psOutput, params) psOutput = append(psOutput, params)
} }
return return
} }
func getNamespaces(pid int) namespace {
ctrPID := strconv.Itoa(pid)
cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
return namespace{
PID: ctrPID,
Cgroup: cgroup,
IPC: ipc,
MNT: mnt,
NET: net,
PIDNS: pidns,
User: user,
UTS: uts,
}
}
func getNamespaceInfo(path string) (string, error) {
val, err := os.Readlink(path)
if err != nil {
return "", errors.Wrapf(err, "error getting info from %q", path)
}
return getStrFromSquareBrackets(val), nil
}
// getJSONOutput returns the container info in its raw form // getJSONOutput returns the container info in its raw form
func getJSONOutput(containers []*libkpod.ContainerData) (psOutput []psJSONParams) { func getJSONOutput(containers []*libkpod.ContainerData, nSpace bool) (psOutput []psJSONParams) {
var ns namespace
for _, ctr := range containers { for _, ctr := range containers {
if nSpace {
ns = getNamespaces(ctr.State.Pid)
}
params := psJSONParams{ params := psJSONParams{
ID: ctr.ID, ID: ctr.ID,
Image: ctr.FromImage, Image: ctr.FromImage,
ImageID: ctr.FromImageID, ImageID: ctr.FromImageID,
Command: getCommand(ctr.ImageCreatedBy), Command: getStrFromSquareBrackets(ctr.ImageCreatedBy),
CreatedAt: ctr.State.Created, CreatedAt: ctr.State.Created,
RunningFor: time.Since(ctr.State.Created), RunningFor: time.Since(ctr.State.Created),
Status: ctr.State.Status, Status: ctr.State.Status,
@ -309,6 +386,7 @@ func getJSONOutput(containers []*libkpod.ContainerData) (psOutput []psJSONParams
Labels: ctr.Labels, Labels: ctr.Labels,
Mounts: ctr.Mounts, Mounts: ctr.Mounts,
ContainerRunning: ctr.State.Status == runningState, ContainerRunning: ctr.State.Status == runningState,
Namespaces: ns,
} }
psOutput = append(psOutput, params) psOutput = append(psOutput, params)
} }
@ -325,7 +403,7 @@ func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.Conta
switch opts.format { switch opts.format {
case formats.JSONString: case formats.JSONString:
psOutput := getJSONOutput(containersOutput) psOutput := getJSONOutput(containersOutput, opts.namespace)
out = formats.JSONStructArray{Output: psToGeneric([]psTemplateParams{}, psOutput)} out = formats.JSONStructArray{Output: psToGeneric([]psTemplateParams{}, psOutput)}
default: default:
psOutput := getTemplateOutput(containersOutput, opts) psOutput := getTemplateOutput(containersOutput, opts)
@ -335,8 +413,8 @@ func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.Conta
return formats.Writer(out).Out() return formats.Writer(out).Out()
} }
// getCommand gets the actual command from the whole command // getStrFromSquareBrackets gets the string inside [] from a string
func getCommand(cmd string) string { func getStrFromSquareBrackets(cmd string) string {
reg, err := regexp.Compile(".*\\[|\\].*") reg, err := regexp.Compile(".*\\[|\\].*")
if err != nil { if err != nil {
return "" return ""

View file

@ -361,6 +361,7 @@ _kpod_ps() {
--no-trunc --no-trunc
--quiet -q --quiet -q
--size -s --size -s
--namespace --ns
" "
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
} }

View file

@ -69,6 +69,9 @@ Valid placeholders for the Go template are listed below:
**--latest, -l** **--latest, -l**
show the latest container created (all states) show the latest container created (all states)
**--namespace, --ns**
Display namespace information
**--filter, -f** **--filter, -f**
Filter output based on conditions given Filter output based on conditions given
@ -108,6 +111,14 @@ sudo kpod ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
69ed779d8ef9f redis:alpine batch=no,type=small proc,tmpfs,devpts,shm,mqueue,sysfs,cgroup,/var/run/,/var/run/ 69ed779d8ef9f redis:alpine batch=no,type=small proc,tmpfs,devpts,shm,mqueue,sysfs,cgroup,/var/run/,/var/run/
``` ```
```
sudo kpod ps --ns -a
CONTAINER ID NAMES PID CGROUP IPC MNT NET PIDNS USER UTS
3557d882a82e3 k8s_container2_podsandbox1_redhat.test.crio_redhat-test-crio_1 29910 4026531835 4026532585 4026532593 4026532508 4026532595 4026531837 4026532594
09564cdae0bec k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1 29851 4026531835 4026532585 4026532590 4026532508 4026532592 4026531837 4026532591
a31ebbee9cee7 k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0 29717 4026531835 4026532585 4026532587 4026532508 4026532589 4026531837 4026532588
```
## ps ## ps
Print a list of containers Print a list of containers

View file

@ -179,6 +179,30 @@ KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT ${STORAGE_OPTS}"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "kpod ps namespace flag" {
start_crio
[ "$status" -eq 0 ]
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
echo "$output"
[ "$status" -eq 0 ]
pod_id="$output"
run crioctl image pull "$IMAGE"
[ "$status" -eq 0 ]
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
echo "$output"
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} ps -a --ns
echo "$output"
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} ps --all --namespace
echo "$output"
[ "$status" -eq 0 ]
cleanup_ctrs
cleanup_pods
stop_crio
[ "$status" -eq 0 ]
}
@test "kpod ps format flag = json" { @test "kpod ps format flag = json" {
start_crio start_crio
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
@ -191,7 +215,7 @@ KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT ${STORAGE_OPTS}"
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
echo "$output" echo "$output"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} ps -a --format json | python -m json.tool" run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} ps -a --ns --format json | python -m json.tool"
echo "$output" echo "$output"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
cleanup_ctrs cleanup_ctrs