mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Extract draft of cluster overview components of OpenLens from monolith to a Feature
Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
parent
d082a2766c
commit
5de8c1c14a
@ -1,161 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import styles from "../cluster-metrics.module.scss";
|
|
||||||
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import type { ChartOptions, ChartPoint } from "chart.js";
|
|
||||||
import type { ClusterOverviewStore } from "../cluster-overview-store/cluster-overview-store";
|
|
||||||
import { MetricType } from "../cluster-overview-store/cluster-overview-store";
|
|
||||||
import { BarChart } from "../../chart";
|
|
||||||
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
|
||||||
import { Spinner } from "../../spinner";
|
|
||||||
import { ZebraStripesPlugin } from "../../chart/zebra-stripes.plugin";
|
|
||||||
import { ClusterNoMetrics } from "../cluster-no-metrics";
|
|
||||||
import { ClusterMetricSwitchers } from "../cluster-metric-switchers";
|
|
||||||
import { getMetricLastPoints } from "../../../../common/k8s-api/endpoints/metrics.api";
|
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
|
||||||
import clusterOverviewStoreInjectable from "../cluster-overview-store/cluster-overview-store.injectable";
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import { clusterOverviewUIBlockInjectionToken } from "@k8slens/metrics";
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NonInjectedClusterMetrics = observer(
|
|
||||||
({
|
|
||||||
clusterOverviewStore: {
|
|
||||||
metricType,
|
|
||||||
metricNodeRole,
|
|
||||||
getMetricsValues,
|
|
||||||
metricsLoaded,
|
|
||||||
metrics,
|
|
||||||
},
|
|
||||||
}: Dependencies) => {
|
|
||||||
const [plugins] = useState([new ZebraStripesPlugin()]);
|
|
||||||
const { memoryCapacity, cpuCapacity } = getMetricLastPoints(metrics ?? {});
|
|
||||||
const metricValues = getMetricsValues(metrics ?? {});
|
|
||||||
const colors = { cpu: "#3D90CE", memory: "#C93DCE" };
|
|
||||||
const data = metricValues.map((value) => ({
|
|
||||||
x: value[0],
|
|
||||||
y: parseFloat(value[1]).toFixed(3),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const datasets = [
|
|
||||||
{
|
|
||||||
id: metricType + metricNodeRole,
|
|
||||||
label: `${metricType.toUpperCase()} usage`,
|
|
||||||
borderColor: colors[metricType],
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const cpuOptions: ChartOptions = {
|
|
||||||
scales: {
|
|
||||||
yAxes: [
|
|
||||||
{
|
|
||||||
ticks: {
|
|
||||||
suggestedMax: cpuCapacity,
|
|
||||||
callback: (value) => value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
callbacks: {
|
|
||||||
label: ({ index }, data) => {
|
|
||||||
if (!index) {
|
|
||||||
return "<unknown>";
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = data.datasets?.[0].data?.[index] as ChartPoint;
|
|
||||||
|
|
||||||
return value.y?.toString() ?? "<unknown>";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const memoryOptions: ChartOptions = {
|
|
||||||
scales: {
|
|
||||||
yAxes: [
|
|
||||||
{
|
|
||||||
ticks: {
|
|
||||||
suggestedMax: memoryCapacity,
|
|
||||||
callback: (value: string) =>
|
|
||||||
!value ? 0 : bytesToUnits(parseInt(value)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
callbacks: {
|
|
||||||
label: ({ index }, data) => {
|
|
||||||
if (!index) {
|
|
||||||
return "<unknown>";
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = data.datasets?.[0].data?.[index] as ChartPoint;
|
|
||||||
|
|
||||||
return bytesToUnits(parseInt(value.y as string), { precision: 3 });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const options = metricType === MetricType.CPU ? cpuOptions : memoryOptions;
|
|
||||||
|
|
||||||
const renderMetrics = () => {
|
|
||||||
if (!metricValues.length && !metricsLoaded) {
|
|
||||||
return <Spinner center />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memoryCapacity || !cpuCapacity) {
|
|
||||||
return <ClusterNoMetrics className={styles.empty} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BarChart
|
|
||||||
name={`${metricNodeRole}-${metricType}`}
|
|
||||||
options={options}
|
|
||||||
data={{ datasets }}
|
|
||||||
timeLabelStep={5}
|
|
||||||
showLegend={false}
|
|
||||||
plugins={plugins}
|
|
||||||
className={styles.chart}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={cssNames(styles.ClusterMetrics, "flex column")}>
|
|
||||||
<ClusterMetricSwitchers />
|
|
||||||
{renderMetrics()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const ClusterMetrics = withInjectables<Dependencies>(
|
|
||||||
NonInjectedClusterMetrics,
|
|
||||||
|
|
||||||
{
|
|
||||||
getProps: (di) => ({
|
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const clusterMetricsOverviewBlockInjectable = getInjectable({
|
|
||||||
id: "cluster-metrics-overview-block",
|
|
||||||
|
|
||||||
instantiate: () => ({
|
|
||||||
id: "cluster-metrics-overview-block",
|
|
||||||
Component: ClusterMetrics,
|
|
||||||
}),
|
|
||||||
|
|
||||||
injectionToken: clusterOverviewUIBlockInjectionToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default clusterMetricsOverviewBlockInjectable;
|
|
||||||
@ -1,322 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import styles from "../cluster-pie-charts.module.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import type { ClusterOverviewStore } from "../cluster-overview-store/cluster-overview-store";
|
|
||||||
import { MetricNodeRole } from "../cluster-overview-store/cluster-overview-store";
|
|
||||||
import { Spinner } from "../../spinner";
|
|
||||||
import { Icon } from "../../icon";
|
|
||||||
import type { NodeStore } from "../../+nodes/store";
|
|
||||||
import type { PieChartData } from "../../chart";
|
|
||||||
import { PieChart } from "../../chart";
|
|
||||||
import { ClusterNoMetrics } from "../cluster-no-metrics";
|
|
||||||
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
|
||||||
import type { LensTheme } from "../../../themes/lens-theme";
|
|
||||||
import { getMetricLastPoints } from "../../../../common/k8s-api/endpoints/metrics.api";
|
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
|
||||||
import clusterOverviewStoreInjectable from "../cluster-overview-store/cluster-overview-store.injectable";
|
|
||||||
import nodeStoreInjectable from "../../+nodes/store.injectable";
|
|
||||||
import type { IComputedValue } from "mobx";
|
|
||||||
import activeThemeInjectable from "../../../themes/active.injectable";
|
|
||||||
import type { ClusterMetricData } from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
|
||||||
import { clusterOverviewUIBlockInjectionToken } from "@k8slens/metrics";
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
|
|
||||||
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
|
||||||
return rawLabelData.map(
|
|
||||||
([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkedBytesToUnits = (value: number | undefined) =>
|
|
||||||
typeof value === "number" ? bytesToUnits(value) : "N/A";
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
|
||||||
nodeStore: NodeStore;
|
|
||||||
activeTheme: IComputedValue<LensTheme>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NonInjectedClusterPieCharts = observer(
|
|
||||||
({ clusterOverviewStore, nodeStore, activeTheme }: Dependencies) => {
|
|
||||||
const renderLimitWarning = () => {
|
|
||||||
return (
|
|
||||||
<div className="node-warning flex gaps align-center">
|
|
||||||
<Icon material="info" />
|
|
||||||
<p>Specified limits are higher than node capacity!</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderCharts = (
|
|
||||||
lastPoints: Partial<Record<keyof ClusterMetricData, number>>
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
memoryUsage,
|
|
||||||
memoryRequests,
|
|
||||||
memoryAllocatableCapacity,
|
|
||||||
memoryCapacity,
|
|
||||||
memoryLimits,
|
|
||||||
cpuUsage,
|
|
||||||
cpuRequests,
|
|
||||||
cpuAllocatableCapacity,
|
|
||||||
cpuCapacity,
|
|
||||||
cpuLimits,
|
|
||||||
podUsage,
|
|
||||||
podAllocatableCapacity,
|
|
||||||
podCapacity,
|
|
||||||
} = lastPoints;
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof cpuCapacity !== "number" ||
|
|
||||||
typeof cpuAllocatableCapacity !== "number" ||
|
|
||||||
typeof podCapacity !== "number" ||
|
|
||||||
typeof podAllocatableCapacity !== "number" ||
|
|
||||||
typeof memoryAllocatableCapacity !== "number" ||
|
|
||||||
typeof memoryCapacity !== "number" ||
|
|
||||||
typeof memoryUsage !== "number" ||
|
|
||||||
typeof memoryRequests !== "number"
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultColor = activeTheme.get().colors.pieChartDefaultColor;
|
|
||||||
|
|
||||||
const cpuData: PieChartData = {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: [cpuUsage, cpuUsage ? cpuAllocatableCapacity - cpuUsage : 1],
|
|
||||||
backgroundColor: ["#c93dce", defaultColor],
|
|
||||||
id: "cpuUsage",
|
|
||||||
label: "Usage",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
cpuRequests,
|
|
||||||
cpuRequests ? cpuAllocatableCapacity - cpuRequests : 1,
|
|
||||||
],
|
|
||||||
backgroundColor: ["#4caf50", defaultColor],
|
|
||||||
id: "cpuRequests",
|
|
||||||
label: "Requests",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
cpuLimits,
|
|
||||||
Math.max(
|
|
||||||
0,
|
|
||||||
cpuAllocatableCapacity - (cpuLimits ?? cpuAllocatableCapacity)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
backgroundColor: ["#3d90ce", defaultColor],
|
|
||||||
id: "cpuLimits",
|
|
||||||
label: "Limits",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labels: createLabels([
|
|
||||||
["Usage", cpuUsage],
|
|
||||||
["Requests", cpuRequests],
|
|
||||||
["Limits", cpuLimits],
|
|
||||||
["Allocatable Capacity", cpuAllocatableCapacity],
|
|
||||||
["Capacity", cpuCapacity],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
const memoryData: PieChartData = {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
memoryUsage,
|
|
||||||
memoryUsage ? memoryAllocatableCapacity - memoryUsage : 1,
|
|
||||||
],
|
|
||||||
backgroundColor: ["#c93dce", defaultColor],
|
|
||||||
id: "memoryUsage",
|
|
||||||
label: "Usage",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
memoryRequests,
|
|
||||||
memoryRequests ? memoryAllocatableCapacity - memoryRequests : 1,
|
|
||||||
],
|
|
||||||
backgroundColor: ["#4caf50", defaultColor],
|
|
||||||
id: "memoryRequests",
|
|
||||||
label: "Requests",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
memoryLimits,
|
|
||||||
Math.max(
|
|
||||||
0,
|
|
||||||
memoryAllocatableCapacity -
|
|
||||||
(memoryLimits ?? memoryAllocatableCapacity)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
backgroundColor: ["#3d90ce", defaultColor],
|
|
||||||
id: "memoryLimits",
|
|
||||||
label: "Limits",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labels: [
|
|
||||||
`Usage: ${bytesToUnits(memoryUsage)}`,
|
|
||||||
`Requests: ${bytesToUnits(memoryRequests)}`,
|
|
||||||
`Limits: ${checkedBytesToUnits(memoryLimits)}`,
|
|
||||||
`Allocatable Capacity: ${bytesToUnits(memoryAllocatableCapacity)}`,
|
|
||||||
`Capacity: ${bytesToUnits(memoryCapacity)}`,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const podsData: PieChartData = {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: [podUsage, podUsage ? podAllocatableCapacity - podUsage : 1],
|
|
||||||
backgroundColor: ["#4caf50", defaultColor],
|
|
||||||
id: "podUsage",
|
|
||||||
label: "Usage",
|
|
||||||
tooltipLabels: [
|
|
||||||
(percent) => `Usage: ${percent}`,
|
|
||||||
(percent) => `Available: ${percent}`,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labels: [
|
|
||||||
`Usage: ${podUsage || 0}`,
|
|
||||||
`Capacity: ${podAllocatableCapacity}`,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center box grow gaps">
|
|
||||||
<div
|
|
||||||
className={cssNames(
|
|
||||||
styles.chart,
|
|
||||||
"flex column align-center box grow"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<PieChart
|
|
||||||
data={cpuData}
|
|
||||||
title="CPU"
|
|
||||||
legendColors={[
|
|
||||||
"#c93dce",
|
|
||||||
"#4caf50",
|
|
||||||
"#3d90ce",
|
|
||||||
"#032b4d",
|
|
||||||
defaultColor,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{(cpuLimits ?? cpuAllocatableCapacity) > cpuAllocatableCapacity &&
|
|
||||||
renderLimitWarning()}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cssNames(
|
|
||||||
styles.chart,
|
|
||||||
"flex column align-center box grow"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<PieChart
|
|
||||||
data={memoryData}
|
|
||||||
title="Memory"
|
|
||||||
legendColors={[
|
|
||||||
"#c93dce",
|
|
||||||
"#4caf50",
|
|
||||||
"#3d90ce",
|
|
||||||
"#032b4d",
|
|
||||||
defaultColor,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{(memoryLimits ?? memoryAllocatableCapacity) >
|
|
||||||
memoryAllocatableCapacity && renderLimitWarning()}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cssNames(
|
|
||||||
styles.chart,
|
|
||||||
"flex column align-center box grow"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<PieChart
|
|
||||||
data={podsData}
|
|
||||||
title="Pods"
|
|
||||||
legendColors={["#4caf50", defaultColor]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContent = ({
|
|
||||||
metricNodeRole,
|
|
||||||
metrics,
|
|
||||||
}: ClusterOverviewStore) => {
|
|
||||||
const { masterNodes, workerNodes } = nodeStore;
|
|
||||||
const nodes =
|
|
||||||
metricNodeRole === MetricNodeRole.MASTER ? masterNodes : workerNodes;
|
|
||||||
|
|
||||||
if (!nodes.length) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cssNames(
|
|
||||||
styles.empty,
|
|
||||||
"flex column box grow align-center justify-center"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Icon material="info" />
|
|
||||||
No Nodes Available.
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!metrics) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cssNames(
|
|
||||||
styles.empty,
|
|
||||||
"flex justify-center align-center box grow"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastPoints = getMetricLastPoints(metrics);
|
|
||||||
const { memoryCapacity, cpuCapacity, podCapacity } = lastPoints;
|
|
||||||
|
|
||||||
if (!memoryCapacity || !cpuCapacity || !podCapacity) {
|
|
||||||
return (
|
|
||||||
<div className={styles.noMetrics}>
|
|
||||||
<ClusterNoMetrics className={styles.empty} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderCharts(lastPoints);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <div className="flex">{renderContent(clusterOverviewStore)}</div>;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const ClusterPieCharts = withInjectables<Dependencies>(
|
|
||||||
NonInjectedClusterPieCharts,
|
|
||||||
{
|
|
||||||
getProps: (di) => ({
|
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
|
||||||
activeTheme: di.inject(activeThemeInjectable),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const clusterPieChartsClusterOverviewInjectable = getInjectable({
|
|
||||||
id: "cluster-pie-charts-cluster-overview",
|
|
||||||
|
|
||||||
instantiate: (di) => ({
|
|
||||||
id: "cluster-pie-charts-cluster-overview",
|
|
||||||
Component: ClusterPieCharts,
|
|
||||||
}),
|
|
||||||
|
|
||||||
injectionToken: clusterOverviewUIBlockInjectionToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default clusterPieChartsClusterOverviewInjectable;
|
|
||||||
@ -24,8 +24,7 @@ runInAction(() => {
|
|||||||
registerMobX(di);
|
registerMobX(di);
|
||||||
registerInjectableReact(di);
|
registerInjectableReact(di);
|
||||||
registerLensCore(di, environment);
|
registerLensCore(di, environment);
|
||||||
registerFeature(di, applicationFeature);
|
registerFeature(di, applicationFeature, metricsFeature);
|
||||||
registerFeature(di, metricsFeature);
|
|
||||||
|
|
||||||
autoRegister({
|
autoRegister({
|
||||||
di,
|
di,
|
||||||
|
|||||||
@ -33,7 +33,9 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@k8slens/feature-core": "^6.5.0-alpha.0",
|
"@k8slens/feature-core": "^6.5.0-alpha.0",
|
||||||
"@ogre-tools/injectable": "^15.1.2"
|
"@ogre-tools/injectable": "^15.1.2",
|
||||||
|
"@ogre-tools/injectable-react": "^15.1.2",
|
||||||
|
"chart.js": "^2.9.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@k8slens/eslint-config": "6.5.0-alpha.1"
|
"@k8slens/eslint-config": "6.5.0-alpha.1"
|
||||||
|
|||||||
@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
// import styles from "./cluster-metrics.module.scss";
|
||||||
|
//
|
||||||
|
// import React, { useState } from "react";
|
||||||
|
// import { observer } from "mobx-react";
|
||||||
|
// import type { ChartOptions, ChartPoint } from "chart.js";
|
||||||
|
// import type { ClusterOverviewStore } from "../cluster-overview-store/cluster-overview-store";
|
||||||
|
// import { MetricType } from "../cluster-overview-store/cluster-overview-store";
|
||||||
|
// import { BarChart } from "../../chart";
|
||||||
|
// import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
||||||
|
// import { Spinner } from "../../spinner";
|
||||||
|
// import { ZebraStripesPlugin } from "../../chart/zebra-stripes.plugin";
|
||||||
|
// import { ClusterNoMetrics } from "../cluster-no-metrics";
|
||||||
|
// import { ClusterMetricSwitchers } from "../cluster-metric-switchers";
|
||||||
|
// import { getMetricLastPoints } from "../../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
// import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
// import clusterOverviewStoreInjectable from "../cluster-overview-store/cluster-overview-store.injectable";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { clusterOverviewUIBlockInjectionToken } from "../cluster-overview-ui-block";
|
||||||
|
|
||||||
|
// interface Dependencies {
|
||||||
|
// clusterOverviewStore: ClusterOverviewStore;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const NonInjectedClusterMetrics = observer(
|
||||||
|
// ({
|
||||||
|
// clusterOverviewStore: {
|
||||||
|
// metricType,
|
||||||
|
// metricNodeRole,
|
||||||
|
// getMetricsValues,
|
||||||
|
// metricsLoaded,
|
||||||
|
// metrics,
|
||||||
|
// },
|
||||||
|
// }: Dependencies) => {
|
||||||
|
// const [plugins] = useState([new ZebraStripesPlugin()]);
|
||||||
|
// const { memoryCapacity, cpuCapacity } = getMetricLastPoints(metrics ?? {});
|
||||||
|
// const metricValues = getMetricsValues(metrics ?? {});
|
||||||
|
// const colors = { cpu: "#3D90CE", memory: "#C93DCE" };
|
||||||
|
// const data = metricValues.map((value) => ({
|
||||||
|
// x: value[0],
|
||||||
|
// y: parseFloat(value[1]).toFixed(3),
|
||||||
|
// }));
|
||||||
|
//
|
||||||
|
// const datasets = [
|
||||||
|
// {
|
||||||
|
// id: metricType + metricNodeRole,
|
||||||
|
// label: `${metricType.toUpperCase()} usage`,
|
||||||
|
// borderColor: colors[metricType],
|
||||||
|
// data,
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
// const cpuOptions: ChartOptions = {
|
||||||
|
// scales: {
|
||||||
|
// yAxes: [
|
||||||
|
// {
|
||||||
|
// ticks: {
|
||||||
|
// suggestedMax: cpuCapacity,
|
||||||
|
// callback: (value) => value,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// tooltips: {
|
||||||
|
// callbacks: {
|
||||||
|
// label: ({ index }, data) => {
|
||||||
|
// if (!index) {
|
||||||
|
// return "<unknown>";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const value = data.datasets?.[0].data?.[index] as ChartPoint;
|
||||||
|
//
|
||||||
|
// return value.y?.toString() ?? "<unknown>";
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// const memoryOptions: ChartOptions = {
|
||||||
|
// scales: {
|
||||||
|
// yAxes: [
|
||||||
|
// {
|
||||||
|
// ticks: {
|
||||||
|
// suggestedMax: memoryCapacity,
|
||||||
|
// callback: (value: string) =>
|
||||||
|
// !value ? 0 : bytesToUnits(parseInt(value)),
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// tooltips: {
|
||||||
|
// callbacks: {
|
||||||
|
// label: ({ index }, data) => {
|
||||||
|
// if (!index) {
|
||||||
|
// return "<unknown>";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const value = data.datasets?.[0].data?.[index] as ChartPoint;
|
||||||
|
//
|
||||||
|
// return bytesToUnits(parseInt(value.y as string), { precision: 3 });
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// const options = metricType === MetricType.CPU ? cpuOptions : memoryOptions;
|
||||||
|
//
|
||||||
|
// const renderMetrics = () => {
|
||||||
|
// if (!metricValues.length && !metricsLoaded) {
|
||||||
|
// return <Spinner center />;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!memoryCapacity || !cpuCapacity) {
|
||||||
|
// return <ClusterNoMetrics className={styles.empty} />;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <BarChart
|
||||||
|
// name={`${metricNodeRole}-${metricType}`}
|
||||||
|
// options={options}
|
||||||
|
// data={{ datasets }}
|
||||||
|
// timeLabelStep={5}
|
||||||
|
// showLegend={false}
|
||||||
|
// plugins={plugins}
|
||||||
|
// className={styles.chart}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <div className={cssNames(styles.ClusterMetrics, "flex column")}>
|
||||||
|
// <ClusterMetricSwitchers />
|
||||||
|
// {renderMetrics()}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const ClusterMetrics = withInjectables<Dependencies>(
|
||||||
|
// NonInjectedClusterMetrics,
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// getProps: (di) => ({
|
||||||
|
// clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
const ClusterMetrics = () => <div>cluster-metrics.injectable</div>;
|
||||||
|
|
||||||
|
const clusterMetricsOverviewBlockInjectable = getInjectable({
|
||||||
|
id: "cluster-metrics-overview-block",
|
||||||
|
|
||||||
|
instantiate: () => ({
|
||||||
|
id: "cluster-metrics-overview-block",
|
||||||
|
Component: ClusterMetrics,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: clusterOverviewUIBlockInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterMetricsOverviewBlockInjectable;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ClusterMetrics {
|
||||||
|
position: relative;
|
||||||
|
min-height: 280px;
|
||||||
|
padding: calc(var(--margin) * 2);
|
||||||
|
background: var(--contentColor);
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
:global(.chart-container) {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,324 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// import styles from "./cluster-pie-charts.module.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
// import { observer } from "mobx-react";
|
||||||
|
// import type { ClusterOverviewStore } from "../cluster-overview-store/cluster-overview-store";
|
||||||
|
// import { MetricNodeRole } from "../cluster-overview-store/cluster-overview-store";
|
||||||
|
// import { Spinner } from "../../spinner";
|
||||||
|
// import { Icon } from "../../icon";
|
||||||
|
// import type { NodeStore } from "../../+nodes/store";
|
||||||
|
// import type { PieChartData } from "../../chart";
|
||||||
|
// import { PieChart } from "../../chart";
|
||||||
|
// import { ClusterNoMetrics } from "../cluster-no-metrics";
|
||||||
|
// import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
||||||
|
// import type { LensTheme } from "../../../themes/lens-theme";
|
||||||
|
// import { getMetricLastPoints } from "../../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
// import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
// import clusterOverviewStoreInjectable from "../cluster-overview-store/cluster-overview-store.injectable";
|
||||||
|
// import nodeStoreInjectable from "../../+nodes/store.injectable";
|
||||||
|
// import type { IComputedValue } from "mobx";
|
||||||
|
// import activeThemeInjectable from "../../../themes/active.injectable";
|
||||||
|
// import type { ClusterMetricData } from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { clusterOverviewUIBlockInjectionToken } from "../cluster-overview-ui-block";
|
||||||
|
|
||||||
|
// function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
||||||
|
// return rawLabelData.map(
|
||||||
|
// ([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const checkedBytesToUnits = (value: number | undefined) =>
|
||||||
|
// typeof value === "number" ? bytesToUnits(value) : "N/A";
|
||||||
|
//
|
||||||
|
// interface Dependencies {
|
||||||
|
// clusterOverviewStore: ClusterOverviewStore;
|
||||||
|
// nodeStore: NodeStore;
|
||||||
|
// activeTheme: IComputedValue<LensTheme>;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const NonInjectedClusterPieCharts = observer(
|
||||||
|
// ({ clusterOverviewStore, nodeStore, activeTheme }: Dependencies) => {
|
||||||
|
// const renderLimitWarning = () => {
|
||||||
|
// return (
|
||||||
|
// <div className="node-warning flex gaps align-center">
|
||||||
|
// <Icon material="info" />
|
||||||
|
// <p>Specified limits are higher than node capacity!</p>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const renderCharts = (
|
||||||
|
// lastPoints: Partial<Record<keyof ClusterMetricData, number>>
|
||||||
|
// ) => {
|
||||||
|
// const {
|
||||||
|
// memoryUsage,
|
||||||
|
// memoryRequests,
|
||||||
|
// memoryAllocatableCapacity,
|
||||||
|
// memoryCapacity,
|
||||||
|
// memoryLimits,
|
||||||
|
// cpuUsage,
|
||||||
|
// cpuRequests,
|
||||||
|
// cpuAllocatableCapacity,
|
||||||
|
// cpuCapacity,
|
||||||
|
// cpuLimits,
|
||||||
|
// podUsage,
|
||||||
|
// podAllocatableCapacity,
|
||||||
|
// podCapacity,
|
||||||
|
// } = lastPoints;
|
||||||
|
//
|
||||||
|
// if (
|
||||||
|
// typeof cpuCapacity !== "number" ||
|
||||||
|
// typeof cpuAllocatableCapacity !== "number" ||
|
||||||
|
// typeof podCapacity !== "number" ||
|
||||||
|
// typeof podAllocatableCapacity !== "number" ||
|
||||||
|
// typeof memoryAllocatableCapacity !== "number" ||
|
||||||
|
// typeof memoryCapacity !== "number" ||
|
||||||
|
// typeof memoryUsage !== "number" ||
|
||||||
|
// typeof memoryRequests !== "number"
|
||||||
|
// ) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const defaultColor = activeTheme.get().colors.pieChartDefaultColor;
|
||||||
|
//
|
||||||
|
// const cpuData: PieChartData = {
|
||||||
|
// datasets: [
|
||||||
|
// {
|
||||||
|
// data: [cpuUsage, cpuUsage ? cpuAllocatableCapacity - cpuUsage : 1],
|
||||||
|
// backgroundColor: ["#c93dce", defaultColor],
|
||||||
|
// id: "cpuUsage",
|
||||||
|
// label: "Usage",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: [
|
||||||
|
// cpuRequests,
|
||||||
|
// cpuRequests ? cpuAllocatableCapacity - cpuRequests : 1,
|
||||||
|
// ],
|
||||||
|
// backgroundColor: ["#4caf50", defaultColor],
|
||||||
|
// id: "cpuRequests",
|
||||||
|
// label: "Requests",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: [
|
||||||
|
// cpuLimits,
|
||||||
|
// Math.max(
|
||||||
|
// 0,
|
||||||
|
// cpuAllocatableCapacity - (cpuLimits ?? cpuAllocatableCapacity)
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// backgroundColor: ["#3d90ce", defaultColor],
|
||||||
|
// id: "cpuLimits",
|
||||||
|
// label: "Limits",
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// labels: createLabels([
|
||||||
|
// ["Usage", cpuUsage],
|
||||||
|
// ["Requests", cpuRequests],
|
||||||
|
// ["Limits", cpuLimits],
|
||||||
|
// ["Allocatable Capacity", cpuAllocatableCapacity],
|
||||||
|
// ["Capacity", cpuCapacity],
|
||||||
|
// ]),
|
||||||
|
// };
|
||||||
|
// const memoryData: PieChartData = {
|
||||||
|
// datasets: [
|
||||||
|
// {
|
||||||
|
// data: [
|
||||||
|
// memoryUsage,
|
||||||
|
// memoryUsage ? memoryAllocatableCapacity - memoryUsage : 1,
|
||||||
|
// ],
|
||||||
|
// backgroundColor: ["#c93dce", defaultColor],
|
||||||
|
// id: "memoryUsage",
|
||||||
|
// label: "Usage",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: [
|
||||||
|
// memoryRequests,
|
||||||
|
// memoryRequests ? memoryAllocatableCapacity - memoryRequests : 1,
|
||||||
|
// ],
|
||||||
|
// backgroundColor: ["#4caf50", defaultColor],
|
||||||
|
// id: "memoryRequests",
|
||||||
|
// label: "Requests",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: [
|
||||||
|
// memoryLimits,
|
||||||
|
// Math.max(
|
||||||
|
// 0,
|
||||||
|
// memoryAllocatableCapacity -
|
||||||
|
// (memoryLimits ?? memoryAllocatableCapacity)
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// backgroundColor: ["#3d90ce", defaultColor],
|
||||||
|
// id: "memoryLimits",
|
||||||
|
// label: "Limits",
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// labels: [
|
||||||
|
// `Usage: ${bytesToUnits(memoryUsage)}`,
|
||||||
|
// `Requests: ${bytesToUnits(memoryRequests)}`,
|
||||||
|
// `Limits: ${checkedBytesToUnits(memoryLimits)}`,
|
||||||
|
// `Allocatable Capacity: ${bytesToUnits(memoryAllocatableCapacity)}`,
|
||||||
|
// `Capacity: ${bytesToUnits(memoryCapacity)}`,
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// const podsData: PieChartData = {
|
||||||
|
// datasets: [
|
||||||
|
// {
|
||||||
|
// data: [podUsage, podUsage ? podAllocatableCapacity - podUsage : 1],
|
||||||
|
// backgroundColor: ["#4caf50", defaultColor],
|
||||||
|
// id: "podUsage",
|
||||||
|
// label: "Usage",
|
||||||
|
// tooltipLabels: [
|
||||||
|
// (percent) => `Usage: ${percent}`,
|
||||||
|
// (percent) => `Available: ${percent}`,
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// labels: [
|
||||||
|
// `Usage: ${podUsage || 0}`,
|
||||||
|
// `Capacity: ${podAllocatableCapacity}`,
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <div className="flex justify-center box grow gaps">
|
||||||
|
// <div
|
||||||
|
// className={cssNames(
|
||||||
|
// styles.chart,
|
||||||
|
// "flex column align-center box grow"
|
||||||
|
// )}
|
||||||
|
// >
|
||||||
|
// <PieChart
|
||||||
|
// data={cpuData}
|
||||||
|
// title="CPU"
|
||||||
|
// legendColors={[
|
||||||
|
// "#c93dce",
|
||||||
|
// "#4caf50",
|
||||||
|
// "#3d90ce",
|
||||||
|
// "#032b4d",
|
||||||
|
// defaultColor,
|
||||||
|
// ]}
|
||||||
|
// />
|
||||||
|
// {(cpuLimits ?? cpuAllocatableCapacity) > cpuAllocatableCapacity &&
|
||||||
|
// renderLimitWarning()}
|
||||||
|
// </div>
|
||||||
|
// <div
|
||||||
|
// className={cssNames(
|
||||||
|
// styles.chart,
|
||||||
|
// "flex column align-center box grow"
|
||||||
|
// )}
|
||||||
|
// >
|
||||||
|
// <PieChart
|
||||||
|
// data={memoryData}
|
||||||
|
// title="Memory"
|
||||||
|
// legendColors={[
|
||||||
|
// "#c93dce",
|
||||||
|
// "#4caf50",
|
||||||
|
// "#3d90ce",
|
||||||
|
// "#032b4d",
|
||||||
|
// defaultColor,
|
||||||
|
// ]}
|
||||||
|
// />
|
||||||
|
// {(memoryLimits ?? memoryAllocatableCapacity) >
|
||||||
|
// memoryAllocatableCapacity && renderLimitWarning()}
|
||||||
|
// </div>
|
||||||
|
// <div
|
||||||
|
// className={cssNames(
|
||||||
|
// styles.chart,
|
||||||
|
// "flex column align-center box grow"
|
||||||
|
// )}
|
||||||
|
// >
|
||||||
|
// <PieChart
|
||||||
|
// data={podsData}
|
||||||
|
// title="Pods"
|
||||||
|
// legendColors={["#4caf50", defaultColor]}
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const renderContent = ({
|
||||||
|
// metricNodeRole,
|
||||||
|
// metrics,
|
||||||
|
// }: ClusterOverviewStore) => {
|
||||||
|
// const { masterNodes, workerNodes } = nodeStore;
|
||||||
|
// const nodes =
|
||||||
|
// metricNodeRole === MetricNodeRole.MASTER ? masterNodes : workerNodes;
|
||||||
|
//
|
||||||
|
// if (!nodes.length) {
|
||||||
|
// return (
|
||||||
|
// <div
|
||||||
|
// className={cssNames(
|
||||||
|
// styles.empty,
|
||||||
|
// "flex column box grow align-center justify-center"
|
||||||
|
// )}
|
||||||
|
// >
|
||||||
|
// <Icon material="info" />
|
||||||
|
// No Nodes Available.
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!metrics) {
|
||||||
|
// return (
|
||||||
|
// <div
|
||||||
|
// className={cssNames(
|
||||||
|
// styles.empty,
|
||||||
|
// "flex justify-center align-center box grow"
|
||||||
|
// )}
|
||||||
|
// >
|
||||||
|
// <Spinner />
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const lastPoints = getMetricLastPoints(metrics);
|
||||||
|
// const { memoryCapacity, cpuCapacity, podCapacity } = lastPoints;
|
||||||
|
//
|
||||||
|
// if (!memoryCapacity || !cpuCapacity || !podCapacity) {
|
||||||
|
// return (
|
||||||
|
// <div className={styles.noMetrics}>
|
||||||
|
// <ClusterNoMetrics className={styles.empty} />
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return renderCharts(lastPoints);
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// return <div className="flex">{renderContent(clusterOverviewStore)}</div>;
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const ClusterPieCharts = withInjectables<Dependencies>(
|
||||||
|
// NonInjectedClusterPieCharts,
|
||||||
|
// {
|
||||||
|
// getProps: (di) => ({
|
||||||
|
// clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||||
|
// nodeStore: di.inject(nodeStoreInjectable),
|
||||||
|
// activeTheme: di.inject(activeThemeInjectable),
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
const ClusterPieCharts = () => <div>cluster-pie-charts.injectable</div>;
|
||||||
|
|
||||||
|
const clusterPieChartsClusterOverviewInjectable = getInjectable({
|
||||||
|
id: "cluster-pie-charts-cluster-overview",
|
||||||
|
|
||||||
|
instantiate: (di) => ({
|
||||||
|
id: "cluster-pie-charts-cluster-overview",
|
||||||
|
Component: ClusterPieCharts,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: clusterOverviewUIBlockInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterPieChartsClusterOverviewInjectable;
|
||||||
@ -1,7 +1,15 @@
|
|||||||
import { getFeature } from "@k8slens/feature-core";
|
import { getFeature } from "@k8slens/feature-core";
|
||||||
|
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
|
||||||
|
|
||||||
export const metricsFeature = getFeature({
|
export const metricsFeature = getFeature({
|
||||||
id: "metrics",
|
id: "metrics",
|
||||||
|
|
||||||
register: () => {},
|
register: (di) => {
|
||||||
|
autoRegister({
|
||||||
|
di,
|
||||||
|
targetModule: module,
|
||||||
|
|
||||||
|
getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)],
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user