From 9b001adb0754e086bfc8e86e5f7c0639bd0b019c Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 25 Jan 2023 17:04:56 +0300 Subject: [PATCH] Fix getting metrics name for HPA v1 (#7011) * Add ability for KubeApi to filter server versions Signed-off-by: Sebastian Malton * Update error message Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton * Show metric name is targetCPUUtilizationPercentage is used Signed-off-by: Alex Andreev * Testing metric names in HPA details Signed-off-by: Alex Andreev * Linter fixes Signed-off-by: Alex Andreev Signed-off-by: Sebastian Malton Signed-off-by: Alex Andreev Co-authored-by: Sebastian Malton --- .../__snapshots__/hpa-details.test.tsx.snap | 326 +++++++++++++++ .../get-hpa-metric-name.ts | 4 +- .../+config-autoscalers/hpa-details.test.tsx | 392 ++++++++++++++++++ .../+config-autoscalers/hpa-details.tsx | 18 +- 4 files changed, 732 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/renderer/components/+config-autoscalers/__snapshots__/hpa-details.test.tsx.snap create mode 100644 packages/core/src/renderer/components/+config-autoscalers/hpa-details.test.tsx diff --git a/packages/core/src/renderer/components/+config-autoscalers/__snapshots__/hpa-details.test.tsx.snap b/packages/core/src/renderer/components/+config-autoscalers/__snapshots__/hpa-details.test.tsx.snap new file mode 100644 index 0000000000..a32a798b8b --- /dev/null +++ b/packages/core/src/renderer/components/+config-autoscalers/__snapshots__/hpa-details.test.tsx.snap @@ -0,0 +1,326 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders 1`] = ` + +
+
+
+ + Reference + + + Deployment + / + hpav2deployment + +
+
+ + Min Pods + + + 0 + +
+
+ + Max Pods + + + 10 + +
+
+ + Replicas + + + 0 + +
+
+ + Status + + +
+
+
+ +`; + +exports[` shows unknown metrics with lack of metric type 1`] = ` + +
+
+
+ + Reference + + + Deployment + / + hpav2deployment + +
+
+ + Min Pods + + + 0 + +
+
+ + Max Pods + + + 10 + +
+
+ + Replicas + + + 0 + +
+
+ + Status + + +
+
+ Metrics +
+
+
+
+
+ Name +
+
+ Current / Target +
+
+
+
+ unknown +
+
+ unknown / unknown +
+
+
+
+
+
+ +`; + +exports[` shows unknown metrics with with unusual type 1`] = ` + +
+
+
+ + Reference + + + Deployment + / + hpav2deployment + +
+
+ + Min Pods + + + 0 + +
+
+ + Max Pods + + + 10 + +
+
+ + Replicas + + + 0 + +
+
+ + Status + + +
+
+ Metrics +
+
+
+
+
+ Name +
+
+ Current / Target +
+
+
+
+ unknown +
+
+ unknown / unknown +
+
+
+
+
+
+ +`; diff --git a/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts b/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts index f92b249909..d45db05ec0 100644 --- a/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts +++ b/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts @@ -19,8 +19,8 @@ interface Metric extends MetricNames { type: HpaMetricType; } -export function getMetricName(metric: Metric): string | undefined { - switch (metric.type) { +export function getMetricName(metric: Metric | undefined): string | undefined { + switch (metric?.type) { case HpaMetricType.Resource: return metric.resource?.name; case HpaMetricType.Pods: diff --git a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.test.tsx b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.test.tsx new file mode 100644 index 0000000000..017b719841 --- /dev/null +++ b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.test.tsx @@ -0,0 +1,392 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import React from "react"; +import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints"; +import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import type { DiRender } from "../test-utils/renderFor"; +import { renderFor } from "../test-utils/renderFor"; +import { HpaDetails } from "./hpa-details"; + +jest.mock("react-router-dom", () => ({ + Link: ({ children }: { children: React.ReactNode }) => children, +})); + +const hpaV2 = { + apiVersion: "autoscaling/v2", + kind: "HorizontalPodAutoscaler", + metadata: { + name: "hpav2", + resourceVersion: "1", + uid: "hpav2", + namespace: "default", + selfLink: "/apis/autoscaling/v2/namespaces/default/horizontalpodautoscalers/hpav2", + }, + spec: { + maxReplicas: 10, + scaleTargetRef: { + kind: "Deployment", + name: "hpav2deployment", + apiVersion: "apps/v1", + }, + }, +}; + +describe("", () => { + let result: RenderResult; + let render: DiRender; + + beforeEach(() => { + const di = getDiForUnitTesting({ doGeneralOverrides: true }); + + render = renderFor(di); + }); + + it("renders", () => { + const hpa = new HorizontalPodAutoscaler(hpaV2); + + result = render( + , + ); + + expect(result.baseElement).toMatchSnapshot(); + }); + + it("does not show metrics table if no metrics found", () => { + const hpa = new HorizontalPodAutoscaler(hpaV2); + + result = render( + , + ); + + expect(result.queryByTestId("hpa-metrics")).toBeNull(); + }); + + it("shows proper metric name for autoscaling/v1", () => { + const hpa = new HorizontalPodAutoscaler({ + apiVersion: "autoscaling/v1", + kind: "HorizontalPodAutoscaler", + metadata: { + name: "hpav1", + resourceVersion: "1", + uid: "hpav1", + namespace: "default", + selfLink: "/apis/autoscaling/v1/namespaces/default/horizontalpodautoscalers/hpav1", + }, + spec: { + maxReplicas: 10, + scaleTargetRef: { + kind: "Deployment", + name: "hpav1deployment", + apiVersion: "apps/v1", + }, + targetCPUUtilizationPercentage: 80, + }, + }); + + result = render( + , + ); + + expect(result.getByText("CPU Utilization percentage")).toBeInTheDocument(); + }); + + it("shows proper metric name for container resource metrics", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.ContainerResource, + containerResource: { + name: "cpu", + container: "nginx", + target: { + type: "Utilization", + averageUtilization: 60, + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument(); + }); + + it("shows proper metric name for resource metrics", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.Resource, + resource: { + name: "cpu", + target: { + type: "Utilization", + averageUtilization: 50, + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument(); + }); + + it("shows proper metric name for pod metrics for hpa v2", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.Pods, + pods: { + metric: { + name: "packets-per-second", + }, + target: { + type: "AverageValue", + averageValue: "1k", + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument(); + }); + + it("shows proper metric name for pod metrics for hpa v2beta1", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.Pods, + pods: { + metricName: "packets-per-second", + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument(); + }); + + it("shows proper metric name for object metrics for hpa v2", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.Object, + object: { + metric: { + name: "requests-per-second", + }, + target: { + type: "Value", + value: "10k", + }, + describedObject: { + kind: "Service", + name: "nginx", + apiVersion: "v1", + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText(/requests-per-second/)).toHaveTextContent("requests-per-second onService/nginx"); + }); + + it("shows proper metric name for object metrics for hpa v2beta1", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.Object, + object: { + metricName: "requests-per-second", + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("requests-per-second")).toBeInTheDocument(); + }); + + it("shows proper metric name for external metrics for hpa v2", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.External, + external: { + metric: { + name: "queue_messages_ready", + selector: { + matchLabels: { queue: "worker_tasks" }, + }, + }, + target: { + type: "AverageValue", + averageValue: "30", + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument(); + }); + + it("shows proper metric name for external metrics for hpa v2beta1", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + type: HpaMetricType.External, + external: { + metricName: "queue_messages_ready", + metricSelector: { + matchLabels: { queue: "worker_tasks" }, + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument(); + }); + + it("shows unknown metrics with lack of metric type", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + // @ts-ignore + { + resource: { + name: "cpu", + target: { + type: "Utilization", + averageUtilization: 50, + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.baseElement).toMatchSnapshot(); + }); + + it("shows unknown metrics with with unusual type", () => { + const hpa = new HorizontalPodAutoscaler( + { + ...hpaV2, + spec: { + ...hpaV2.spec, + metrics: [ + { + // @ts-ignore + type: "Unusual", + resource: { + name: "cpu", + target: { + type: "Utilization", + averageUtilization: 50, + }, + }, + }, + ], + }, + }, + ); + + result = render( + , + ); + + expect(result.baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx index 96c1a7e20b..49130991a5 100644 --- a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -62,7 +62,7 @@ class NonInjectedHpaDetails extends React.Component { const metricName = getMetricName(metric); - switch (metric.type) { + switch (metric?.type) { case HpaMetricType.ContainerResource: // fallthrough @@ -85,11 +85,13 @@ class NonInjectedHpaDetails extends React.Component + Name Current / Target @@ -162,10 +164,14 @@ class NonInjectedHpaDetails extends React.Component - Metrics -
- {this.renderMetrics()} -
+ {(hpa.getMetrics().length !== 0 || hpa.spec?.targetCPUUtilizationPercentage) && ( + <> + Metrics +
+ {this.renderMetrics()} +
+ + )} ); }