From 776ea9c6fc41d8e5cec137c81d2e2405900fd75c Mon Sep 17 00:00:00 2001 From: "ben.elyess@cgm.com" Date: Tue, 7 Jan 2025 16:26:43 +0100 Subject: [PATCH] support ephemeral storage request/limits --- pkg/capacity/list.go | 40 ++++++----- pkg/capacity/options.go | 1 + pkg/capacity/resources.go | 65 +++++++++++++---- pkg/capacity/table.go | 143 +++++++++++++++++++++----------------- pkg/cmd/root.go | 2 + 5 files changed, 157 insertions(+), 94 deletions(-) diff --git a/pkg/capacity/list.go b/pkg/capacity/list.go index 1aef8a20..5c19c4ad 100644 --- a/pkg/capacity/list.go +++ b/pkg/capacity/list.go @@ -22,25 +22,28 @@ import ( ) type listNodeMetric struct { - Name string `json:"name"` - CPU *listResourceOutput `json:"cpu,omitempty"` - Memory *listResourceOutput `json:"memory,omitempty"` - Pods []*listPod `json:"pods,omitempty"` - PodCount string `json:"podCount,omitempty"` + Name string `json:"name"` + CPU *listResourceOutput `json:"cpu,omitempty"` + Memory *listResourceOutput `json:"memory,omitempty"` + EphemeralStorage *listResourceOutput `json:"ephemeralStorage,omitempty"` + Pods []*listPod `json:"pods,omitempty"` + PodCount string `json:"podCount,omitempty"` } type listPod struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - CPU *listResourceOutput `json:"cpu"` - Memory *listResourceOutput `json:"memory"` - Containers []listContainer `json:"containers,omitempty"` + Name string `json:"name"` + Namespace string `json:"namespace"` + CPU *listResourceOutput `json:"cpu"` + Memory *listResourceOutput `json:"memory"` + EphemeralStorage *listResourceOutput `json:"ephemeralStorage,omitempty"` + Containers []listContainer `json:"containers,omitempty"` } type listContainer struct { - Name string `json:"name"` - CPU *listResourceOutput `json:"cpu"` - Memory *listResourceOutput `json:"memory"` + Name string `json:"name"` + CPU *listResourceOutput `json:"cpu"` + Memory *listResourceOutput `json:"memory"` + EphemeralStorage *listResourceOutput `json:"ephemeralStorage,omitempty"` } type listResourceOutput struct { @@ -58,9 +61,10 @@ type listClusterMetrics struct { } type listClusterTotals struct { - CPU *listResourceOutput `json:"cpu"` - Memory *listResourceOutput `json:"memory"` - PodCount string `json:"podCount,omitempty"` + CPU *listResourceOutput `json:"cpu"` + Memory *listResourceOutput `json:"memory"` + EphemeralStorage *listResourceOutput `json:"ephemeralStorage,omitempty"` + PodCount string `json:"podCount,omitempty"` } type listPrinter struct { @@ -103,6 +107,7 @@ func (lp *listPrinter) buildListClusterMetrics() listClusterMetrics { response.ClusterTotals = &listClusterTotals{ CPU: lp.buildListResourceOutput(lp.cm.cpu), Memory: lp.buildListResourceOutput(lp.cm.memory), + EphemeralStorage: lp.buildListResourceOutput(lp.cm.ephemeralStorage), } if lp.showPodCount { @@ -114,6 +119,7 @@ func (lp *listPrinter) buildListClusterMetrics() listClusterMetrics { node.Name = nodeMetric.name node.CPU = lp.buildListResourceOutput(nodeMetric.cpu) node.Memory = lp.buildListResourceOutput(nodeMetric.memory) + node.EphemeralStorage = lp.buildListResourceOutput(nodeMetric.ephemeralStorage) if lp.showPodCount { node.PodCount = nodeMetric.podCount.podCountString() @@ -126,6 +132,7 @@ func (lp *listPrinter) buildListClusterMetrics() listClusterMetrics { pod.Namespace = podMetric.namespace pod.CPU = lp.buildListResourceOutput(podMetric.cpu) pod.Memory = lp.buildListResourceOutput(podMetric.memory) + pod.EphemeralStorage = lp.buildListResourceOutput(podMetric.ephemeralStorage) if lp.showContainers { for _, containerMetric := range podMetric.getSortedContainerMetrics(lp.sortBy) { @@ -133,6 +140,7 @@ func (lp *listPrinter) buildListClusterMetrics() listClusterMetrics { Name: containerMetric.name, Memory: lp.buildListResourceOutput(containerMetric.memory), CPU: lp.buildListResourceOutput(containerMetric.cpu), + EphemeralStorage: lp.buildListResourceOutput(containerMetric.ephemeralStorage), }) } } diff --git a/pkg/capacity/options.go b/pkg/capacity/options.go index 2393cac9..6c7ba239 100644 --- a/pkg/capacity/options.go +++ b/pkg/capacity/options.go @@ -7,6 +7,7 @@ type Options struct { ShowPods bool ShowUtil bool ShowPodCount bool + ShowEphemeralStorage bool // New flag for ephemeral storage PodLabels string NodeLabels string NodeTaints string diff --git a/pkg/capacity/resources.go b/pkg/capacity/resources.go index 732e65ab..ce3bead8 100644 --- a/pkg/capacity/resources.go +++ b/pkg/capacity/resources.go @@ -55,30 +55,34 @@ type resourceMetric struct { type clusterMetric struct { cpu *resourceMetric memory *resourceMetric + ephemeralStorage *resourceMetric nodeMetrics map[string]*nodeMetric podCount *podCount } type nodeMetric struct { - name string - cpu *resourceMetric - memory *resourceMetric - podMetrics map[string]*podMetric - podCount *podCount + name string + cpu *resourceMetric + memory *resourceMetric + ephemeralStorage *resourceMetric + podMetrics map[string]*podMetric + podCount *podCount } type podMetric struct { - name string - namespace string - cpu *resourceMetric - memory *resourceMetric - containerMetrics map[string]*containerMetric + name string + namespace string + cpu *resourceMetric + memory *resourceMetric + ephemeralStorage *resourceMetric + containerMetrics map[string]*containerMetric } type containerMetric struct { - name string - cpu *resourceMetric - memory *resourceMetric + name string + cpu *resourceMetric + memory *resourceMetric + ephemeralStorage *resourceMetric } type podCount struct { @@ -91,6 +95,7 @@ func buildClusterMetric(podList *corev1.PodList, pmList *v1beta1.PodMetricsList, cm := clusterMetric{ cpu: &resourceMetric{resourceType: "cpu"}, memory: &resourceMetric{resourceType: "memory"}, + ephemeralStorage: &resourceMetric{resourceType: "ephemeral-storage"}, nodeMetrics: map[string]*nodeMetric{}, podCount: &podCount{}, } @@ -116,6 +121,10 @@ func buildClusterMetric(podList *corev1.PodList, pmList *v1beta1.PodMetricsList, resourceType: "memory", allocatable: node.Status.Allocatable["memory"], }, + ephemeralStorage: &resourceMetric{ + resourceType: "ephemeral-storage", + allocatable: node.Status.Allocatable["ephemeral-storage"], + }, podMetrics: map[string]*podMetric{}, podCount: &podCount{ current: tmpPodCount, @@ -134,6 +143,7 @@ func buildClusterMetric(podList *corev1.PodList, pmList *v1beta1.PodMetricsList, } cm.nodeMetrics[nm.Name].cpu.utilization = nm.Usage["cpu"] cm.nodeMetrics[nm.Name].memory.utilization = nm.Usage["memory"] + cm.nodeMetrics[nm.Name].ephemeralStorage.utilization = nm.Usage["ephemeral-storage"] } } @@ -189,6 +199,11 @@ func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMet request: req["memory"], limit: limit["memory"], }, + ephemeralStorage: &resourceMetric{ + resourceType: "ephemeral-storage", + request: req["ephemeral-storage"], + limit: limit["ephemeral-storage"], + }, containerMetrics: map[string]*containerMetric{}, } @@ -207,6 +222,12 @@ func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMet limit: container.Resources.Limits["memory"], allocatable: nm.memory.allocatable, }, + ephemeralStorage: &resourceMetric{ + resourceType: "ephemeral-storage", + request: container.Resources.Requests["ephemeral-storage"], + limit: container.Resources.Limits["ephemeral-storage"], + allocatable: nm.ephemeralStorage.allocatable, + }, } } @@ -214,11 +235,14 @@ func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMet nm.podMetrics[key] = pm nm.podMetrics[key].cpu.allocatable = nm.cpu.allocatable nm.podMetrics[key].memory.allocatable = nm.memory.allocatable + nm.podMetrics[key].ephemeralStorage.allocatable = nm.ephemeralStorage.allocatable nm.cpu.request.Add(req["cpu"]) nm.cpu.limit.Add(limit["cpu"]) nm.memory.request.Add(req["memory"]) nm.memory.limit.Add(limit["memory"]) + nm.ephemeralStorage.request.Add(req["ephemeral-storage"]) + nm.ephemeralStorage.limit.Add(limit["ephemeral-storage"]) } for _, container := range podMetrics.Containers { @@ -228,6 +252,8 @@ func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMet pm.cpu.utilization.Add(container.Usage["cpu"]) pm.containerMetrics[container.Name].memory.utilization = container.Usage["memory"] pm.memory.utilization.Add(container.Usage["memory"]) + pm.containerMetrics[container.Name].ephemeralStorage.utilization = container.Usage["ephemeral-storage"] + pm.ephemeralStorage.utilization.Add(container.Usage["ephemeral-storage"]) } } } @@ -235,6 +261,7 @@ func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMet func (cm *clusterMetric) addNodeMetric(nm *nodeMetric) { cm.cpu.addMetric(nm.cpu) cm.memory.addMetric(nm.memory) + cm.ephemeralStorage.addMetric(nm.ephemeralStorage) } func (cm *clusterMetric) getSortedNodeMetrics(sortBy string) []*nodeMetric { @@ -333,6 +360,7 @@ func (nm *nodeMetric) addPodUtilization() { for _, pm := range nm.podMetrics { nm.cpu.utilization.Add(pm.cpu.utilization) nm.memory.utilization.Add(pm.memory.utilization) + nm.ephemeralStorage.utilization.Add(pm.ephemeralStorage.utilization) } } @@ -403,6 +431,9 @@ func resourceString(resourceType string, actual, allocatable resource.Quantity, case "memory": actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable)-formatToMegiBytes(actual)) allocatableStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable)) + case "ephemeral-storage": + actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable)-formatToMegiBytes(actual)) + allocatableStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable)) default: actualStr = fmt.Sprintf("%d", allocatable.Value()-actual.Value()) allocatableStr = fmt.Sprintf("%d", allocatable.Value()) @@ -416,6 +447,8 @@ func resourceString(resourceType string, actual, allocatable resource.Quantity, actualStr = fmt.Sprintf("%dm", actual.MilliValue()) case "memory": actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(actual)) + case "ephemeral-storage": + actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(actual)) default: actualStr = fmt.Sprintf("%d", actual.Value()) } @@ -442,6 +475,10 @@ func (rm resourceMetric) valueFunction() (f func(r resource.Quantity) string) { f = func(r resource.Quantity) string { return fmt.Sprintf("%dMi", formatToMegiBytes(r)) } + case "ephemeral-storage": + f = func(r resource.Quantity) string { + return fmt.Sprintf("%dMi", formatToMegiBytes(r)) + } } return f } @@ -462,7 +499,7 @@ func (rm resourceMetric) percent(r resource.Quantity) int64 { // ----------------------------------------- func resourceCSVString(resourceType string, actual resource.Quantity) string { - if resourceType == "memory" { + if resourceType == "memory" || resourceType == "ephemeral-storage" { return fmt.Sprintf("%d", formatToMegiBytes(actual)) } return fmt.Sprintf("%d", actual.MilliValue()) diff --git a/pkg/capacity/table.go b/pkg/capacity/table.go index 84374f48..2ada85aa 100644 --- a/pkg/capacity/table.go +++ b/pkg/capacity/table.go @@ -34,31 +34,35 @@ type tablePrinter struct { } type tableLine struct { - node string - namespace string - pod string - container string - cpuRequests string - cpuLimits string - cpuUtil string - memoryRequests string - memoryLimits string - memoryUtil string - podCount string + node string + namespace string + pod string + container string + cpuRequests string + cpuLimits string + cpuUtil string + memoryRequests string + memoryLimits string + memoryUtil string + podCount string + ephemeralRequests string + ephemeralLimits string } var headerStrings = tableLine{ - node: "NODE", - namespace: "NAMESPACE", - pod: "POD", - container: "CONTAINER", - cpuRequests: "CPU REQUESTS", - cpuLimits: "CPU LIMITS", - cpuUtil: "CPU UTIL", - memoryRequests: "MEMORY REQUESTS", - memoryLimits: "MEMORY LIMITS", - memoryUtil: "MEMORY UTIL", - podCount: "POD COUNT", + node: "NODE", + namespace: "NAMESPACE", + pod: "POD", + container: "CONTAINER", + cpuRequests: "CPU REQUESTS", + cpuLimits: "CPU LIMITS", + cpuUtil: "CPU UTIL", + memoryRequests: "MEMORY REQUESTS", + memoryLimits: "MEMORY LIMITS", + memoryUtil: "MEMORY UTIL", + podCount: "POD COUNT", + ephemeralRequests: "EPHEMERAL REQUESTS", + ephemeralLimits: "EPHEMERAL LIMITS", } func (tp *tablePrinter) Print() { @@ -131,6 +135,9 @@ func (tp *tablePrinter) getLineItems(tl *tableLine) []string { lineItems = append(lineItems, tl.memoryUtil) } + lineItems = append(lineItems, tl.ephemeralRequests) + lineItems = append(lineItems, tl.ephemeralLimits) + if tp.showPodCount { lineItems = append(lineItems, tl.podCount) } @@ -140,62 +147,70 @@ func (tp *tablePrinter) getLineItems(tl *tableLine) []string { func (tp *tablePrinter) printClusterLine() { tp.printLine(&tableLine{ - node: VoidValue, - namespace: VoidValue, - pod: VoidValue, - container: VoidValue, - 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), - podCount: tp.cm.podCount.podCountString(), + node: VoidValue, + namespace: VoidValue, + pod: VoidValue, + container: VoidValue, + 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), + ephemeralRequests: tp.cm.ephemeralStorage.requestString(tp.availableFormat), + ephemeralLimits: tp.cm.ephemeralStorage.limitString(tp.availableFormat), + podCount: tp.cm.podCount.podCountString(), }) } func (tp *tablePrinter) printNodeLine(nodeName string, nm *nodeMetric) { tp.printLine(&tableLine{ - node: nodeName, - namespace: VoidValue, - pod: VoidValue, - container: VoidValue, - 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), - podCount: nm.podCount.podCountString(), + node: nodeName, + namespace: VoidValue, + pod: VoidValue, + container: VoidValue, + 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), + ephemeralRequests: nm.ephemeralStorage.requestString(tp.availableFormat), + ephemeralLimits: nm.ephemeralStorage.limitString(tp.availableFormat), + podCount: nm.podCount.podCountString(), }) } func (tp *tablePrinter) printPodLine(nodeName string, pm *podMetric) { tp.printLine(&tableLine{ - node: nodeName, - namespace: pm.namespace, - pod: pm.name, - container: VoidValue, - 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), + node: nodeName, + namespace: pm.namespace, + pod: pm.name, + container: VoidValue, + 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), + ephemeralRequests: pm.ephemeralStorage.requestString(tp.availableFormat), + ephemeralLimits: pm.ephemeralStorage.limitString(tp.availableFormat), }) } func (tp *tablePrinter) printContainerLine(nodeName string, pm *podMetric, cm *containerMetric) { tp.printLine(&tableLine{ - node: nodeName, - namespace: pm.namespace, - pod: pm.name, - container: cm.name, - 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), + node: nodeName, + namespace: pm.namespace, + pod: pm.name, + container: cm.name, + 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), + ephemeralRequests: cm.ephemeralStorage.requestString(tp.availableFormat), + ephemeralLimits: cm.ephemeralStorage.limitString(tp.availableFormat), }) } diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 666a93ca..f47a4d47 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -51,6 +51,8 @@ func init() { "util", "u", false, "includes resource utilization in output") rootCmd.PersistentFlags().BoolVarP(&opts.ShowPodCount, "pod-count", "", false, "includes pod count per node in output") + rootCmd.PersistentFlags().BoolVarP(&opts.ShowEphemeralStorage, + "ephemeral-storage", "e", false, "includes ephemeral storage requests and limits in output") rootCmd.PersistentFlags().BoolVarP(&opts.AvailableFormat, "available", "a", false, "includes quantity available instead of percentage used") rootCmd.PersistentFlags().StringVarP(&opts.PodLabels,