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

Convert status injectables to be for a Map instead of object

- Allows us to use the data during further computations instead
  of doing it all again
- Large speed wins

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-03-09 15:03:18 -05:00
parent e3cdcdc826
commit 3686fa24e0
10 changed files with 101 additions and 89 deletions

View File

@ -4,16 +4,16 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable";
import daemonSetStoreInjectable from "./store.injectable"; import daemonSetStoreInjectable from "./store.injectable";
const totalStatusesForDaemonSetsInSelectedNamespacesInjectable = getInjectable({ const totalStatusesForDaemonSetsInSelectedNamespacesInjectable = getInjectable({
id: "total-statuses-for-daemon-sets-in-selected-namespaces", id: "total-statuses-for-daemon-sets-in-selected-namespaces",
instantiate: (di) => { instantiate: (di) => {
const daemonSetStore = di.inject(daemonSetStoreInjectable); const daemonSetStore = di.inject(daemonSetStoreInjectable);
const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable);
return computed(() => computeStatusCountsForOwners(daemonSetStore.contextItems)); return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(daemonSetStore.contextItems));
}, },
}); });

View File

@ -4,48 +4,19 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable from "../+workloads-replicasets/statuses-by-owner-id.injectable";
import type { Deployment, PodStatusPhase } from "../../../common/k8s-api/endpoints";
import { byLabels } from "../../../common/k8s-api/kube-object.store";
import { getOrInsert } from "../../utils";
import { foldPodStatusPhase } from "../../utils/fold-pod-status-phase";
import deploymentStoreInjectable from "./store.injectable"; import deploymentStoreInjectable from "./store.injectable";
const statusCountsForAllDeploymentsInSelectedNamespacesInjectable = getInjectable({ const statusCountsForAllDeploymentsInSelectedNamespacesInjectable = getInjectable({
id: "status-counts-for-all-deployments-in-selected-namespaces", id: "status-counts-for-all-deployments-in-selected-namespaces",
instantiate: (di) => { instantiate: (di) => {
const deploymentStore = di.inject(deploymentStoreInjectable); const deploymentStore = di.inject(deploymentStoreInjectable);
const podStore = di.inject(podStoreInjectable); const replicaSetStatuses = di.inject(statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable);
return computed(() => { return computed(() => new Map(deploymentStore.contextItems.map(deployment => [
const statuses = { running: 0, failed: 0, pending: 0 }; deployment.getId(),
const podsByNamespace = new Map<string, { metadata: { labels: Partial<Record<string, string>> }; status: PodStatusPhase }[]>(); replicaSetStatuses.get().get(deployment.getId()) ?? [],
])));
for (const pod of podStore.items) {
getOrInsert(podsByNamespace, pod.getNs(), []).push({
metadata: {
labels: JSON.parse(JSON.stringify(pod.metadata.labels ?? {})),
},
status: pod.getStatus(),
});
}
const getChildPods = (deployment: Deployment) => {
const pods = podsByNamespace.get(deployment.getNs()) ?? [];
return pods.filter(byLabels(deployment.spec.template.metadata.labels));
};
for (const deployment of deploymentStore.contextItems) {
const status = getChildPods(deployment)
.map(pod => pod.status)
.reduce(foldPodStatusPhase, "running");
statuses[status]++;
}
return statuses;
});
}, },
}); });

View File

@ -4,16 +4,16 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable";
import jobStoreInjectable from "./store.injectable"; import jobStoreInjectable from "./store.injectable";
const statusCountsForAllJobsInSelectedNamespacesInjectable = getInjectable({ const statusCountsForAllJobsInSelectedNamespacesInjectable = getInjectable({
id: "status-counts-for-all-jobs-in-selected-namespaces", id: "status-counts-for-all-jobs-in-selected-namespaces",
instantiate: (di) => { instantiate: (di) => {
const jobStore = di.inject(jobStoreInjectable); const jobStore = di.inject(jobStoreInjectable);
const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable);
return computed(() => computeStatusCountsForOwners(jobStore.contextItems)); return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(jobStore.contextItems));
}, },
}); });

View File

