mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Parse HPA metrics from different versions (#6971)
* First sketch of hpav2 metrics Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * HPA metrics initial tests Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Receive target Object metrics for v2 Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * External target metrics Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add more types for ObjectMetricStatus Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Move metrics parsing from HPA object to separate injectable Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add metric parser for HPA v2 Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Using metrics parser in hpa list and details Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add more test cases for HPA v2 Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add HorizontalPodAutoscalerV1MetricParser Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding injectable for hpa v1 metric parser Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding test cases for autoscaling/v1 metrics Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add test cases for hpa beta versions Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Check for legacy targetCPUUtilizationPercentage Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fix external metirc parser output Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Small clean up Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Linter fixes Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Removing fallbackApiBases Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Remove left comments Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Making metric parser classes as not injectable Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fix metrics in hpa details for newer versions Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Spreading types to V2 and V2Beta1 Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Move getMetricName() to its own file Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
5eefa8f03a
commit
5f416921e9
@ -3,11 +3,11 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object";
|
import type { OptionVarient } from "../../utils";
|
||||||
import { KubeObject } from "../kube-object";
|
|
||||||
import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api";
|
import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api";
|
||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
import type { OptionVarient } from "../../utils";
|
import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object";
|
||||||
|
import { KubeObject } from "../kube-object";
|
||||||
|
|
||||||
export enum HpaMetricType {
|
export enum HpaMetricType {
|
||||||
Resource = "Resource",
|
Resource = "Resource",
|
||||||
@ -17,46 +17,131 @@ export enum HpaMetricType {
|
|||||||
ContainerResource = "ContainerResource",
|
ContainerResource = "ContainerResource",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MetricCurrentTarget {
|
||||||
|
current?: string;
|
||||||
|
target?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HorizontalPodAutoscalerMetricTarget {
|
export interface HorizontalPodAutoscalerMetricTarget {
|
||||||
kind: string;
|
kind: string;
|
||||||
name: string;
|
name: string;
|
||||||
apiVersion: string;
|
apiVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContainerResourceMetricSource {
|
export interface V2ContainerResourceMetricSource {
|
||||||
|
container: string;
|
||||||
|
name: string;
|
||||||
|
target?: {
|
||||||
|
averageUtilization?: number;
|
||||||
|
averageValue?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ContainerResourceMetricSource {
|
||||||
container: string;
|
container: string;
|
||||||
name: string;
|
name: string;
|
||||||
targetAverageUtilization?: number;
|
targetAverageUtilization?: number;
|
||||||
targetAverageValue?: string;
|
targetAverageValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExternalMetricSource {
|
export type ContainerResourceMetricSource =
|
||||||
metricName: string;
|
| V2ContainerResourceMetricSource
|
||||||
|
| V2Beta1ContainerResourceMetricSource;
|
||||||
|
|
||||||
|
export interface V2ExternalMetricSource {
|
||||||
|
metricName?: string;
|
||||||
|
metricSelector?: LabelSelector;
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
target?: {
|
||||||
|
type: string;
|
||||||
|
value?: string;
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ExternalMetricSource {
|
||||||
|
metricName?: string;
|
||||||
metricSelector?: LabelSelector;
|
metricSelector?: LabelSelector;
|
||||||
targetAverageValue?: string;
|
targetAverageValue?: string;
|
||||||
targetValue?: string;
|
targetValue?: string;
|
||||||
|
metric?: {
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ObjectMetricSource {
|
export type ExternalMetricSource =
|
||||||
|
| V2Beta1ExternalMetricSource
|
||||||
|
| V2ExternalMetricSource;
|
||||||
|
|
||||||
|
export interface V2ObjectMetricSource {
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
target?: {
|
||||||
|
type?: string;
|
||||||
|
value?: string;
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
describedObject?: CrossVersionObjectReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ObjectMetricSource {
|
||||||
averageValue?: string;
|
averageValue?: string;
|
||||||
metricName: string;
|
metricName?: string;
|
||||||
selector?: LabelSelector;
|
selector?: LabelSelector;
|
||||||
target: CrossVersionObjectReference;
|
targetValue?: string;
|
||||||
targetValue: string;
|
describedObject?: CrossVersionObjectReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PodsMetricSource {
|
export type ObjectMetricSource =
|
||||||
metricName: string;
|
| V2ObjectMetricSource
|
||||||
selector?: LabelSelector;
|
| V2Beta1ObjectMetricSource;
|
||||||
targetAverageValue: string;
|
|
||||||
|
export interface V2PodsMetricSource {
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
target?: {
|
||||||
|
averageValue?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceMetricSource {
|
export interface V2Beta1PodsMetricSource {
|
||||||
|
metricName?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
targetAverageValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PodsMetricSource =
|
||||||
|
| V2PodsMetricSource
|
||||||
|
| V2Beta1PodsMetricSource;
|
||||||
|
|
||||||
|
export interface V2ResourceMetricSource {
|
||||||
|
name: string;
|
||||||
|
target?: {
|
||||||
|
averageUtilization?: number;
|
||||||
|
averageValue?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ResourceMetricSource {
|
||||||
name: string;
|
name: string;
|
||||||
targetAverageUtilization?: number;
|
targetAverageUtilization?: number;
|
||||||
targetAverageValue?: string;
|
targetAverageValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ResourceMetricSource =
|
||||||
|
| V2ResourceMetricSource
|
||||||
|
| V2Beta1ResourceMetricSource;
|
||||||
|
|
||||||
export interface BaseHorizontalPodAutoscalerMetricSpec {
|
export interface BaseHorizontalPodAutoscalerMetricSpec {
|
||||||
containerResource: ContainerResourceMetricSource;
|
containerResource: ContainerResourceMetricSource;
|
||||||
external: ExternalMetricSource;
|
external: ExternalMetricSource;
|
||||||
@ -93,40 +178,112 @@ interface HPAScalingPolicy {
|
|||||||
|
|
||||||
type HPAScalingPolicyType = string;
|
type HPAScalingPolicyType = string;
|
||||||
|
|
||||||
export interface ContainerResourceMetricStatus {
|
export interface V2ContainerResourceMetricStatus {
|
||||||
container: string;
|
container?: string;
|
||||||
|
name: string;
|
||||||
|
current?: {
|
||||||
|
averageUtilization?: number;
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ContainerResourceMetricStatus {
|
||||||
|
container?: string;
|
||||||
currentAverageUtilization?: number;
|
currentAverageUtilization?: number;
|
||||||
currentAverageValue: string;
|
currentAverageValue?: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExternalMetricStatus {
|
export type ContainerResourceMetricStatus =
|
||||||
|
| V2ContainerResourceMetricStatus
|
||||||
|
| V2Beta1ContainerResourceMetricStatus;
|
||||||
|
|
||||||
|
export interface V2ExternalMetricStatus {
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
current?: {
|
||||||
|
averageValue?: string;
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ExternalMetricStatus {
|
||||||
currentAverageValue?: string;
|
currentAverageValue?: string;
|
||||||
currentValue: string;
|
currentValue?: string;
|
||||||
metricName: string;
|
metricName?: string;
|
||||||
metricSelector?: LabelSelector;
|
metricSelector?: LabelSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ObjectMetricStatus {
|
export type ExternalMetricStatus =
|
||||||
|
| V2Beta1ExternalMetricStatus
|
||||||
|
| V2ExternalMetricStatus;
|
||||||
|
|
||||||
|
export interface V2ObjectMetricStatus {
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
current?: {
|
||||||
|
type?: string;
|
||||||
|
value?: string;
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
describedObject?: CrossVersionObjectReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ObjectMetricStatus {
|
||||||
averageValue?: string;
|
averageValue?: string;
|
||||||
currentValue?: string;
|
currentValue?: string;
|
||||||
metricName: string;
|
metricName?: string;
|
||||||
selector?: LabelSelector;
|
selector?: LabelSelector;
|
||||||
target: CrossVersionObjectReference;
|
describedObject?: CrossVersionObjectReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PodsMetricStatus {
|
export type ObjectMetricStatus =
|
||||||
currentAverageValue: string;
|
| V2Beta1ObjectMetricStatus
|
||||||
metricName: string;
|
| V2ObjectMetricStatus;
|
||||||
|
|
||||||
|
export interface V2PodsMetricStatus {
|
||||||
|
selector?: LabelSelector;
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
current?: {
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1PodsMetricStatus {
|
||||||
|
currentAverageValue?: string;
|
||||||
|
metricName?: string;
|
||||||
selector?: LabelSelector;
|
selector?: LabelSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceMetricStatus {
|
export type PodsMetricStatus =
|
||||||
|
| V2Beta1PodsMetricStatus
|
||||||
|
| V2PodsMetricStatus;
|
||||||
|
|
||||||
|
export interface V2ResourceMetricStatus {
|
||||||
|
name: string;
|
||||||
|
current?: {
|
||||||
|
averageUtilization?: number;
|
||||||
|
averageValue?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface V2Beta1ResourceMetricStatus {
|
||||||
currentAverageUtilization?: number;
|
currentAverageUtilization?: number;
|
||||||
currentAverageValue: string;
|
currentAverageValue?: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ResourceMetricStatus =
|
||||||
|
| V2Beta1ResourceMetricStatus
|
||||||
|
| V2ResourceMetricStatus;
|
||||||
|
|
||||||
export interface BaseHorizontalPodAutoscalerMetricStatus {
|
export interface BaseHorizontalPodAutoscalerMetricStatus {
|
||||||
containerResource: ContainerResourceMetricStatus;
|
containerResource: ContainerResourceMetricStatus;
|
||||||
external: ExternalMetricStatus;
|
external: ExternalMetricStatus;
|
||||||
@ -154,6 +311,7 @@ export interface HorizontalPodAutoscalerSpec {
|
|||||||
maxReplicas: number;
|
maxReplicas: number;
|
||||||
metrics?: HorizontalPodAutoscalerMetricSpec[];
|
metrics?: HorizontalPodAutoscalerMetricSpec[];
|
||||||
behavior?: HorizontalPodAutoscalerBehavior;
|
behavior?: HorizontalPodAutoscalerBehavior;
|
||||||
|
targetCPUUtilizationPercentage?: number; // used only in autoscaling/v1
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HorizontalPodAutoscalerStatus {
|
export interface HorizontalPodAutoscalerStatus {
|
||||||
@ -161,11 +319,7 @@ export interface HorizontalPodAutoscalerStatus {
|
|||||||
currentReplicas: number;
|
currentReplicas: number;
|
||||||
desiredReplicas: number;
|
desiredReplicas: number;
|
||||||
currentMetrics?: HorizontalPodAutoscalerMetricStatus[];
|
currentMetrics?: HorizontalPodAutoscalerMetricStatus[];
|
||||||
}
|
currentCPUUtilizationPercentage?: number; // used only in autoscaling/v1
|
||||||
|
|
||||||
interface MetricCurrentTarget {
|
|
||||||
current?: string;
|
|
||||||
target?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HorizontalPodAutoscaler extends KubeObject<
|
export class HorizontalPodAutoscaler extends KubeObject<
|
||||||
@ -212,15 +366,6 @@ export class HorizontalPodAutoscaler extends KubeObject<
|
|||||||
getCurrentMetrics() {
|
getCurrentMetrics() {
|
||||||
return this.status?.currentMetrics ?? [];
|
return this.status?.currentMetrics ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetricValues(metric: HorizontalPodAutoscalerMetricSpec): string {
|
|
||||||
const {
|
|
||||||
current = "unknown",
|
|
||||||
target = "unknown",
|
|
||||||
} = getMetricCurrentTarget(metric, this.getCurrentMetrics());
|
|
||||||
|
|
||||||
return `${current} / ${target}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HorizontalPodAutoscalerApi extends KubeApi<HorizontalPodAutoscaler> {
|
export class HorizontalPodAutoscalerApi extends KubeApi<HorizontalPodAutoscaler> {
|
||||||
@ -229,114 +374,6 @@ export class HorizontalPodAutoscalerApi extends KubeApi<HorizontalPodAutoscaler>
|
|||||||
...opts ?? {},
|
...opts ?? {},
|
||||||
objectConstructor: HorizontalPodAutoscaler,
|
objectConstructor: HorizontalPodAutoscaler,
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
// Kubernetes < 1.26
|
|
||||||
fallbackApiBases: [
|
|
||||||
"/apis/autoscaling/v2beta2/horizontalpodautoscalers",
|
|
||||||
"/apis/autoscaling/v2beta1/horizontalpodautoscalers",
|
|
||||||
"/apis/autoscaling/v1/horizontalpodautoscalers",
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetricName(metric: HorizontalPodAutoscalerMetricSpec | HorizontalPodAutoscalerMetricStatus): string | undefined {
|
|
||||||
switch (metric.type) {
|
|
||||||
case HpaMetricType.Resource:
|
|
||||||
return metric.resource.name;
|
|
||||||
case HpaMetricType.Pods:
|
|
||||||
return metric.pods.metricName;
|
|
||||||
case HpaMetricType.Object:
|
|
||||||
return metric.object.metricName;
|
|
||||||
case HpaMetricType.External:
|
|
||||||
return metric.external.metricName;
|
|
||||||
case HpaMetricType.ContainerResource:
|
|
||||||
return metric.containerResource.name;
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResourceMetricValue(currentMetric: ResourceMetricStatus | undefined, targetMetric: ResourceMetricSource): MetricCurrentTarget {
|
|
||||||
return {
|
|
||||||
current: (
|
|
||||||
typeof currentMetric?.currentAverageUtilization === "number"
|
|
||||||
? `${currentMetric.currentAverageUtilization}%`
|
|
||||||
: currentMetric?.currentAverageValue
|
|
||||||
),
|
|
||||||
target: (
|
|
||||||
typeof targetMetric?.targetAverageUtilization === "number"
|
|
||||||
? `${targetMetric.targetAverageUtilization}%`
|
|
||||||
: targetMetric?.targetAverageValue
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPodsMetricValue(currentMetric: PodsMetricStatus | undefined, targetMetric: PodsMetricSource): MetricCurrentTarget {
|
|
||||||
return {
|
|
||||||
current: currentMetric?.currentAverageValue,
|
|
||||||
target: targetMetric?.targetAverageValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getObjectMetricValue(currentMetric: ObjectMetricStatus | undefined, targetMetric: ObjectMetricSource): MetricCurrentTarget {
|
|
||||||
return {
|
|
||||||
current: (
|
|
||||||
currentMetric?.currentValue
|
|
||||||
?? currentMetric?.averageValue
|
|
||||||
),
|
|
||||||
target: (
|
|
||||||
targetMetric?.targetValue
|
|
||||||
?? targetMetric?.averageValue
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getExternalMetricValue(currentMetric: ExternalMetricStatus | undefined, targetMetric: ExternalMetricSource): MetricCurrentTarget {
|
|
||||||
return {
|
|
||||||
current: (
|
|
||||||
currentMetric?.currentValue
|
|
||||||
?? currentMetric?.currentAverageValue
|
|
||||||
),
|
|
||||||
target: (
|
|
||||||
targetMetric?.targetValue
|
|
||||||
?? targetMetric?.targetAverageValue
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContainerResourceMetricValue(currentMetric: ContainerResourceMetricStatus | undefined, targetMetric: ContainerResourceMetricSource): MetricCurrentTarget {
|
|
||||||
return {
|
|
||||||
current: (
|
|
||||||
typeof currentMetric?.currentAverageUtilization === "number"
|
|
||||||
? `${currentMetric.currentAverageUtilization}%`
|
|
||||||
: currentMetric?.currentAverageValue
|
|
||||||
),
|
|
||||||
target: (
|
|
||||||
typeof targetMetric?.targetAverageUtilization === "number"
|
|
||||||
? `${targetMetric.targetAverageUtilization}%`
|
|
||||||
: targetMetric?.targetAverageValue
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMetricCurrentTarget(spec: HorizontalPodAutoscalerMetricSpec, status: HorizontalPodAutoscalerMetricStatus[]): MetricCurrentTarget {
|
|
||||||
const currentMetric = status.find(m => (
|
|
||||||
m.type === spec.type
|
|
||||||
&& getMetricName(m) === getMetricName(spec)
|
|
||||||
));
|
|
||||||
|
|
||||||
switch (spec.type) {
|
|
||||||
case HpaMetricType.Resource:
|
|
||||||
return getResourceMetricValue(currentMetric?.resource, spec.resource);
|
|
||||||
case HpaMetricType.Pods:
|
|
||||||
return getPodsMetricValue(currentMetric?.pods, spec.pods);
|
|
||||||
case HpaMetricType.Object:
|
|
||||||
return getObjectMetricValue(currentMetric?.object, spec.object);
|
|
||||||
case HpaMetricType.External:
|
|
||||||
return getExternalMetricValue(currentMetric?.external, spec.external);
|
|
||||||
case HpaMetricType.ContainerResource:
|
|
||||||
return getContainerResourceMetricValue(currentMetric?.containerResource, spec.containerResource);
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { HpaMetricType } from "../../../common/k8s-api/endpoints";
|
||||||
|
import type { LabelSelector } from "../../../common/k8s-api/kube-object";
|
||||||
|
|
||||||
|
type MetricNames = Partial<Record<"resource" | "pods" | "object" | "external" | "containerResource", {
|
||||||
|
name?: string;
|
||||||
|
metricName?: string;
|
||||||
|
metric?: {
|
||||||
|
name?: string;
|
||||||
|
selector?: LabelSelector;
|
||||||
|
};
|
||||||
|
}>>;
|
||||||
|
|
||||||
|
interface Metric extends MetricNames {
|
||||||
|
type: HpaMetricType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMetricName(metric: Metric): string | undefined {
|
||||||
|
switch (metric.type) {
|
||||||
|
case HpaMetricType.Resource:
|
||||||
|
return metric.resource?.name;
|
||||||
|
case HpaMetricType.Pods:
|
||||||
|
return metric.pods?.metricName || metric.pods?.metric?.name;
|
||||||
|
case HpaMetricType.Object:
|
||||||
|
return metric.object?.metricName || metric.object?.metric?.name;
|
||||||
|
case HpaMetricType.External:
|
||||||
|
return metric.external?.metricName || metric.external?.metric?.name;
|
||||||
|
case HpaMetricType.ContainerResource:
|
||||||
|
return metric.containerResource?.name;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { HorizontalPodAutoscaler, HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricStatus } from "../../../common/k8s-api/endpoints";
|
||||||
|
import { HpaMetricType } from "../../../common/k8s-api/endpoints";
|
||||||
|
import { getMetricName } from "./get-hpa-metric-name";
|
||||||
|
import { HorizontalPodAutoscalerV1MetricParser } from "./hpa-v1-metric-parser";
|
||||||
|
import { HorizontalPodAutoscalerV2MetricParser } from "./hpa-v2-metric-parser";
|
||||||
|
|
||||||
|
type Parser = HorizontalPodAutoscalerV1MetricParser | HorizontalPodAutoscalerV2MetricParser;
|
||||||
|
|
||||||
|
const getHorizontalPodAutoscalerMetrics = getInjectable({
|
||||||
|
id: "get-horizontal-pod-autoscaler-metrics",
|
||||||
|
instantiate: () => (hpa: HorizontalPodAutoscaler) => {
|
||||||
|
const hpaV1Parser = new HorizontalPodAutoscalerV1MetricParser();
|
||||||
|
const hpaV2Parser = new HorizontalPodAutoscalerV2MetricParser();
|
||||||
|
const metrics = hpa.spec?.metrics ?? [];
|
||||||
|
const currentMetrics = hpa.status?.currentMetrics ?? [];
|
||||||
|
const cpuUtilization = hpa.spec?.targetCPUUtilizationPercentage;
|
||||||
|
|
||||||
|
if (cpuUtilization) {
|
||||||
|
const utilizationCurrent = hpa.status?.currentCPUUtilizationPercentage ? `${hpa.status.currentCPUUtilizationPercentage}%` : "unknown";
|
||||||
|
const utilizationTarget = cpuUtilization ? `${cpuUtilization}%` : "unknown";
|
||||||
|
|
||||||
|
return [`${utilizationCurrent} / ${utilizationTarget}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics.map((metric) => {
|
||||||
|
const currentMetric = currentMetrics.find(current =>
|
||||||
|
current.type === metric.type
|
||||||
|
&& getMetricName(current) === getMetricName(metric),
|
||||||
|
);
|
||||||
|
|
||||||
|
const h2Values = getMetricValues<HorizontalPodAutoscalerV2MetricParser>(hpaV2Parser, currentMetric, metric);
|
||||||
|
const h1Values = getMetricValues<HorizontalPodAutoscalerV1MetricParser>(hpaV1Parser, currentMetric, metric);
|
||||||
|
let values = h1Values;
|
||||||
|
|
||||||
|
if (h2Values.current || h2Values.target) {
|
||||||
|
values = h2Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${values.current ?? "unknown"} / ${values.target ?? "unknown"}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function getMetricValues<Type extends Parser>(parser: Type, current: HorizontalPodAutoscalerMetricStatus | undefined, target: HorizontalPodAutoscalerMetricSpec) {
|
||||||
|
switch (target.type) {
|
||||||
|
case HpaMetricType.Resource:
|
||||||
|
return parser.getResource({ current: current?.resource, target: target.resource });
|
||||||
|
case HpaMetricType.Pods:
|
||||||
|
return parser.getPods({ current: current?.pods, target: target.pods });
|
||||||
|
case HpaMetricType.Object:
|
||||||
|
return parser.getObject({ current: current?.object, target: target.object });
|
||||||
|
case HpaMetricType.External:
|
||||||
|
return parser.getExternal({ current: current?.external, target: target.external });
|
||||||
|
case HpaMetricType.ContainerResource:
|
||||||
|
return parser.getContainerResource({ current: current?.containerResource, target: target.containerResource });
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getHorizontalPodAutoscalerMetrics;
|
||||||
File diff suppressed because it is too large
Load Diff
@ -11,9 +11,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.metrics .Table {
|
.metrics .Table {
|
||||||
|
margin: 0 (-$margin * 3);
|
||||||
|
|
||||||
.TableCell {
|
.TableCell {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: $margin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: $margin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
&.name {
|
&.name {
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
|||||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
|
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
|
||||||
import loggerInjectable from "../../../common/logger.injectable";
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
|
import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable";
|
||||||
|
import { getMetricName } from "./get-hpa-metric-name";
|
||||||
|
|
||||||
export interface HpaDetailsProps extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
|
export interface HpaDetailsProps extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
|
||||||
}
|
}
|
||||||
@ -30,6 +32,7 @@ interface Dependencies {
|
|||||||
apiManager: ApiManager;
|
apiManager: ApiManager;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
getDetailsUrl: GetDetailsUrl;
|
getDetailsUrl: GetDetailsUrl;
|
||||||
|
getMetrics: (hpa: HorizontalPodAutoscaler) => string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -57,47 +60,46 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
|
|||||||
const { object: hpa } = this.props;
|
const { object: hpa } = this.props;
|
||||||
|
|
||||||
const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => {
|
const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => {
|
||||||
|
const metricName = getMetricName(metric);
|
||||||
|
|
||||||
switch (metric.type) {
|
switch (metric.type) {
|
||||||
case HpaMetricType.ContainerResource:
|
case HpaMetricType.ContainerResource:
|
||||||
|
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case HpaMetricType.Resource: {
|
case HpaMetricType.Resource: {
|
||||||
const metricSpec = metric.resource ?? metric.containerResource;
|
const metricSpec = metric.resource ?? metric.containerResource;
|
||||||
const addition = metricSpec.targetAverageUtilization
|
|
||||||
? " (as a percentage of request)"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return `Resource ${metricSpec.name} on Pods${addition}`;
|
return `Resource ${metricSpec.name} on Pods`;
|
||||||
}
|
}
|
||||||
case HpaMetricType.Pods:
|
case HpaMetricType.Pods:
|
||||||
return `${metric.pods.metricName} on Pods`;
|
return `${metricName} on Pods`;
|
||||||
|
|
||||||
case HpaMetricType.Object: {
|
case HpaMetricType.Object: {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{metric.object.metricName}
|
{metricName}
|
||||||
{" "}
|
{" "}
|
||||||
{this.renderTargetLink(metric.object.target)}
|
{this.renderTargetLink(metric.object?.describedObject)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case HpaMetricType.External:
|
case HpaMetricType.External:
|
||||||
return `${metric.external.metricName} on ${JSON.stringify(metric.external.metricSelector)}`;
|
return `${metricName} on ${JSON.stringify(metric.external.metricSelector ?? metric.external.metric?.selector)}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead flat>
|
||||||
<TableCell className="name">Name</TableCell>
|
<TableCell className="name">Name</TableCell>
|
||||||
<TableCell className="metrics">Current / Target</TableCell>
|
<TableCell className="metrics">Current / Target</TableCell>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
{
|
{
|
||||||
hpa.getMetrics()
|
this.props.getMetrics(hpa)
|
||||||
.map((metric, index) => (
|
.map((metrics, index) => (
|
||||||
<TableRow key={index}>
|
<TableRow key={index}>
|
||||||
<TableCell className="name">{renderName(metric)}</TableCell>
|
<TableCell className="name">{renderName(hpa.getMetrics()[index])}</TableCell>
|
||||||
<TableCell className="metrics">{hpa.getMetricValues(metric)}</TableCell>
|
<TableCell className="metrics">{metrics}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -175,5 +177,6 @@ export const HpaDetails = withInjectables<Dependencies, HpaDetailsProps>(NonInje
|
|||||||
apiManager: di.inject(apiManagerInjectable),
|
apiManager: di.inject(apiManagerInjectable),
|
||||||
getDetailsUrl: di.inject(getDetailsUrlInjectable),
|
getDetailsUrl: di.inject(getDetailsUrlInjectable),
|
||||||
logger: di.inject(loggerInjectable),
|
logger: di.inject(loggerInjectable),
|
||||||
|
getMetrics: di.inject(getHorizontalPodAutoscalerMetrics),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { MetricCurrentTarget, V2Beta1ContainerResourceMetricSource, V2Beta1ContainerResourceMetricStatus, V2Beta1ExternalMetricSource, V2Beta1ExternalMetricStatus, V2Beta1ObjectMetricSource, V2Beta1ObjectMetricStatus, V2Beta1PodsMetricSource, V2Beta1PodsMetricStatus, V2Beta1ResourceMetricSource, V2Beta1ResourceMetricStatus } from "../../../common/k8s-api/endpoints";
|
||||||
|
|
||||||
|
export class HorizontalPodAutoscalerV1MetricParser {
|
||||||
|
public getResource({ current, target }: { current: V2Beta1ResourceMetricStatus | undefined; target: V2Beta1ResourceMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
typeof current?.currentAverageUtilization === "number"
|
||||||
|
? `${current.currentAverageUtilization}%`
|
||||||
|
: current?.currentAverageValue
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
typeof target?.targetAverageUtilization === "number"
|
||||||
|
? `${target.targetAverageUtilization}%`
|
||||||
|
: target?.targetAverageValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPods({ current, target }: { current: V2Beta1PodsMetricStatus | undefined; target: V2Beta1PodsMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: current?.currentAverageValue,
|
||||||
|
target: target?.targetAverageValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getObject({ current, target }: { current: V2Beta1ObjectMetricStatus | undefined; target: V2Beta1ObjectMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
current?.currentValue
|
||||||
|
?? current?.averageValue
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
target?.targetValue
|
||||||
|
?? target?.averageValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getExternal({ current, target }: { current: V2Beta1ExternalMetricStatus | undefined; target: V2Beta1ExternalMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
current?.currentValue
|
||||||
|
?? current?.currentAverageValue
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
target?.targetValue
|
||||||
|
?? target?.targetAverageValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getContainerResource({ current, target }: { current: V2Beta1ContainerResourceMetricStatus | undefined; target: V2Beta1ContainerResourceMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
typeof current?.currentAverageUtilization === "number"
|
||||||
|
? `${current.currentAverageUtilization}%`
|
||||||
|
: current?.currentAverageValue
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
typeof target?.targetAverageUtilization === "number"
|
||||||
|
? `${target.targetAverageUtilization}%`
|
||||||
|
: target?.targetAverageValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { MetricCurrentTarget, V2ContainerResourceMetricSource, V2ContainerResourceMetricStatus, V2ExternalMetricSource, V2ExternalMetricStatus, V2ObjectMetricSource, V2ObjectMetricStatus, V2PodsMetricSource, V2PodsMetricStatus, V2ResourceMetricSource, V2ResourceMetricStatus } from "../../../common/k8s-api/endpoints";
|
||||||
|
|
||||||
|
export class HorizontalPodAutoscalerV2MetricParser {
|
||||||
|
public getResource({ current, target }: { current: V2ResourceMetricStatus | undefined; target: V2ResourceMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
typeof current?.current?.averageUtilization === "number"
|
||||||
|
? `${current.current?.averageUtilization}%`
|
||||||
|
: current?.current?.averageValue
|
||||||
|
),
|
||||||
|
target: typeof target?.target?.averageUtilization === "number"
|
||||||
|
? `${target.target.averageUtilization}%`
|
||||||
|
: target?.target?.averageValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPods({ current, target }: { current: V2PodsMetricStatus | undefined; target: V2PodsMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: current?.current?.averageValue,
|
||||||
|
target: target?.target?.averageValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getObject({ current, target }: { current: V2ObjectMetricStatus | undefined; target: V2ObjectMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
current?.current?.value
|
||||||
|
?? current?.current?.averageValue
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
target?.target?.value
|
||||||
|
?? target?.target?.averageValue
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getExternal({ current, target }: { current: V2ExternalMetricStatus | undefined; target: V2ExternalMetricSource }): MetricCurrentTarget {
|
||||||
|
const currentAverage = current?.current?.averageValue ? `${current?.current?.averageValue} (avg)` : undefined;
|
||||||
|
const targetAverage = target?.target?.averageValue ? `${target?.target?.averageValue} (avg)` : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
current?.current?.value
|
||||||
|
?? currentAverage
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
target?.target?.value
|
||||||
|
?? targetAverage
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getContainerResource({ current, target }: { current: V2ContainerResourceMetricStatus | undefined; target: V2ContainerResourceMetricSource }): MetricCurrentTarget {
|
||||||
|
return {
|
||||||
|
current: (
|
||||||
|
current?.current?.averageValue
|
||||||
|
?? current?.current?.averageUtilization ? `${current?.current?.averageUtilization}%` : undefined
|
||||||
|
),
|
||||||
|
target: (
|
||||||
|
target?.target?.averageValue
|
||||||
|
?? target?.target?.averageUtilization ? `${target?.target?.averageUtilization}%` : undefined
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ import { KubeObjectAge } from "../kube-object/age";
|
|||||||
import type { HorizontalPodAutoscalerStore } from "./store";
|
import type { HorizontalPodAutoscalerStore } from "./store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import horizontalPodAutoscalerStoreInjectable from "./store.injectable";
|
import horizontalPodAutoscalerStoreInjectable from "./store.injectable";
|
||||||
|
import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable";
|
||||||
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
|
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
|
||||||
|
|
||||||
enum columnId {
|
enum columnId {
|
||||||
@ -32,6 +33,7 @@ enum columnId {
|
|||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
horizontalPodAutoscalerStore: HorizontalPodAutoscalerStore;
|
horizontalPodAutoscalerStore: HorizontalPodAutoscalerStore;
|
||||||
|
getMetrics: (hpa: HorizontalPodAutoscaler) => string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -39,7 +41,7 @@ class NonInjectedHorizontalPodAutoscalers extends React.Component<Dependencies>
|
|||||||
getTargets(hpa: HorizontalPodAutoscaler) {
|
getTargets(hpa: HorizontalPodAutoscaler) {
|
||||||
const metrics = hpa.getMetrics();
|
const metrics = hpa.getMetrics();
|
||||||
|
|
||||||
if (metrics.length === 0) {
|
if (metrics.length === 0 && !hpa.spec?.targetCPUUtilizationPercentage) {
|
||||||
return <p>--</p>;
|
return <p>--</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ class NonInjectedHorizontalPodAutoscalers extends React.Component<Dependencies>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
{hpa.getMetricValues(metrics[0])}
|
{this.props.getMetrics(hpa)[0]}
|
||||||
{" "}
|
{" "}
|
||||||
{metricsRemain}
|
{metricsRemain}
|
||||||
</p>
|
</p>
|
||||||
@ -120,5 +122,6 @@ export const HorizontalPodAutoscalers = withInjectables<Dependencies>(NonInjecte
|
|||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
horizontalPodAutoscalerStore: di.inject(horizontalPodAutoscalerStoreInjectable),
|
horizontalPodAutoscalerStore: di.inject(horizontalPodAutoscalerStoreInjectable),
|
||||||
|
getMetrics: di.inject(getHorizontalPodAutoscalerMetrics),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -27,7 +27,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({
|
|||||||
|
|
||||||
export const isHorizontalPodAutoscaler = kubeObjectMatchesToKindAndApiVersion(
|
export const isHorizontalPodAutoscaler = kubeObjectMatchesToKindAndApiVersion(
|
||||||
"HorizontalPodAutoscaler",
|
"HorizontalPodAutoscaler",
|
||||||
["autoscaling/v2beta1"],
|
["autoscaling/v2", "autoscaling/v2beta2", "autoscaling/v2beta1", "autoscaling/v1"],
|
||||||
);
|
);
|
||||||
|
|
||||||
export default horizontalPodAutoscalerDetailItemInjectable;
|
export default horizontalPodAutoscalerDetailItemInjectable;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user