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:
parent
e3cdcdc826
commit
3686fa24e0
@ -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));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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}`
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()],
|
||||||
|
])));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user