Merge pull request #871 from umohnani8/kpod_ps

Add namespace flag to kpod ps
This commit is contained in:
Daniel J Walsh 2017-09-11 10:14:39 -04:00 committed by GitHub
commit a3a0bb5e55
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"
@ -30,6 +32,7 @@ type psOptions struct {
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,7 +159,7 @@ 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")
} }
@ -146,6 +173,7 @@ func psCmd(c *cli.Context) error {
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