Skip to content

Commit

Permalink
FIX PR #93 (#110)
Browse files Browse the repository at this point in the history
* wip

* initial writer code

* feature complete

* update readme

* force labels to be text

* fix separators

* add constants for repetitive strings

* fix

* fix csv value

* clean up

* Clean lint

Lint is preventing merge of PR 93,
a feature that includes comma and tab seperated
exports of capacity data

---------

Co-authored-by: Francis Nickels <fnickels@riotgames.com>
  • Loading branch information
isaacnboyd and fnickels authored Jan 22, 2024
1 parent 7ccdb4e commit 53845d2
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 12 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ kube-capacity --pods --output json
kube-capacity --pods --containers --util --output yaml
```

### CSV and TSV Output
If you would like the data in a comma or tab separated file to make importing the data into a spreadsheet easier the output flag has options for those as well. Here are some sample commands:
```
kube-capacity --pods --output csv
kube-capacity --pods --containers --util --output tsv
```
>Note: the `--available` flag is ignored with these two choices as the values can be derived within a spreadsheet
## Flags Supported
```
-c, --containers includes containers in output
Expand All @@ -149,9 +157,9 @@ kube-capacity --pods --containers --util --output yaml
--no-taint exclude nodes with taints
--node-labels string labels to filter nodes with
-o, --output string output format for information
(supports: [table json yaml])
(supports: [table json yaml csv tsv])
(default "table")
-a, --available includes quantity available instead of percentage used
-a, --available includes quantity available instead of percentage used (ignored with csv or tsv output types)
-l, --pod-labels string labels to filter pods with
-p, --pods includes pods in output
--sort string attribute to sort results by (supports:
Expand Down Expand Up @@ -180,6 +188,7 @@ Although this project was originally developed by [robscott](https://github.com/
- [justinbarrick](https://github.com/justinbarrick)
- [Padarn](https://github.com/Padarn)
- [nickatsegment](https://github.com/nickatsegment)
- [fnickels](https://github.com/fnickels)

## License
Apache License 2.0
21 changes: 21 additions & 0 deletions pkg/capacity/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

// add constants for repetitive strings
const (
VoidValue = "*"
CSVStringTerminator = "\""
)
262 changes: 262 additions & 0 deletions pkg/capacity/csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// Copyright 2023 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
"fmt"
"io"
"os"
"strings"
)

type csvPrinter struct {
cm *clusterMetric
showPods bool
showUtil bool
showPodCount bool
showContainers bool
showNamespace bool
sortBy string
file io.Writer
separator string
}

type csvLine struct {
node string
namespace string
pod string
container string
cpuCapacity string
cpuRequests string
cpuRequestsPercentage string
cpuLimits string
cpuLimitsPercentage string
cpuUtil string
cpuUtilPercentage string
memoryCapacity string
memoryRequests string
memoryRequestsPercentage string
memoryLimits string
memoryLimitsPercentage string
memoryUtil string
memoryUtilPercentage string
podCountCurrent string
podCountAllocatable string
}

var csvHeaderStrings = csvLine{
node: "NODE",
namespace: "NAMESPACE",
pod: "POD",
container: "CONTAINER",
cpuCapacity: "CPU CAPACITY (milli)",
cpuRequests: "CPU REQUESTS",
cpuRequestsPercentage: "CPU REQUESTS %%",
cpuLimits: "CPU LIMITS",
cpuLimitsPercentage: "CPU LIMITS %%",
cpuUtil: "CPU UTIL",
cpuUtilPercentage: "CPU UTIL %%",
memoryCapacity: "MEMORY CAPACITY (Mi)",
memoryRequests: "MEMORY REQUESTS",
memoryRequestsPercentage: "MEMORY REQUESTS %%",
memoryLimits: "MEMORY LIMITS",
memoryLimitsPercentage: "MEMORY LIMITS %%",
memoryUtil: "MEMORY UTIL",
memoryUtilPercentage: "MEMORY UTIL %%",
podCountCurrent: "POD COUNT CURRENT",
podCountAllocatable: "POD COUNT ALLOCATABLE",
}

func (cp *csvPrinter) Print(outputType string) {

cp.file = os.Stdout
cp.separator = outputType

sortedNodeMetrics := cp.cm.getSortedNodeMetrics(cp.sortBy)

cp.printLine(&csvHeaderStrings)

if len(sortedNodeMetrics) > 1 {
cp.printClusterLine()
}

for _, nm := range sortedNodeMetrics {
cp.printNodeLine(nm.name, nm)

if cp.showPods || cp.showContainers {
podMetrics := nm.getSortedPodMetrics(cp.sortBy)
for _, pm := range podMetrics {
cp.printPodLine(nm.name, pm)
if cp.showContainers {
containerMetrics := pm.getSortedContainerMetrics(cp.sortBy)
for _, containerMetric := range containerMetrics {
cp.printContainerLine(nm.name, pm, containerMetric)
}
}
}
}
}
}

func (cp *csvPrinter) printLine(cl *csvLine) {
separator := ","
if cp.separator == TSVOutput {
separator = "\t"
}

lineItems := cp.getLineItems(cl)

fmt.Fprintf(cp.file, strings.Join(lineItems[:], separator)+"\n")
}

func (cp *csvPrinter) getLineItems(cl *csvLine) []string {
lineItems := []string{CSVStringTerminator + cl.node + CSVStringTerminator}

if cp.showContainers || cp.showPods {
if cp.showNamespace {
lineItems = append(lineItems, CSVStringTerminator+cl.namespace+CSVStringTerminator)
}
lineItems = append(lineItems, CSVStringTerminator+cl.pod+CSVStringTerminator)
}

if cp.showContainers {
lineItems = append(lineItems, CSVStringTerminator+cl.container+CSVStringTerminator)
}

lineItems = append(lineItems, cl.cpuCapacity)
lineItems = append(lineItems, cl.cpuRequests)
lineItems = append(lineItems, cl.cpuRequestsPercentage)
lineItems = append(lineItems, cl.cpuLimits)
lineItems = append(lineItems, cl.cpuLimitsPercentage)

if cp.showUtil {
lineItems = append(lineItems, cl.cpuUtil)
lineItems = append(lineItems, cl.cpuUtilPercentage)
}

lineItems = append(lineItems, cl.memoryCapacity)
lineItems = append(lineItems, cl.memoryRequests)
lineItems = append(lineItems, cl.memoryRequestsPercentage)
lineItems = append(lineItems, cl.memoryLimits)
lineItems = append(lineItems, cl.memoryLimitsPercentage)

if cp.showUtil {
lineItems = append(lineItems, cl.memoryUtil)
lineItems = append(lineItems, cl.memoryUtilPercentage)
}

if cp.showPodCount {
lineItems = append(lineItems, cl.podCountCurrent)
lineItems = append(lineItems, cl.podCountAllocatable)
}

return lineItems
}

func (cp *csvPrinter) printClusterLine() {
cp.printLine(&csvLine{
node: VoidValue,
namespace: VoidValue,
pod: VoidValue,
container: VoidValue,
cpuCapacity: cp.cm.cpu.capacityString(),
cpuRequests: cp.cm.cpu.requestActualString(),
cpuRequestsPercentage: cp.cm.cpu.requestPercentageString(),
cpuLimits: cp.cm.cpu.limitActualString(),
cpuLimitsPercentage: cp.cm.cpu.limitPercentageString(),
cpuUtil: cp.cm.cpu.utilActualString(),
cpuUtilPercentage: cp.cm.cpu.utilPercentageString(),
memoryCapacity: cp.cm.memory.capacityString(),
memoryRequests: cp.cm.memory.requestActualString(),
memoryRequestsPercentage: cp.cm.memory.requestPercentageString(),
memoryLimits: cp.cm.memory.limitActualString(),
memoryLimitsPercentage: cp.cm.memory.limitPercentageString(),
memoryUtil: cp.cm.memory.utilActualString(),
memoryUtilPercentage: cp.cm.memory.utilPercentageString(),
podCountCurrent: cp.cm.podCount.podCountCurrentString(),
podCountAllocatable: cp.cm.podCount.podCountAllocatableString(),
})
}

func (cp *csvPrinter) printNodeLine(nodeName string, nm *nodeMetric) {
cp.printLine(&csvLine{
node: nodeName,
namespace: VoidValue,
pod: VoidValue,
container: VoidValue,
cpuCapacity: nm.cpu.capacityString(),
cpuRequests: nm.cpu.requestActualString(),
cpuRequestsPercentage: nm.cpu.requestPercentageString(),
cpuLimits: nm.cpu.limitActualString(),
cpuLimitsPercentage: nm.cpu.limitPercentageString(),
cpuUtil: nm.cpu.utilActualString(),
cpuUtilPercentage: nm.cpu.utilPercentageString(),
memoryCapacity: nm.memory.capacityString(),
memoryRequests: nm.memory.requestActualString(),
memoryRequestsPercentage: nm.memory.requestPercentageString(),
memoryLimits: nm.memory.limitActualString(),
memoryLimitsPercentage: nm.memory.limitPercentageString(),
memoryUtil: nm.memory.utilActualString(),
memoryUtilPercentage: nm.memory.utilPercentageString(),
podCountCurrent: nm.podCount.podCountCurrentString(),
podCountAllocatable: nm.podCount.podCountAllocatableString(),
})
}

func (cp *csvPrinter) printPodLine(nodeName string, pm *podMetric) {
cp.printLine(&csvLine{
node: nodeName,
namespace: pm.namespace,
pod: pm.name,
container: VoidValue,
cpuCapacity: pm.cpu.capacityString(),
cpuRequests: pm.cpu.requestActualString(),
cpuRequestsPercentage: pm.cpu.requestPercentageString(),
cpuLimits: pm.cpu.limitActualString(),
cpuLimitsPercentage: pm.cpu.limitPercentageString(),
cpuUtil: pm.cpu.utilActualString(),
cpuUtilPercentage: pm.cpu.utilPercentageString(),
memoryCapacity: pm.memory.capacityString(),
memoryRequests: pm.memory.requestActualString(),
memoryRequestsPercentage: pm.memory.requestPercentageString(),
memoryLimits: pm.memory.limitActualString(),
memoryLimitsPercentage: pm.memory.limitPercentageString(),
memoryUtil: pm.memory.utilActualString(),
memoryUtilPercentage: pm.memory.utilPercentageString(),
})
}

func (cp *csvPrinter) printContainerLine(nodeName string, pm *podMetric, cm *containerMetric) {
cp.printLine(&csvLine{
node: nodeName,
namespace: pm.namespace,
pod: pm.name,
container: cm.name,
cpuCapacity: cm.cpu.capacityString(),
cpuRequests: cm.cpu.requestActualString(),
cpuRequestsPercentage: cm.cpu.requestPercentageString(),
cpuLimits: cm.cpu.limitActualString(),
cpuLimitsPercentage: cm.cpu.limitPercentageString(),
cpuUtil: cm.cpu.utilActualString(),
cpuUtilPercentage: cm.cpu.utilPercentageString(),
memoryCapacity: cm.memory.capacityString(),
memoryRequests: cm.memory.requestActualString(),
memoryRequestsPercentage: cm.memory.requestPercentageString(),
memoryLimits: cm.memory.limitActualString(),
memoryLimitsPercentage: cm.memory.limitPercentageString(),
memoryUtil: cm.memory.utilActualString(),
memoryUtilPercentage: cm.memory.utilPercentageString(),
})
}
17 changes: 17 additions & 0 deletions pkg/capacity/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import (
const (
//TableOutput is the constant value for output type table
TableOutput string = "table"
//CSVOutput is the constant value for output type csv
CSVOutput string = "csv"
//TSVOutput is the constant value for output type csv
TSVOutput string = "tsv"
//JSONOutput is the constant value for output type JSON
JSONOutput string = "json"
//YAMLOutput is the constant value for output type YAML
Expand All @@ -33,6 +37,8 @@ const (
func SupportedOutputs() []string {
return []string{
TableOutput,
CSVOutput,
TSVOutput,
JSONOutput,
YAMLOutput,
}
Expand Down Expand Up @@ -62,6 +68,17 @@ func printList(cm *clusterMetric, showContainers, showPods, showUtil, showPodCou
availableFormat: availableFormat,
}
tp.Print()
} else if output == CSVOutput || output == TSVOutput {
cp := &csvPrinter{
cm: cm,
showPods: showPods,
showUtil: showUtil,
showPodCount: showPodCount,
showContainers: showContainers,
showNamespace: showNamespace,
sortBy: sortBy,
}
cp.Print(output)
} else {
fmt.Printf("Called with an unsupported output type: %s", output)
os.Exit(1)
Expand Down
Loading

0 comments on commit 53845d2

Please sign in to comment.