1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix getting metrics name for HPA v1 (#7011)

* Add ability for KubeApi to filter server versions

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update error message

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Show metric name is targetCPUUtilizationPercentage is used

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Testing metric names in HPA details

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Linter fixes

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
Co-authored-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Alex Andreev 2023-01-25 17:04:56 +03:00 committed by GitHub
parent c62fcb742c
commit 9b001adb07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 732 additions and 8 deletions

View File

@ -0,0 +1,326 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<HpaDetails/> renders 1`] = `
<body>
<div>
<div
class="HpaDetails"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Reference
</span>
<span
class="value"
>
Deployment
/
hpav2deployment
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Min Pods
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Max Pods
</span>
<span
class="value"
>
10
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Replicas
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem status labelsOnly"
>
<span
class="name"
>
Status
</span>
<span
class="value"
/>
</div>
</div>
</div>
</body>
`;
exports[`<HpaDetails/> shows unknown metrics with lack of metric type 1`] = `
<body>
<div>
<div
class="HpaDetails"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Reference
</span>
<span
class="value"
>
Deployment
/
hpav2deployment
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Min Pods
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Max Pods
</span>
<span
class="value"
>
10
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Replicas
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem status labelsOnly"
>
<span
class="name"
>
Status
</span>
<span
class="value"
/>
</div>
<div
class="DrawerTitle title"
>
Metrics
</div>
<div
class="metrics"
>
<div
class="Table flex column scrollable autoSize"
>
<div
class="TableHead sticky flat"
>
<div
class="TableCell name"
>
Name
</div>
<div
class="TableCell metrics"
>
Current / Target
</div>
</div>
<div
class="TableRow"
>
<div
class="TableCell name"
>
unknown
</div>
<div
class="TableCell metrics"
>
unknown / unknown
</div>
</div>
</div>
</div>
</div>
</div>
</body>
`;
exports[`<HpaDetails/> shows unknown metrics with with unusual type 1`] = `
<body>
<div>
<div
class="HpaDetails"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Reference
</span>
<span
class="value"
>
Deployment
/
hpav2deployment
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Min Pods
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Max Pods
</span>
<span
class="value"
>
10
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Replicas
</span>
<span
class="value"
>
0
</span>
</div>
<div
class="DrawerItem status labelsOnly"
>
<span
class="name"
>
Status
</span>
<span
class="value"
/>
</div>
<div
class="DrawerTitle title"
>
Metrics
</div>
<div
class="metrics"
>
<div
class="Table flex column scrollable autoSize"
>
<div
class="TableHead sticky flat"
>
<div
class="TableCell name"
>
Name
</div>
<div
class="TableCell metrics"
>
Current / Target
</div>
</div>
<div
class="TableRow"
>
<div
class="TableCell name"
>
unknown
</div>
<div
class="TableCell metrics"
>
unknown / unknown
</div>
</div>
</div>
</div>
</div>
</div>
</body>
`;

View File

@ -19,8 +19,8 @@ interface Metric extends MetricNames {
type: HpaMetricType; type: HpaMetricType;
} }
export function getMetricName(metric: Metric): string | undefined { export function getMetricName(metric: Metric | undefined): string | undefined {
switch (metric.type) { switch (metric?.type) {
case HpaMetricType.Resource: case HpaMetricType.Resource:
return metric.resource?.name; return metric.resource?.name;
case HpaMetricType.Pods: case HpaMetricType.Pods:

View File

@ -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("<HpaDetails/>", () => {
let result: RenderResult;
let render: DiRender;
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
});
it("renders", () => {
const hpa = new HorizontalPodAutoscaler(hpaV2);
result = render(
<HpaDetails object={hpa} />,
);
expect(result.baseElement).toMatchSnapshot();
});
it("does not show metrics table if no metrics found", () => {
const hpa = new HorizontalPodAutoscaler(hpaV2);
result = render(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
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(
<HpaDetails object={hpa} />,
);
expect(result.baseElement).toMatchSnapshot();
});
});

View File

@ -62,7 +62,7 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => { const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => {
const metricName = getMetricName(metric); const metricName = getMetricName(metric);
switch (metric.type) { switch (metric?.type) {
case HpaMetricType.ContainerResource: case HpaMetricType.ContainerResource:
// fallthrough // fallthrough
@ -85,11 +85,13 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
} }
case HpaMetricType.External: case HpaMetricType.External:
return `${metricName} on ${JSON.stringify(metric.external.metricSelector ?? metric.external.metric?.selector)}`; return `${metricName} on ${JSON.stringify(metric.external.metricSelector ?? metric.external.metric?.selector)}`;
default:
return hpa.spec?.targetCPUUtilizationPercentage ? "CPU Utilization percentage" : "unknown";
} }
}; };
return ( return (
<Table> <Table data-testid="hpa-metrics">
<TableHead flat> <TableHead flat>
<TableCell className="name">Name</TableCell> <TableCell className="name">Name</TableCell>
<TableCell className="metrics">Current / Target</TableCell> <TableCell className="metrics">Current / Target</TableCell>
@ -162,10 +164,14 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
))} ))}
</DrawerItem> </DrawerItem>
<DrawerTitle>Metrics</DrawerTitle> {(hpa.getMetrics().length !== 0 || hpa.spec?.targetCPUUtilizationPercentage) && (
<div className="metrics"> <>
{this.renderMetrics()} <DrawerTitle>Metrics</DrawerTitle>
</div> <div className="metrics">
{this.renderMetrics()}
</div>
</>
)}
</div> </div>
); );
} }