- {renderContent(clusterOverviewStore)}
+
+
+
+ {((cpuLimits ?? cpuAllocatableCapacity) > cpuAllocatableCapacity) && renderLimitWarning()}
+
+
+
+ {((memoryLimits ?? memoryAllocatableCapacity) > memoryAllocatableCapacity) && renderLimitWarning()}
+
+
);
-});
+};
+
+const renderContent = (defaultColor: string, nodes: Node[], metrics: ClusterMetricData | undefined) => {
+ if (!nodes.length) {
+ return (
+
+
+ No Nodes Available.
+
+ );
+ }
+
+ if (!metrics) {
+ return (
+
+
+
+ );
+ }
+
+ const lastPoints = getMetricLastPoints(metrics);
+ const { memoryCapacity, cpuCapacity, podCapacity } = lastPoints;
+
+ if (!memoryCapacity || !cpuCapacity || !podCapacity) {
+ return (
+
+
+
+ );
+ }
+
+ return renderCharts(defaultColor, lastPoints);
+};
+
+const NonInjectedClusterPieCharts = observer(({
+ selectedNodeRoleForMetrics,
+ clusterOverviewMetrics,
+ activeTheme,
+}: Dependencies) => (
+
+ {renderContent(
+ activeTheme.get().colors.pieChartDefaultColor,
+ selectedNodeRoleForMetrics.nodes.get(),
+ clusterOverviewMetrics.value.get(),
+ )}
+
+));
export const ClusterPieCharts = withInjectables
(NonInjectedClusterPieCharts, {
getProps: (di) => ({
- clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
- nodeStore: di.inject(nodeStoreInjectable),
activeTheme: di.inject(activeThemeInjectable),
+ clusterOverviewMetrics: di.inject(clusterOverviewMetricsInjectable),
+ selectedNodeRoleForMetrics: di.inject(selectedNodeRoleForMetricsInjectable),
}),
});
diff --git a/packages/core/src/renderer/components/cluster/overview/selected-metrics-type.injectable.ts b/packages/core/src/renderer/components/cluster/overview/selected-metrics-type.injectable.ts
new file mode 100644
index 0000000000..2eb768dec4
--- /dev/null
+++ b/packages/core/src/renderer/components/cluster/overview/selected-metrics-type.injectable.ts
@@ -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;
+
+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;
diff --git a/packages/core/src/renderer/components/cluster/overview/selected-node-role-for-metrics.injectable.ts b/packages/core/src/renderer/components/cluster/overview/selected-node-role-for-metrics.injectable.ts
new file mode 100644
index 0000000000..50026be985
--- /dev/null
+++ b/packages/core/src/renderer/components/cluster/overview/selected-node-role-for-metrics.injectable.ts
@@ -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;
+
+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;
diff --git a/packages/core/src/renderer/components/cluster/overview/storage.injectable.ts b/packages/core/src/renderer/components/cluster/overview/storage.injectable.ts
new file mode 100644
index 0000000000..91a5d6420e
--- /dev/null
+++ b/packages/core/src/renderer/components/cluster/overview/storage.injectable.ts
@@ -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(
+ "cluster_overview",
+ {
+ metricType: "cpu", // setup defaults
+ metricNodeRole: "worker",
+ },
+ );
+ },
+});
+
+export default clusterOverviewStorageInjectable;
diff --git a/packages/core/src/renderer/components/cluster/store.injectable.ts b/packages/core/src/renderer/components/cluster/store.injectable.ts
new file mode 100644
index 0000000000..cf0ce6888b
--- /dev/null
+++ b/packages/core/src/renderer/components/cluster/store.injectable.ts
@@ -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;
diff --git a/packages/core/src/renderer/components/cluster/store.ts b/packages/core/src/renderer/components/cluster/store.ts
new file mode 100644
index 0000000000..748cbf82ea
--- /dev/null
+++ b/packages/core/src/renderer/components/cluster/store.ts
@@ -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 {
+}