Skip to content

Commit

Permalink
Merge pull request #1 from makocchi-git/20190701
Browse files Browse the repository at this point in the history
Enable printing usage with namespace scope
  • Loading branch information
makocchi-git authored Jul 8, 2019
2 parents ed417da + 87f44db commit f935bf1
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 6 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ $ kubectl free
## Usage

```shell
# Show pod resource usage of Kubernetes nodes.
# Show pod resource usage of Kubernetes nodes (default namespace is "default").
kubectl free

# Show pod resource usage of Kubernetes nodes (all namespaces).
kubectl free --all-namespaces

# Show pod resource usage of Kubernetes nodes with number of pods and containers.
kubectl free --pod

Expand All @@ -71,6 +74,11 @@ kubectl free --emoji
kubectl free --list --emoji
```

## Notice

This plugin shows just sum of requested(limited) resources, **not a real usage**.
I recommend to use `kubectl free` with `kubectl top`.

## License

This software is released under the MIT License.
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ module github.com/makocchi-git/kubectl-free
go 1.12

require (
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
github.com/docker/docker v0.0.0-00010101000000-000000000000 // indirect
github.com/gookit/color v1.1.7
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/russross/blackfriday v0.0.0-00010101000000-000000000000 // indirect
github.com/spf13/cobra v0.0.2
github.com/spf13/pflag v1.0.1
k8s.io/api v0.0.0-20190531132109-d3f5f50bdd94
Expand Down
21 changes: 19 additions & 2 deletions pkg/cmd/free.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ var (

// DfExample defines command examples
freeExample = templates.Examples(`
# Show pod resource usage of Kubernetes nodes.
# Show pod resource usage of Kubernetes nodes (default namespace is "default").
kubectl free
# Show pod resource usage of Kubernetes nodes (all namespaces).
kubectl free --all-namespaces
# Show pod resource usage of Kubernetes nodes with number of pods and containers.
kubectl free --pod
Expand Down Expand Up @@ -70,6 +73,7 @@ type FreeOptions struct {
header string
pod bool
emojiStatus bool
allNamespaces bool

// unit options
bytes bool
Expand Down Expand Up @@ -120,6 +124,7 @@ func NewFreeOptions(streams genericclioptions.IOStreams) *FreeOptions {
emojiStatus: false,
table: table.NewOutputTable(os.Stdout),
header: "default",
allNamespaces: false,
}
}

Expand Down Expand Up @@ -165,6 +170,7 @@ func NewCmdFree(streams genericclioptions.IOStreams, version, commit, date strin
cmd.Flags().BoolVarP(&o.listContainerImage, "list-image", "", o.listContainerImage, `Show pod list on node with container image.`)
cmd.Flags().BoolVarP(&o.listAll, "list-all", "", o.listAll, `Show pods even if they have no requests/limit`)
cmd.Flags().BoolVarP(&o.emojiStatus, "emoji", "", o.emojiStatus, `Let's smile!! 😃 😭`)
cmd.Flags().BoolVarP(&o.allNamespaces, "all-namespaces", "", o.allNamespaces, `If present, list pod resources(limits) across all namespaces. Namespace in current context is ignored even if specified with --namespace.`)

// int64 options
cmd.Flags().Int64VarP(&o.warnThreshold, "warn-threshold", "", o.warnThreshold, `Threshold of warn(yellow) color for USED column.`)
Expand Down Expand Up @@ -199,7 +205,18 @@ func (o *FreeOptions) Prepare() error {
o.nodeClient = client.CoreV1().Nodes()

// pod client
o.podClient = client.CoreV1().Pods(*o.configFlags.Namespace)
if o.allNamespaces {
// --all-namespace flag
o.podClient = client.CoreV1().Pods(v1.NamespaceAll)
} else {
if *o.configFlags.Namespace == "" {
// default namespace is "default"
o.podClient = client.CoreV1().Pods(v1.NamespaceDefault)
} else {
// targeted namespace (--namespace flag)
o.podClient = client.CoreV1().Pods(*o.configFlags.Namespace)
}
}

// prepare table header
o.prepareFreeTableHeader()
Expand Down
112 changes: 109 additions & 3 deletions pkg/cmd/free_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var testPods = []v1.Pod{
Phase: v1.PodRunning,
},
Spec: v1.PodSpec{
NodeName: "node2",
NodeName: "node1",
Containers: []v1.Container{
{
Name: "container2a",
Expand All @@ -102,6 +102,39 @@ var testPods = []v1.Pod{
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod3",
Namespace: "awesome-ns",
},
Status: v1.PodStatus{
PodIP: "2.3.4.5",
Phase: v1.PodRunning,
},
Spec: v1.PodSpec{
NodeName: "node1",
Containers: []v1.Container{
{
Name: "container3a",
Image: "centos:7",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(300, resource.DecimalSI),
},
Requests: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(300, resource.DecimalSI),
},
},
},
{
Name: "container3b",
Image: "ubuntu:bionic",
},
},
},
},
}

func TestNewFreeOptions(t *testing.T) {
Expand Down Expand Up @@ -260,6 +293,32 @@ func TestPrepare(t *testing.T) {
return
}
})

t.Run("prepare allnamespace", func(t *testing.T) {

o := &FreeOptions{
configFlags: genericclioptions.NewConfigFlags(true),
allNamespaces: true,
}

if err := o.Prepare(); err != nil {
t.Errorf("unexpected error: %v", err)
return
}
})

t.Run("prepare specific namespace", func(t *testing.T) {

o := &FreeOptions{
configFlags: genericclioptions.NewConfigFlags(true),
}
*o.configFlags.Namespace = "awesome-ns"

if err := o.Prepare(); err != nil {
t.Errorf("unexpected error: %v", err)
return
}
})
}