@ -10,17 +10,20 @@ import capitalize from "lodash/capitalize";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { PieChartData } from "../chart"; import type { PieChartData } from "../chart";
import { PieChart } from "../chart"; import { PieChart } from "../chart";
import { object } from "../../utils"; import { iter, object } from "../../utils";
import type { LensTheme } from "../../themes/lens-theme"; import type { LensTheme } from "../../themes/lens-theme";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { PascalCase } from "type-fest"; import type { PascalCase } from "type-fest";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import activeThemeInjectable from "../../themes/active.injectable"; import activeThemeInjectable from "../../themes/active.injectable";
import type { Workload } from "./workloads/workload-injection-token"; import type { Workload } from "./workloads/workload-injection-token";
import { foldWorkloadStatusPhase } from "../../utils/fold-workload-status-phase";
export type LowercaseOrPascalCase<T extends string> = Lowercase<T> | PascalCase<T>; export type LowercaseOrPascalCase<T extends string> = Lowercase<T> | PascalCase<T>;
export type WorkloadStatus = Partial<Record<LowercaseOrPascalCase<keyof typeof statusBackgroundColorMapping>, number>>; export type WorkloadStatusCounts = Partial<Record<LowercaseOrPascalCase<WorkloadStatus>, number>>;
export type WorkloadStatus = keyof typeof backgroundColourMapping;
function toLowercase<T extends string>(src: T): Lowercase<T> { function toLowercase<T extends string>(src: T): Lowercase<T> {
return src.toLowerCase() as Lowercase<T>; return src.toLowerCase() as Lowercase<T>;
@ -34,18 +37,24 @@ interface Dependencies {
activeTheme: IComputedValue<LensTheme>; activeTheme: IComputedValue<LensTheme>;
} }
const statusBackgroundColorMapping = { const cronJobStatusBackgroundColourMapping = {
"running": "colorOk",
"scheduled": "colorOk",
"pending": "colorWarning",
"suspended": "colorWarning", "suspended": "colorWarning",
"evicted": "colorError", "scheduled": "colorOk",
"succeeded": "colorSuccess", } as const;
const podStatusBackgroundColourMapping = {
"failed": "colorError", "failed": "colorError",
"evicted": "colorError",
"pending": "colorWarning",
"running": "colorOk",
"succeeded": "colorSuccess",
"terminated": "colorTerminated", "terminated": "colorTerminated",
"terminating": "colorTerminated",
"unknown": "colorVague", "unknown": "colorVague",
"complete": "colorSuccess", } as const;
const backgroundColourMapping = {
...podStatusBackgroundColourMapping,
...cronJobStatusBackgroundColourMapping,
} as const; } as const;
const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatusProps & Dependencies) => { const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatusProps & Dependencies) => {
@ -54,7 +63,12 @@ const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatu
activeTheme, activeTheme,
} = props; } = props;
const statusesToBeShown = object.entries(workload.status.get()).filter(([, val]) => val > 0); const statuses = workload.status.get();
const statusCounts = iter.chain(statuses.values())
.map(phases => phases.reduce(foldWorkloadStatusPhase, "unknown"))
.count();
const statusesToBeShown = object.entries(statusCounts).filter(([, val]) => val > 0);
const theme = activeTheme.get(); const theme = activeTheme.get();
const emptyDataSet = { const emptyDataSet = {
@ -66,7 +80,7 @@ const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatu
label: "Status", label: "Status",
data: statusesToBeShown.map(([, value]) => value), data: statusesToBeShown.map(([, value]) => value),
backgroundColor: statusesToBeShown.map(([status]) => ( backgroundColor: statusesToBeShown.map(([status]) => (
theme.colors[statusBackgroundColorMapping[toLowercase(status)]] theme.colors[backgroundColourMapping[toLowercase(status)]]
)), )),
tooltipLabels: statusesToBeShown.map(([status]) => ( tooltipLabels: statusesToBeShown.map(([status]) => (
(percent: string) => `${capitalize(status)}: ${percent}` (percent: string) => `${capitalize(status)}: ${percent}`

View File

@ -5,13 +5,22 @@
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import type { KubeApiResourceDescriptor } from "../../../../common/rbac"; import type { KubeApiResourceDescriptor } from "../../../../common/rbac";
import type { WorkloadStatus } from "../overview-workload-status";
export type WorkloadStatusPhase =
"Terminated"
| "Failed"
| "Pending"
| "Running"
| "Succeeded"
| "Evicted"
| "Suspended"
| "Scheduled";
export interface Workload { export interface Workload {
resource: KubeApiResourceDescriptor; resource: KubeApiResourceDescriptor;
open: () => void; open: () => void;
amountOfItems: IComputedValue<number>; amountOfItems: IComputedValue<number>;
status: IComputedValue<WorkloadStatus>; status: IComputedValue<Map<string, WorkloadStatusPhase[]>>;
title: string; title: string;
orderNumber: number; orderNumber: number;
} }

View File

@ -11,7 +11,10 @@ const totalStatusesForPodsInSelectedNamespacesInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const podStore = di.inject(podStoreInjectable); const podStore = di.inject(podStoreInjectable);
return computed(() => podStore.getStatuses(podStore.contextItems)); return computed(() => new Map(podStore.contextItems.map(pod => [
pod.getId(),
[pod.getStatus()],
])));
}, },
}); });

View File

