Skip to content

Commit

Permalink
adding ability to display usage in terms of total not percentage (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
Padarn authored Dec 30, 2020
1 parent 59f74a6 commit 957ca86
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 55 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ example-node-1 220m (22%) 10m (1%) 10m (1%) 192Mi (6%) 3
example-node-2 340m (34%) 120m (12%) 30m (3%) 380Mi (13%) 410Mi (14%) 260Mi (9%)
```

### Displaying Available Resources
To more clearly see the total available resources on the node it is possible to pass the `--available` option
to kube-capacity, which will give output in the following format

```
kube-capacity --available
NODE CPU REQUESTS CPU LIMITS MEMORY REQUESTS MEMORY LIMITS
* 560/2000m 130/2000m 572/5923Mi 770/5923Mi
example-node-1 220/1000m 10/1000m 192/3200Mi 360/3200Mi
example-node-2 340/1000m 120/1000m 380/2923Mi 410/2923Mi
```

### Including Pods and Utilization
For more detailed output, kube-capacity can include both pods and resource utilization in the output. When `--util` and `--pods` are passed to kube-capacity, it will result in a wide output that looks like this:

Expand Down Expand Up @@ -120,6 +133,7 @@ kube-capacity --pods --containers --util --output yaml
-o, --output string output format for information
(supports: [table json yaml])
(default "table")
-a, --available includes quantity available instead of percentage used
-l, --pod-labels string labels to filter pods with
-p, --pods includes pods in output
--sort string attribute to sort results be (supports:
Expand All @@ -143,6 +157,7 @@ Although this project was originally developed by [robscott](https://github.com/

- [endzyme](https://github.com/endzyme)
- [justinbarrick](https://github.com/justinbarrick)
- [Padarn](https://github.com/Padarn)

## License
Apache License 2.0
4 changes: 2 additions & 2 deletions pkg/capacity/capacity.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
)

// FetchAndPrint gathers cluster resource data and outputs it
func FetchAndPrint(showContainers, showPods, showUtil bool, podLabels, nodeLabels, namespaceLabels, kubeContext, output, sortBy string) {
func FetchAndPrint(showContainers, showPods, showUtil, availableFormat bool, podLabels, nodeLabels, namespaceLabels, kubeContext, output, sortBy string) {
clientset, err := kube.NewClientSet(kubeContext)
if err != nil {
fmt.Printf("Error connecting to Kubernetes: %v\n", err)
Expand All @@ -51,7 +51,7 @@ func FetchAndPrint(showContainers, showPods, showUtil bool, podLabels, nodeLabel
}

cm := buildClusterMetric(podList, pmList, nodeList, nmList)
printList(&cm, showContainers, showPods, showUtil, output, sortBy)
printList(&cm, showContainers, showPods, showUtil, output, sortBy, availableFormat)
}

func getPodsAndNodes(clientset kubernetes.Interface, podLabels, nodeLabels, namespaceLabels string) (*corev1.PodList, *corev1.NodeList) {
Expand Down
15 changes: 8 additions & 7 deletions pkg/capacity/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func SupportedOutputs() []string {
}
}

func printList(cm *clusterMetric, showContainers, showPods, showUtil bool, output, sortBy string) {
func printList(cm *clusterMetric, showContainers, showPods, showUtil bool, output, sortBy string, availableFormat bool) {
if output == JSONOutput || output == YAMLOutput {
lp := &listPrinter{
cm: cm,
Expand All @@ -50,12 +50,13 @@ func printList(cm *clusterMetric, showContainers, showPods, showUtil bool, outpu
lp.Print(output)
} else if output == TableOutput {
tp := &tablePrinter{
cm: cm,
showPods: showPods,
showUtil: showUtil,
showContainers: showContainers,
sortBy: sortBy,
w: new(tabwriter.Writer),
cm: cm,
showPods: showPods,
showUtil: showUtil,
showContainers: showContainers,
sortBy: sortBy,
w: new(tabwriter.Writer),
availableFormat: availableFormat,
}
tp.Print()
} else {
Expand Down
33 changes: 18 additions & 15 deletions pkg/capacity/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,41 +294,44 @@ func (pm *podMetric) getSortedContainerMetrics(sortBy string) []*containerMetric
return sortedContainerMetrics
}

func (rm *resourceMetric) requestString() string {
return resourceString(rm.request, rm.allocatable, rm.resourceType)
func (rm *resourceMetric) requestString(availableFormat bool) string {
return resourceString(rm.request, rm.allocatable, availableFormat)
}

func (rm *resourceMetric) limitString() string {
return resourceString(rm.limit, rm.allocatable, rm.resourceType)
func (rm *resourceMetric) limitString(availableFormat bool) string {
return resourceString(rm.limit, rm.allocatable, availableFormat)
}

func (rm *resourceMetric) utilString() string {
return resourceString(rm.utilization, rm.allocatable, rm.resourceType)
func (rm *resourceMetric) utilString(availableFormat bool) string {
return resourceString(rm.utilization, rm.allocatable, availableFormat)
}

func resourceString(actual, allocatable resource.Quantity, resourceType string) string {
utilPercent := float64(0)
func resourceString(actual, allocatable resource.Quantity, availableFormat bool) string {
utilPercent := int64(0)
if allocatable.MilliValue() > 0 {
utilPercent = float64(actual.MilliValue()) / float64(allocatable.MilliValue()) * 100
utilPercent = int64(float64(actual.MilliValue()) / float64(allocatable.MilliValue()) * 100)
}

if resourceType == "cpu" {
return fmt.Sprintf("%dm (%d", actual.MilliValue(), int64(utilPercent)) + "%%)"
if availableFormat {
// performs rounding on BinarySI to make sure units are the same
if allocatable.Format == resource.BinarySI {
allocatable.SetMilli(actual.MilliValue() * utilPercent)
}
return fmt.Sprintf("%s/%s", actual.String(), allocatable.String())
}
return fmt.Sprintf("%s (%d%%%%)", actual.String(), utilPercent)

return fmt.Sprintf("%dMi (%d", actual.Value()/1048576, int64(utilPercent)) + "%%)"
}

// NOTE: This might not be a great place for closures due to the cyclical nature of how resourceType works. Perhaps better implemented another way.
func (rm resourceMetric) valueFunction() (f func(r resource.Quantity) string) {
switch rm.resourceType {
case "cpu":
f = func(r resource.Quantity) string {
return fmt.Sprintf("%dm", r.MilliValue())
return fmt.Sprintf("%s", r.String())
}
case "memory":
f = func(r resource.Quantity) string {
return fmt.Sprintf("%dMi", r.Value()/1048576)
return fmt.Sprintf("%s", r.String())
}
}
return f
Expand Down
61 changes: 31 additions & 30 deletions pkg/capacity/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import (
)

type tablePrinter struct {
cm *clusterMetric
showPods bool
showUtil bool
showContainers bool
sortBy string
w *tabwriter.Writer
cm *clusterMetric
showPods bool
showUtil bool
showContainers bool
sortBy string
w *tabwriter.Writer
availableFormat bool
}

type tableLine struct {
Expand Down Expand Up @@ -130,12 +131,12 @@ func (tp *tablePrinter) printClusterLine() {
namespace: "*",
pod: "*",
container: "*",
cpuRequests: tp.cm.cpu.requestString(),
cpuLimits: tp.cm.cpu.limitString(),
cpuUtil: tp.cm.cpu.utilString(),
memoryRequests: tp.cm.memory.requestString(),
memoryLimits: tp.cm.memory.limitString(),
memoryUtil: tp.cm.memory.utilString(),
cpuRequests: tp.cm.cpu.requestString(tp.availableFormat),
cpuLimits: tp.cm.cpu.limitString(tp.availableFormat),
cpuUtil: tp.cm.cpu.utilString(tp.availableFormat),
memoryRequests: tp.cm.memory.requestString(tp.availableFormat),
memoryLimits: tp.cm.memory.limitString(tp.availableFormat),
memoryUtil: tp.cm.memory.utilString(tp.availableFormat),
})
}

Expand All @@ -145,12 +146,12 @@ func (tp *tablePrinter) printNodeLine(nodeName string, nm *nodeMetric) {
namespace: "*",
pod: "*",
container: "*",
cpuRequests: nm.cpu.requestString(),
cpuLimits: nm.cpu.limitString(),
cpuUtil: nm.cpu.utilString(),
memoryRequests: nm.memory.requestString(),
memoryLimits: nm.memory.limitString(),
memoryUtil: nm.memory.utilString(),
cpuRequests: nm.cpu.requestString(tp.availableFormat),
cpuLimits: nm.cpu.limitString(tp.availableFormat),
cpuUtil: nm.cpu.utilString(tp.availableFormat),
memoryRequests: nm.memory.requestString(tp.availableFormat),
memoryLimits: nm.memory.limitString(tp.availableFormat),
memoryUtil: nm.memory.utilString(tp.availableFormat),
})
}

Expand All @@ -160,12 +161,12 @@ func (tp *tablePrinter) printPodLine(nodeName string, pm *podMetric) {
namespace: pm.namespace,
pod: pm.name,
container: "*",
cpuRequests: pm.cpu.requestString(),
cpuLimits: pm.cpu.limitString(),
cpuUtil: pm.cpu.utilString(),
memoryRequests: pm.memory.requestString(),
memoryLimits: pm.memory.limitString(),
memoryUtil: pm.memory.utilString(),
cpuRequests: pm.cpu.requestString(tp.availableFormat),
cpuLimits: pm.cpu.limitString(tp.availableFormat),
cpuUtil: pm.cpu.utilString(tp.availableFormat),
memoryRequests: pm.memory.requestString(tp.availableFormat),
memoryLimits: pm.memory.limitString(tp.availableFormat),
memoryUtil: pm.memory.utilString(tp.availableFormat),
})
}

Expand All @@ -175,11 +176,11 @@ func (tp *tablePrinter) printContainerLine(nodeName string, pm *podMetric, cm *c
namespace: pm.namespace,
pod: pm.name,
container: cm.name,
cpuRequests: cm.cpu.requestString(),
cpuLimits: cm.cpu.limitString(),
cpuUtil: cm.cpu.utilString(),
memoryRequests: cm.memory.requestString(),
memoryLimits: cm.memory.limitString(),
memoryUtil: cm.memory.utilString(),
cpuRequests: cm.cpu.requestString(tp.availableFormat),
cpuLimits: cm.cpu.limitString(tp.availableFormat),
cpuUtil: cm.cpu.utilString(tp.availableFormat),
memoryRequests: cm.memory.requestString(tp.availableFormat),
memoryLimits: cm.memory.limitString(tp.availableFormat),
memoryUtil: cm.memory.utilString(tp.availableFormat),
})
}
5 changes: 4 additions & 1 deletion pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var namespaceLabels string
var kubeContext string
var outputFormat string
var sortBy string
var availableFormat bool

var rootCmd = &cobra.Command{
Use: "kube-capacity",
Expand All @@ -46,7 +47,7 @@ var rootCmd = &cobra.Command{
os.Exit(1)
}

capacity.FetchAndPrint(showContainers, showPods, showUtil, podLabels, nodeLabels, namespaceLabels, kubeContext, outputFormat, sortBy)
capacity.FetchAndPrint(showContainers, showPods, showUtil, availableFormat, podLabels, nodeLabels, namespaceLabels, kubeContext, outputFormat, sortBy)
},
}

Expand All @@ -57,6 +58,8 @@ func init() {
"pods", "p", false, "includes pods in output")
rootCmd.PersistentFlags().BoolVarP(&showUtil,
"util", "u", false, "includes resource utilization in output")
rootCmd.PersistentFlags().BoolVarP(&availableFormat,
"available", "a", false, "includes quantity available instead of percentage used")
rootCmd.PersistentFlags().StringVarP(&podLabels,
"pod-labels", "l", "", "labels to filter pods with")
rootCmd.PersistentFlags().StringVarP(&nodeLabels,
Expand Down

0 comments on commit 957ca86

Please sign in to comment.