func TestValidate(t *testing.T) {
Expand Down Expand Up @@ -658,12 +717,14 @@ func TestShowFree(t *testing.T) {
var tests = []struct {
description string
pod bool
namespace string
expected []string
expectedErr error
}{
{
"default free",
false,
"default",
[]string{
"node1 NotReady 1 4 25% 1K 4K 25%",
"",
Expand All @@ -673,19 +734,30 @@ func TestShowFree(t *testing.T) {
{
"default free --pod",
true,
"default",
[]string{
"node1 NotReady 1 4 25% 1K 4K 25% 1 110 1",
"",
},
nil,
},
{
"awesome-ns free",
true,
"awesome-ns",
[]string{
"node1 NotReady 200m 4 5% 0K 4K 7% 1 110 2",
"",
},
nil,
},
}

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {

fakeNodeClient := fake.NewSimpleClientset(&testNodes[0])
fakePodClient := fake.NewSimpleClientset(&testPods[0])
fakePodClient := fake.NewSimpleClientset(&testPods[0], &testPods[2])

buffer := &bytes.Buffer{}
o := &FreeOptions{
Expand All @@ -694,7 +766,7 @@ func TestShowFree(t *testing.T) {
list: false,
pod: test.pod,
nodeClient: fakeNodeClient.CoreV1().Nodes(),
podClient: fakePodClient.CoreV1().Pods(""),
podClient: fakePodClient.CoreV1().Pods(test.namespace),
header: "none",
}

Expand All @@ -711,6 +783,40 @@ func TestShowFree(t *testing.T) {

})
}

t.Run("Allnamespace", func(t *testing.T) {

fakeNodeClient := fake.NewSimpleClientset(&testNodes[0])
fakePodClient := fake.NewSimpleClientset(&testPods[0], &testPods[1], &testPods[2])

buffer := &bytes.Buffer{}
o := &FreeOptions{
nocolor: true,
table: table.NewOutputTable(buffer),
list: false,
pod: false,
nodeClient: fakeNodeClient.CoreV1().Nodes(),
podClient: fakePodClient.CoreV1().Pods(""),
allNamespaces: true,
header: "none",
}

if err := o.showFree([]v1.Node{testNodes[0]}); err != nil {
t.Errorf("unexpected error: %v", err)
return
}

expected := []string{
"node1 NotReady 1700m 4 42% 2K 4K 57%",
"",
}
e := strings.Join(expected, "\n")
if buffer.String() != e {
t.Errorf("expected(%s) differ (got: %s)", e, buffer.String())
return
}

})
}

func TestListPodsOnNode(t *testing.T) {
Expand Down

0 comments on commit f935bf1

Please sign in to comment.