@ -0,0 +1,32 @@
/**
* 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 { computed } from "mobx";
import type { PodStatusPhase } from "../../../common/k8s-api/endpoints";
import { getOrInsert } from "../../utils";
import statusCountsForAllReplicaSetsInSelectedNamespacesInjectable from "./statuses.injectable";
import replicaSetStoreInjectable from "./store.injectable";
const statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable = getInjectable({
id: "statuses-of-replica-sets-by-owner-ids-in-selected-namespaces",
instantiate: (di) => {
const replicaSetStore = di.inject(replicaSetStoreInjectable);
const statuses = di.inject(statusCountsForAllReplicaSetsInSelectedNamespacesInjectable);
return computed(() => {
const result = new Map<string, PodStatusPhase[]>();
for (const replicaSet of replicaSetStore.contextItems) {
for (const ownerRef of replicaSet.getOwnerRefs()) {
getOrInsert(result, ownerRef.uid, []).push(...statuses.get().get(replicaSet.getId()) ?? []);
}
}
return result;
});
},
});
export default statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable;

View File

@ -5,16 +5,16 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable";
import replicaSetStoreInjectable from "./store.injectable"; import replicaSetStoreInjectable from "./store.injectable";
const statusCountsForAllReplicaSetsInSelectedNamespacesInjectable = getInjectable({ const statusCountsForAllReplicaSetsInSelectedNamespacesInjectable = getInjectable({
id: "status-counts-for-all-replica-sets-in-selected-namespaces", id: "status-counts-for-all-replica-sets-in-selected-namespaces",
instantiate: (di) => { instantiate: (di) => {
const replicaSetStore = di.inject(replicaSetStoreInjectable); const replicaSetStore = di.inject(replicaSetStoreInjectable);
const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable);
return computed(() => computeStatusCountsForOwners(replicaSetStore.contextItems)); return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(replicaSetStore.contextItems));
}, },
}); });

View File

@ -4,16 +4,16 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable";
import statefulSetStoreInjectable from "./store.injectable"; import statefulSetStoreInjectable from "./store.injectable";
const totalStatusesForStatefulSetsInSelectedNamespacesInjectable = getInjectable({ const totalStatusesForStatefulSetsInSelectedNamespacesInjectable = getInjectable({
id: "total-statuses-for-stateful-sets-in-selected-namespaces", id: "total-statuses-for-stateful-sets-in-selected-namespaces",
instantiate: (di) => { instantiate: (di) => {
const statefulSetStore = di.inject(statefulSetStoreInjectable); const statefulSetStore = di.inject(statefulSetStoreInjectable);
const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable);
return computed(() => computeStatusCountsForOwners(statefulSetStore.contextItems)); return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(statefulSetStore.contextItems));
}, },
}); });

View File

@ -5,52 +5,35 @@
import type { PodStatusPhase } from "../../common/k8s-api/endpoints"; import type { PodStatusPhase } from "../../common/k8s-api/endpoints";
import { getOrInsert } from "../../common/utils"; import { getOrInsert } from "../../common/utils";
import { foldPodStatusPhase } from "./fold-pod-status-phase";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import podStoreInjectable from "../components/+workloads-pods/store.injectable"; import podStoreInjectable from "../components/+workloads-pods/store.injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import type { KubeObject } from "../../common/k8s-api/kube-object"; import type { KubeObject } from "../../common/k8s-api/kube-object";
export interface StatusCounts { const computeStatusesOfObjectsBasedOnOwnedPodsInjectable = getInjectable({
running: number; id: "compute-statuses-of-objects-based-on-owned-pods",
failed: number;
pending: number;
}
const computeStatusCountsForOwnersInjectable = getInjectable({
id: "compute-status-counts-for-owners",
instantiate: (di) => { instantiate: (di) => {
const podStore = di.inject(podStoreInjectable); const podStore = di.inject(podStoreInjectable);
const podsByOwnerId = computed(() => { const podStatusesByOwnerId = computed(() => {
const podsByOwnerId = new Map<string, ({ status: PodStatusPhase })[]>(); const podsByOwnerId = new Map<string, PodStatusPhase[]>();
for (const pod of podStore.contextItems) { for (const pod of podStore.contextItems) {
for (const ownerRef of pod.getOwnerRefs()) { for (const ownerRef of pod.getOwnerRefs()) {
getOrInsert(podsByOwnerId, ownerRef.uid, []).push({ getOrInsert(podsByOwnerId, ownerRef.uid, []).push(pod.getStatus());
status: pod.getStatus(),
});
} }
} }
return podsByOwnerId; return podsByOwnerId;
}); });
return (possibleOwners: KubeObject[]): StatusCounts => { return (owners: KubeObject[]) => new Map(owners.map(owner => {
const statuses = { running: 0, failed: 0, pending: 0 }; const statuses = podStatusesByOwnerId.get().get(owner.getId()) ?? [];
for (const possibleOwner of possibleOwners) { return [owner.getId(), statuses];
const status = (podsByOwnerId.get().get(possibleOwner.getId()) ?? []) }));
.map(pod => pod.status)
.reduce(foldPodStatusPhase, "running");
statuses[status]++;
}
return statuses;
};
}, },
}); });
export default computeStatusCountsForOwnersInjectable; export default computeStatusesOfObjectsBasedOnOwnedPodsInjectable;