mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fix: Switch to using IAsyncComputed to resolve bad setState error within ClusterOverview from react
- The ClusterOverviewStore (now just ClusterStore) is not an exported type, so removing the legacy and wrong-abstraction code from it is not a breaking change Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
4a7eff1841
commit
4f8e4707f9
@ -5,49 +5,39 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { NodeStore } from "../nodes/store";
|
|
||||||
import { Radio, RadioGroup } from "../radio";
|
import { Radio, RadioGroup } from "../radio";
|
||||||
import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store";
|
|
||||||
import { MetricNodeRole, MetricType } from "./cluster-overview-store/cluster-overview-store";
|
|
||||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import nodeStoreInjectable from "../nodes/store.injectable";
|
import type { SelectedMetricsType } from "./overview/selected-metrics-type.injectable";
|
||||||
import { normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import type { SelectedNodeRoleForMetrics } from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
import selectedMetricsTypeInjectable from "./overview/selected-metrics-type.injectable";
|
||||||
|
import selectedNodeRoleForMetricsInjectable from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
selectedMetricsType: SelectedMetricsType;
|
||||||
nodeStore: NodeStore;
|
selectedNodeRoleForMetrics: SelectedNodeRoleForMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedClusterMetricSwitchers = observer(({
|
const NonInjectedClusterMetricSwitchers = observer(({
|
||||||
clusterOverviewStore,
|
selectedMetricsType,
|
||||||
nodeStore,
|
selectedNodeRoleForMetrics,
|
||||||
}: Dependencies) => {
|
}: Dependencies) => (
|
||||||
const { masterNodes, workerNodes } = nodeStore;
|
|
||||||
const { cpuUsage, memoryUsage } = clusterOverviewStore.metrics ?? {};
|
|
||||||
const hasMasterNodes = masterNodes.length > 0;
|
|
||||||
const hasWorkerNodes = workerNodes.length > 0;
|
|
||||||
const hasCpuMetrics = normalizeMetrics(cpuUsage).data.result[0].values.length > 0;
|
|
||||||
const hasMemoryMetrics = normalizeMetrics(memoryUsage).data.result[0].values.length > 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex gaps" style={{ marginBottom: "calc(var(--margin) * 2)" }}>
|
<div className="flex gaps" style={{ marginBottom: "calc(var(--margin) * 2)" }}>
|
||||||
<div className="box grow">
|
<div className="box grow">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
asButtons
|
asButtons
|
||||||
className="RadioGroup flex gaps"
|
className="RadioGroup flex gaps"
|
||||||
value={clusterOverviewStore.metricNodeRole}
|
value={selectedNodeRoleForMetrics.value.get()}
|
||||||
onChange={metric => clusterOverviewStore.metricNodeRole = metric}
|
onChange={selectedNodeRoleForMetrics.set}
|
||||||
>
|
>
|
||||||
<Radio
|
<Radio
|
||||||
label="Master"
|
label="Master"
|
||||||
value={MetricNodeRole.MASTER}
|
value="master"
|
||||||
disabled={!hasMasterNodes}
|
disabled={!selectedNodeRoleForMetrics.hasMasterNodes.get()}
|
||||||
/>
|
/>
|
||||||
<Radio
|
<Radio
|
||||||
label="Worker"
|
label="Worker"
|
||||||
value={MetricNodeRole.WORKER}
|
value="worker"
|
||||||
disabled={!hasWorkerNodes}
|
disabled={!selectedNodeRoleForMetrics.hasWorkerNodes.get()}
|
||||||
/>
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
@ -55,29 +45,28 @@ const NonInjectedClusterMetricSwitchers = observer(({
|
|||||||
<RadioGroup
|
<RadioGroup
|
||||||
asButtons
|
asButtons
|
||||||
className="RadioGroup flex gaps"
|
className="RadioGroup flex gaps"
|
||||||
value={clusterOverviewStore.metricType}
|
value={selectedMetricsType.value.get()}
|
||||||
onChange={value => clusterOverviewStore.metricType = value}
|
onChange={selectedMetricsType.set}
|
||||||
>
|
>
|
||||||
<Radio
|
<Radio
|
||||||
label="CPU"
|
label="CPU"
|
||||||
value={MetricType.CPU}
|
value="cpu"
|
||||||
disabled={!hasCpuMetrics}
|
disabled={!selectedMetricsType.hasCPUMetrics.get()}
|
||||||
/>
|
/>
|
||||||
<Radio
|
<Radio
|
||||||
label="Memory"
|
label="Memory"
|
||||||
value={MetricType.MEMORY}
|
value="memory"
|
||||||
disabled={!hasMemoryMetrics}
|
disabled={!selectedMetricsType.hasMemoryMetrics.get()}
|
||||||
/>
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
));
|
||||||
});
|
|
||||||
|
|
||||||
export const ClusterMetricSwitchers = withInjectables<Dependencies>(NonInjectedClusterMetricSwitchers, {
|
export const ClusterMetricSwitchers = withInjectables<Dependencies>(NonInjectedClusterMetricSwitchers, {
|
||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
selectedMetricsType: di.inject(selectedMetricsTypeInjectable),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
selectedNodeRoleForMetrics: di.inject(selectedNodeRoleForMetricsInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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 { asyncComputed } from "@ogre-tools/injectable-react";
|
||||||
|
import { now } from "mobx-utils";
|
||||||
|
import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import requestClusterMetricsByNodeNamesInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import selectedNodeRoleForMetricsInjectable from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
|
||||||
|
const everyMinute = 60 * 1000;
|
||||||
|
|
||||||
|
const clusterOverviewMetricsInjectable = getInjectable({
|
||||||
|
id: "cluster-overview-metrics",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const requestClusterMetricsByNodeNames = di.inject(requestClusterMetricsByNodeNamesInjectable);
|
||||||
|
const selectedNodeRoleForMetrics = di.inject(selectedNodeRoleForMetricsInjectable);
|
||||||
|
|
||||||
|
return asyncComputed<ClusterMetricData | undefined>({
|
||||||
|
getValueFromObservedPromise: async () => {
|
||||||
|
now(everyMinute);
|
||||||
|
|
||||||
|
const nodeNames = selectedNodeRoleForMetrics
|
||||||
|
.nodes
|
||||||
|
.get()
|
||||||
|
.map(node => node.getName());
|
||||||
|
|
||||||
|
return requestClusterMetricsByNodeNames(nodeNames);
|
||||||
|
},
|
||||||
|
betweenUpdates: "show-latest-value",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterOverviewMetricsInjectable;
|
||||||
@ -8,8 +8,6 @@ import styles from "./cluster-metrics.module.scss";
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { ChartOptions, ChartPoint } from "chart.js";
|
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 { BarChart } from "../chart";
|
||||||
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
@ -17,17 +15,34 @@ import { ZebraStripesPlugin } from "../chart/zebra-stripes.plugin";
|
|||||||
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
||||||
import { ClusterMetricSwitchers } from "./cluster-metric-switchers";
|
import { ClusterMetricSwitchers } from "./cluster-metric-switchers";
|
||||||
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import type { SelectedMetricsType } from "./overview/selected-metrics-type.injectable";
|
||||||
|
import type { SelectedNodeRoleForMetrics } from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
import clusterOverviewMetricsInjectable from "./cluster-metrics.injectable";
|
||||||
|
import selectedMetricsTypeInjectable from "./overview/selected-metrics-type.injectable";
|
||||||
|
import selectedNodeRoleForMetricsInjectable from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
clusterOverviewMetrics: IAsyncComputed<ClusterMetricData | undefined>;
|
||||||
|
selectedMetricsType: SelectedMetricsType;
|
||||||
|
selectedNodeRoleForMetrics: SelectedNodeRoleForMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedClusterMetrics = observer(({ clusterOverviewStore: { metricType, metricNodeRole, getMetricsValues, metricsLoaded, metrics }}: Dependencies) => {
|
const NonInjectedClusterMetrics = observer((props: Dependencies) => {
|
||||||
|
const {
|
||||||
|
clusterOverviewMetrics,
|
||||||
|
selectedMetricsType,
|
||||||
|
selectedNodeRoleForMetrics,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const metrics = clusterOverviewMetrics.value.get();
|
||||||
const [plugins] = useState([new ZebraStripesPlugin()]);
|
const [plugins] = useState([new ZebraStripesPlugin()]);
|
||||||
const { memoryCapacity, cpuCapacity } = getMetricLastPoints(metrics ?? {});
|
const { memoryCapacity, cpuCapacity } = getMetricLastPoints(metrics ?? {});
|
||||||
const metricValues = getMetricsValues(metrics ?? {});
|
const metricValues = selectedMetricsType.metrics.get();
|
||||||
|
const metricType = selectedMetricsType.value.get();
|
||||||
|
const metricNodeRole = selectedNodeRoleForMetrics.value.get();
|
||||||
const colors = { cpu: "#3D90CE", memory: "#C93DCE" };
|
const colors = { cpu: "#3D90CE", memory: "#C93DCE" };
|
||||||
const data = metricValues.map(value => ({
|
const data = metricValues.map(value => ({
|
||||||
x: value[0],
|
x: value[0],
|
||||||
@ -86,10 +101,10 @@ const NonInjectedClusterMetrics = observer(({ clusterOverviewStore: { metricType
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const options = metricType === MetricType.CPU ? cpuOptions : memoryOptions;
|
const options = metricType === "cpu" ? cpuOptions : memoryOptions;
|
||||||
|
|
||||||
const renderMetrics = () => {
|
const renderMetrics = () => {
|
||||||
if (!metricValues.length && !metricsLoaded) {
|
if (!metricValues.length && !metrics) {
|
||||||
return <Spinner center/>;
|
return <Spinner center/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,12 +133,10 @@ const NonInjectedClusterMetrics = observer(({ clusterOverviewStore: { metricType
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ClusterMetrics = withInjectables<Dependencies>(
|
export const ClusterMetrics = withInjectables<Dependencies>(NonInjectedClusterMetrics, {
|
||||||
NonInjectedClusterMetrics,
|
|
||||||
|
|
||||||
{
|
|
||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
clusterOverviewMetrics: di.inject(clusterOverviewMetricsInjectable),
|
||||||
|
selectedMetricsType: di.inject(selectedMetricsTypeInjectable),
|
||||||
|
selectedNodeRoleForMetrics: di.inject(selectedNodeRoleForMetricsInjectable),
|
||||||
}),
|
}),
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 { ClusterOverviewStorageState } from "./cluster-overview-store";
|
|
||||||
import { ClusterOverviewStore, MetricNodeRole, MetricType } from "./cluster-overview-store";
|
|
||||||
import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
|
|
||||||
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
|
|
||||||
import clusterApiInjectable from "../../../../common/k8s-api/endpoints/cluster.api.injectable";
|
|
||||||
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
|
|
||||||
import assert from "assert";
|
|
||||||
import nodeStoreInjectable from "../../nodes/store.injectable";
|
|
||||||
import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
|
||||||
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
|
|
||||||
import { loggerInjectionToken } from "@k8slens/logger";
|
|
||||||
|
|
||||||
const clusterOverviewStoreInjectable = getInjectable({
|
|
||||||
id: "cluster-overview-store",
|
|
||||||
|
|
||||||
instantiate: (di) => {
|
|
||||||
assert(di.inject(storesAndApisCanBeCreatedInjectable), "clusterOverviewStore is only available in certain environments");
|
|
||||||
const createStorage = di.inject(createStorageInjectable);
|
|
||||||
const clusterApi = di.inject(clusterApiInjectable);
|
|
||||||
|
|
||||||
return new ClusterOverviewStore({
|
|
||||||
storage: createStorage<ClusterOverviewStorageState>(
|
|
||||||
"cluster_overview",
|
|
||||||
{
|
|
||||||
metricType: MetricType.CPU, // setup defaults
|
|
||||||
metricNodeRole: MetricNodeRole.WORKER,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
|
||||||
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
|
|
||||||
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
|
||||||
logger: di.inject(loggerInjectionToken),
|
|
||||||
}, clusterApi);
|
|
||||||
},
|
|
||||||
injectionToken: kubeObjectStoreInjectionToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default clusterOverviewStoreInjectable;
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { action, observable, reaction, when, makeObservable, runInAction } from "mobx";
|
|
||||||
import type { KubeObjectStoreDependencies } from "../../../../common/k8s-api/kube-object.store";
|
|
||||||
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
|
|
||||||
import type { ClusterApi } from "../../../../common/k8s-api/endpoints";
|
|
||||||
import type { Cluster } from "@k8slens/kube-object";
|
|
||||||
import type { StorageLayer } from "../../../utils/storage-helper";
|
|
||||||
import type { NodeStore } from "../../nodes/store";
|
|
||||||
import type { ClusterMetricData, RequestClusterMetricsByNodeNames } from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
|
||||||
import type { RequestMetricsParams } from "../../../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable";
|
|
||||||
import { normalizeMetrics } from "../../../../common/k8s-api/endpoints/metrics.api";
|
|
||||||
import autoBind from "auto-bind";
|
|
||||||
|
|
||||||
export enum MetricType {
|
|
||||||
MEMORY = "memory",
|
|
||||||
CPU = "cpu",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MetricNodeRole {
|
|
||||||
MASTER = "master",
|
|
||||||
WORKER = "worker",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClusterOverviewStorageState {
|
|
||||||
metricType: MetricType;
|
|
||||||
metricNodeRole: MetricNodeRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ClusterOverviewStoreDependencies extends KubeObjectStoreDependencies {
|
|
||||||
readonly storage: StorageLayer<ClusterOverviewStorageState>;
|
|
||||||
readonly nodeStore: NodeStore;
|
|
||||||
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterOverviewStore extends KubeObjectStore<Cluster, ClusterApi> implements ClusterOverviewStorageState {
|
|
||||||
@observable metrics: ClusterMetricData | undefined = undefined;
|
|
||||||
|
|
||||||
get metricsLoaded() {
|
|
||||||
return !!this.metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
get metricType(): MetricType {
|
|
||||||
return this.dependencies.storage.get().metricType;
|
|
||||||
}
|
|
||||||
|
|
||||||
set metricType(value: MetricType) {
|
|
||||||
this.dependencies.storage.merge({ metricType: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
get metricNodeRole(): MetricNodeRole {
|
|
||||||
return this.dependencies.storage.get().metricNodeRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
set metricNodeRole(value: MetricNodeRole) {
|
|
||||||
this.dependencies.storage.merge({ metricNodeRole: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(protected readonly dependencies: ClusterOverviewStoreDependencies, api: ClusterApi) {
|
|
||||||
super(dependencies, api);
|
|
||||||
makeObservable(this);
|
|
||||||
autoBind(this);
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private init() {
|
|
||||||
// TODO: refactor, seems not a correct place to be
|
|
||||||
// auto-refresh metrics on user-action
|
|
||||||
reaction(() => this.metricNodeRole, () => {
|
|
||||||
if (this.metrics) {
|
|
||||||
this.resetMetrics();
|
|
||||||
void this.loadMetrics();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// check which node type to select
|
|
||||||
reaction(() => this.dependencies.nodeStore.items.length, () => {
|
|
||||||
const { masterNodes, workerNodes } = this.dependencies.nodeStore;
|
|
||||||
|
|
||||||
if (!masterNodes.length) this.metricNodeRole = MetricNodeRole.WORKER;
|
|
||||||
if (!workerNodes.length) this.metricNodeRole = MetricNodeRole.MASTER;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadMetrics(params?: RequestMetricsParams) {
|
|
||||||
await when(() => this.dependencies.nodeStore.isLoaded);
|
|
||||||
const { masterNodes, workerNodes } = this.dependencies.nodeStore;
|
|
||||||
const nodes = this.metricNodeRole === MetricNodeRole.MASTER && masterNodes.length ? masterNodes : workerNodes;
|
|
||||||
|
|
||||||
const metrics = await this.dependencies.requestClusterMetricsByNodeNames(nodes.map(node => node.getName()), params);
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
this.metrics = metrics;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getMetricsValues(source: Partial<ClusterMetricData>): [number, string][] {
|
|
||||||
switch (this.metricType) {
|
|
||||||
case MetricType.CPU:
|
|
||||||
return normalizeMetrics(source.cpuUsage).data.result[0].values;
|
|
||||||
case MetricType.MEMORY:
|
|
||||||
return normalizeMetrics(source.memoryUsage).data.result[0].values;
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
resetMetrics() {
|
|
||||||
this.metrics = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
super.reset();
|
|
||||||
this.resetMetrics();
|
|
||||||
this.dependencies.storage?.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,19 +7,16 @@ import styles from "./cluster-overview.module.scss";
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
import { reaction } from "mobx";
|
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import type { NodeStore } from "../nodes/store";
|
import type { NodeStore } from "../nodes/store";
|
||||||
import type { PodStore } from "../workloads-pods/store";
|
import type { PodStore } from "../workloads-pods/store";
|
||||||
import { interval } from "@k8slens/utilities";
|
|
||||||
import { TabLayout } from "../layout/tab-layout";
|
import { TabLayout } from "../layout/tab-layout";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { ClusterIssues } from "./cluster-issues";
|
import { ClusterIssues } from "./cluster-issues";
|
||||||
import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store";
|
|
||||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||||
import type { EventStore } from "../events/store";
|
import type { EventStore } from "../events/store";
|
||||||
|
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
|
||||||
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||||
import podStoreInjectable from "../workloads-pods/store.injectable";
|
import podStoreInjectable from "../workloads-pods/store.injectable";
|
||||||
@ -30,84 +27,61 @@ import type { ClusterOverviewUIBlock } from "@k8slens/metrics";
|
|||||||
import { clusterOverviewUIBlockInjectionToken } from "@k8slens/metrics";
|
import { clusterOverviewUIBlockInjectionToken } from "@k8slens/metrics";
|
||||||
import { orderByOrderNumber } from "../../../common/utils/composable-responsibilities/orderable/orderable";
|
import { orderByOrderNumber } from "../../../common/utils/composable-responsibilities/orderable/orderable";
|
||||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||||
|
import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import clusterOverviewMetricsInjectable from "./cluster-metrics.injectable";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
subscribeStores: SubscribeStores;
|
subscribeStores: SubscribeStores;
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
|
||||||
podStore: PodStore;
|
podStore: PodStore;
|
||||||
eventStore: EventStore;
|
eventStore: EventStore;
|
||||||
nodeStore: NodeStore;
|
nodeStore: NodeStore;
|
||||||
clusterMetricsAreVisible: IComputedValue<boolean>;
|
clusterMetricsAreVisible: IComputedValue<boolean>;
|
||||||
uiBlocks: IComputedValue<ClusterOverviewUIBlock[]>;
|
uiBlocks: IComputedValue<ClusterOverviewUIBlock[]>;
|
||||||
|
clusterOverviewMetrics: IAsyncComputed<ClusterMetricData | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||||
private readonly metricPoller = interval(60, async () => {
|
|
||||||
try {
|
|
||||||
await this.props.clusterOverviewStore.loadMetrics();
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.metricPoller.start(true);
|
|
||||||
|
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
this.props.subscribeStores([
|
this.props.subscribeStores([
|
||||||
this.props.podStore,
|
this.props.podStore,
|
||||||
this.props.eventStore,
|
this.props.eventStore,
|
||||||
this.props.nodeStore,
|
this.props.nodeStore,
|
||||||
]),
|
]),
|
||||||
|
|
||||||
reaction(
|
|
||||||
() => this.props.clusterOverviewStore.metricNodeRole, // Toggle Master/Worker node switcher
|
|
||||||
() => this.metricPoller.restart(true),
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
renderWithMetrics() {
|
||||||
this.metricPoller.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMetrics(isMetricsHidden: boolean) {
|
|
||||||
if (isMetricsHidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{orderByOrderNumber(this.props.uiBlocks.get()).map((block) => (
|
{
|
||||||
|
orderByOrderNumber(this.props.uiBlocks.get())
|
||||||
|
.map((block) => (
|
||||||
<block.Component key={block.id} />
|
<block.Component key={block.id} />
|
||||||
))}
|
))
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
<ClusterIssues />
|
||||||
renderClusterOverview(isLoaded: boolean, isMetricsHidden: boolean) {
|
|
||||||
if (!isLoaded) {
|
|
||||||
return <Spinner center/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.renderMetrics(isMetricsHidden)}
|
|
||||||
<ClusterIssues className={isMetricsHidden ? "OnlyClusterIssues" : ""}/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { eventStore, nodeStore, clusterMetricsAreVisible } = this.props;
|
const { eventStore, nodeStore, clusterMetricsAreVisible } = this.props;
|
||||||
const isLoaded = nodeStore.isLoaded && eventStore.isLoaded;
|
|
||||||
const isMetricsHidden = !clusterMetricsAreVisible.get();
|
const isMetricsHidden = !clusterMetricsAreVisible.get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabLayout scrollable>
|
<TabLayout scrollable>
|
||||||
<div className={styles.ClusterOverview} data-testid="cluster-overview-page">
|
<div className={styles.ClusterOverview} data-testid="cluster-overview-page">
|
||||||
{this.renderClusterOverview(isLoaded, isMetricsHidden)}
|
{
|
||||||
|
(!nodeStore.isLoaded || !eventStore.isLoaded)
|
||||||
|
? <Spinner center/>
|
||||||
|
: (
|
||||||
|
isMetricsHidden
|
||||||
|
? <ClusterIssues className="OnlyClusterIssues"/>
|
||||||
|
: this.renderWithMetrics()
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</TabLayout>
|
</TabLayout>
|
||||||
);
|
);
|
||||||
@ -117,11 +91,11 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
|||||||
export const ClusterOverview = withInjectables<Dependencies>(NonInjectedClusterOverview, {
|
export const ClusterOverview = withInjectables<Dependencies>(NonInjectedClusterOverview, {
|
||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
|
||||||
clusterMetricsAreVisible: di.inject(enabledMetricsInjectable, ClusterMetricsResourceType.Cluster),
|
clusterMetricsAreVisible: di.inject(enabledMetricsInjectable, ClusterMetricsResourceType.Cluster),
|
||||||
podStore: di.inject(podStoreInjectable),
|
podStore: di.inject(podStoreInjectable),
|
||||||
eventStore: di.inject(eventStoreInjectable),
|
eventStore: di.inject(eventStoreInjectable),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
uiBlocks: di.inject(computedInjectManyInjectable)(clusterOverviewUIBlockInjectionToken),
|
uiBlocks: di.inject(computedInjectManyInjectable)(clusterOverviewUIBlockInjectionToken),
|
||||||
|
clusterOverviewMetrics: di.inject(clusterOverviewMetricsInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,23 +7,24 @@ import styles from "./cluster-pie-charts.module.scss";
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-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 { Spinner } from "../spinner";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import type { NodeStore } from "../nodes/store";
|
|
||||||
import type { PieChartData } from "../chart";
|
import type { PieChartData } from "../chart";
|
||||||
import { PieChart } from "../chart";
|
import { PieChart } from "../chart";
|
||||||
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
||||||
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
import { bytesToUnits, cssNames } from "@k8slens/utilities";
|
||||||
import type { LensTheme } from "../../themes/lens-theme";
|
import type { LensTheme } from "../../themes/lens-theme";
|
||||||
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
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 type { IComputedValue } from "mobx";
|
||||||
import activeThemeInjectable from "../../themes/active.injectable";
|
import activeThemeInjectable from "../../themes/active.injectable";
|
||||||
import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import { isNumber } from "lodash";
|
||||||
|
import type { SelectedNodeRoleForMetrics } from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
import type { Node } from "@k8slens/kube-object";
|
||||||
|
import clusterOverviewMetricsInjectable from "./cluster-metrics.injectable";
|
||||||
|
import selectedNodeRoleForMetricsInjectable from "./overview/selected-node-role-for-metrics.injectable";
|
||||||
|
|
||||||
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
||||||
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
||||||
@ -36,26 +37,19 @@ const checkedBytesToUnits = (value: number | undefined) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
selectedNodeRoleForMetrics: SelectedNodeRoleForMetrics;
|
||||||
nodeStore: NodeStore;
|
clusterOverviewMetrics: IAsyncComputed<ClusterMetricData | undefined>;
|
||||||
activeTheme: IComputedValue<LensTheme>;
|
activeTheme: IComputedValue<LensTheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedClusterPieCharts = observer(({
|
const renderLimitWarning = () => (
|
||||||
clusterOverviewStore,
|
|
||||||
nodeStore,
|
|
||||||
activeTheme,
|
|
||||||
}: Dependencies) => {
|
|
||||||
const renderLimitWarning = () => {
|
|
||||||
return (
|
|
||||||
<div className="node-warning flex gaps align-center">
|
<div className="node-warning flex gaps align-center">
|
||||||
<Icon material="info"/>
|
<Icon material="info" />
|
||||||
<p>Specified limits are higher than node capacity!</p>
|
<p>Specified limits are higher than node capacity!</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const renderCharts = (lastPoints: Partial<Record<keyof ClusterMetricData, number>>) => {
|
const renderCharts = (defaultColor: string, lastPoints: Partial<Record<keyof ClusterMetricData, number>>) => {
|
||||||
const {
|
const {
|
||||||
memoryUsage, memoryRequests, memoryAllocatableCapacity, memoryCapacity, memoryLimits,
|
memoryUsage, memoryRequests, memoryAllocatableCapacity, memoryCapacity, memoryLimits,
|
||||||
cpuUsage, cpuRequests, cpuAllocatableCapacity, cpuCapacity, cpuLimits,
|
cpuUsage, cpuRequests, cpuAllocatableCapacity, cpuCapacity, cpuLimits,
|
||||||
@ -63,20 +57,18 @@ const NonInjectedClusterPieCharts = observer(({
|
|||||||
} = lastPoints;
|
} = lastPoints;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof cpuCapacity !== "number" ||
|
!isNumber(cpuCapacity) ||
|
||||||
typeof cpuAllocatableCapacity !== "number" ||
|
!isNumber(cpuAllocatableCapacity) ||
|
||||||
typeof podCapacity !== "number" ||
|
!isNumber(podCapacity) ||
|
||||||
typeof podAllocatableCapacity !== "number" ||
|
!isNumber(podAllocatableCapacity) ||
|
||||||
typeof memoryAllocatableCapacity !== "number" ||
|
!isNumber(memoryAllocatableCapacity) ||
|
||||||
typeof memoryCapacity !== "number" ||
|
!isNumber(memoryCapacity) ||
|
||||||
typeof memoryUsage !== "number" ||
|
!isNumber(memoryUsage) ||
|
||||||
typeof memoryRequests !== "number"
|
!isNumber(memoryRequests)
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultColor = activeTheme.get().colors.pieChartDefaultColor;
|
|
||||||
|
|
||||||
const cpuData: PieChartData = {
|
const cpuData: PieChartData = {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
@ -235,12 +227,9 @@ const NonInjectedClusterPieCharts = observer(({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderContent = ({ metricNodeRole, metrics }: ClusterOverviewStore) => {
|
|
||||||
const { masterNodes, workerNodes } = nodeStore;
|
|
||||||
const nodes = metricNodeRole === MetricNodeRole.MASTER ? masterNodes : workerNodes;
|
|
||||||
|
|
||||||
|
const renderContent = (defaultColor: string, nodes: Node[], metrics: ClusterMetricData | undefined) => {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return (
|
return (
|
||||||
<div className={cssNames(styles.empty, "flex column box grow align-center justify-center")}>
|
<div className={cssNames(styles.empty, "flex column box grow align-center justify-center")}>
|
||||||
@ -269,20 +258,27 @@ const NonInjectedClusterPieCharts = observer(({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderCharts(lastPoints);
|
return renderCharts(defaultColor, lastPoints);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const NonInjectedClusterPieCharts = observer(({
|
||||||
|
selectedNodeRoleForMetrics,
|
||||||
|
clusterOverviewMetrics,
|
||||||
|
activeTheme,
|
||||||
|
}: Dependencies) => (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{renderContent(clusterOverviewStore)}
|
{renderContent(
|
||||||
|
activeTheme.get().colors.pieChartDefaultColor,
|
||||||
|
selectedNodeRoleForMetrics.nodes.get(),
|
||||||
|
clusterOverviewMetrics.value.get(),
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
));
|
||||||
});
|
|
||||||
|
|
||||||
export const ClusterPieCharts = withInjectables<Dependencies>(NonInjectedClusterPieCharts, {
|
export const ClusterPieCharts = withInjectables<Dependencies>(NonInjectedClusterPieCharts, {
|
||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
|
||||||
activeTheme: di.inject(activeThemeInjectable),
|
activeTheme: di.inject(activeThemeInjectable),
|
||||||
|
clusterOverviewMetrics: di.inject(clusterOverviewMetricsInjectable),
|
||||||
|
selectedNodeRoleForMetrics: di.inject(selectedNodeRoleForMetricsInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action, computed } from "mobx";
|
||||||
|
import { normalizeMetrics } from "../../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
import clusterOverviewMetricsInjectable from "../cluster-metrics.injectable";
|
||||||
|
import type { MetricType } from "./storage.injectable";
|
||||||
|
import clusterOverviewStorageInjectable from "./storage.injectable";
|
||||||
|
|
||||||
|
export type SelectedMetricsType = ReturnType<typeof selectedMetricsTypeInjectable["instantiate"]>;
|
||||||
|
|
||||||
|
const selectedMetricsTypeInjectable = getInjectable({
|
||||||
|
id: "selected-metrics-type",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const storage = di.inject(clusterOverviewStorageInjectable);
|
||||||
|
const overviewMetrics = di.inject(clusterOverviewMetricsInjectable);
|
||||||
|
|
||||||
|
const value = computed(() => storage.get().metricType);
|
||||||
|
const metrics = computed((): [number, string][] => {
|
||||||
|
const rawValue = overviewMetrics.value.get();
|
||||||
|
|
||||||
|
if (!rawValue) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = value.get();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "cpu":
|
||||||
|
return normalizeMetrics(rawValue.cpuUsage).data.result[0].values;
|
||||||
|
case "memory":
|
||||||
|
return normalizeMetrics(rawValue.memoryUsage).data.result[0].values;
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const hasCPUMetrics = computed(() => (
|
||||||
|
normalizeMetrics(overviewMetrics.value.get()?.cpuUsage).data.result[0].values.length > 0
|
||||||
|
));
|
||||||
|
const hasMemoryMetrics = computed(() => (
|
||||||
|
normalizeMetrics(overviewMetrics.value.get()?.memoryUsage).data.result[0].values.length > 0
|
||||||
|
));
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
metrics,
|
||||||
|
hasCPUMetrics,
|
||||||
|
hasMemoryMetrics,
|
||||||
|
set: action((value: MetricType) => {
|
||||||
|
storage.merge({ metricType: value });
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default selectedMetricsTypeInjectable;
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action, computed } from "mobx";
|
||||||
|
import nodeStoreInjectable from "../../nodes/store.injectable";
|
||||||
|
import type { MetricNodeRole } from "./storage.injectable";
|
||||||
|
import clusterOverviewStorageInjectable from "./storage.injectable";
|
||||||
|
|
||||||
|
export type SelectedNodeRoleForMetrics = ReturnType<typeof selectedNodeRoleForMetricsInjectable["instantiate"]>;
|
||||||
|
|
||||||
|
const selectedNodeRoleForMetricsInjectable = getInjectable({
|
||||||
|
id: "selected-node-role-for-metrics",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const storage = di.inject(clusterOverviewStorageInjectable);
|
||||||
|
const nodeStore = di.inject(nodeStoreInjectable);
|
||||||
|
|
||||||
|
const value = computed(() => {
|
||||||
|
const { masterNodes, workerNodes } = nodeStore;
|
||||||
|
const rawValue = storage.get().metricNodeRole;
|
||||||
|
|
||||||
|
const hasMasterNodes = masterNodes.length > 0;
|
||||||
|
const hasWorkerNodes = workerNodes.length > 0;
|
||||||
|
|
||||||
|
if (hasMasterNodes && !hasWorkerNodes && rawValue === "worker") {
|
||||||
|
return "master";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMasterNodes && hasWorkerNodes && rawValue === "master") {
|
||||||
|
return "worker";
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
const nodes = computed(() => {
|
||||||
|
const { masterNodes, workerNodes } = nodeStore;
|
||||||
|
const role = value.get();
|
||||||
|
|
||||||
|
if (role === "master") {
|
||||||
|
return masterNodes.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
return workerNodes.slice();
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasMasterNodes = computed(() => nodeStore.masterNodes.length > 0);
|
||||||
|
const hasWorkerNodes = computed(() => nodeStore.workerNodes.length > 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
nodes,
|
||||||
|
hasMasterNodes,
|
||||||
|
hasWorkerNodes,
|
||||||
|
set: action((value: MetricNodeRole) => {
|
||||||
|
storage.merge({ metricNodeRole: value });
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default selectedNodeRoleForMetricsInjectable;
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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 createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
|
||||||
|
|
||||||
|
export type MetricType = "memory" | "cpu";
|
||||||
|
export type MetricNodeRole = "master" | "worker";
|
||||||
|
|
||||||
|
export interface ClusterOverviewStorageState {
|
||||||
|
metricType: MetricType;
|
||||||
|
metricNodeRole: MetricNodeRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clusterOverviewStorageInjectable = getInjectable({
|
||||||
|
id: "cluster-overview-storage",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createStorage = di.inject(createStorageInjectable);
|
||||||
|
|
||||||
|
return createStorage<ClusterOverviewStorageState>(
|
||||||
|
"cluster_overview",
|
||||||
|
{
|
||||||
|
metricType: "cpu", // setup defaults
|
||||||
|
metricNodeRole: "worker",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterOverviewStorageInjectable;
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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 { ClusterStore } from "./store";
|
||||||
|
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
|
||||||
|
import clusterApiInjectable from "../../../common/k8s-api/endpoints/cluster.api.injectable";
|
||||||
|
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
import { loggerInjectionToken } from "@k8slens/logger";
|
||||||
|
|
||||||
|
const clusterStoreInjectable = getInjectable({
|
||||||
|
id: "cluster-store",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
assert(di.inject(storesAndApisCanBeCreatedInjectable), "clusterStore is only available in certain environments");
|
||||||
|
const clusterApi = di.inject(clusterApiInjectable);
|
||||||
|
|
||||||
|
return new ClusterStore({
|
||||||
|
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
|
logger: di.inject(loggerInjectionToken),
|
||||||
|
}, clusterApi);
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectStoreInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterStoreInjectable;
|
||||||
11
packages/core/src/renderer/components/cluster/store.ts
Normal file
11
packages/core/src/renderer/components/cluster/store.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||||
|
import type { ClusterApi } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { Cluster } from "@k8slens/kube-object";
|
||||||
|
|
||||||
|
export class ClusterStore extends KubeObjectStore<Cluster, ClusterApi> {
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user