From 83d2b12cca4e82cbc3a0a614f9348b15668c23d2 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Dec 2022 07:14:00 -0800 Subject: [PATCH] Refactor kube detail components and add KubeEvents automatically to all custom resources (#6468) * Support Events on CustomResourece details panel Signed-off-by: Sebastian Malton * Remove unnecessary check Signed-off-by: Sebastian Malton * Remove legacy KubeObjectMeta use from non-metrics details Signed-off-by: Sebastian Malton * Change orderNumber of default KubeMetaDetails Signed-off-by: Sebastian Malton * Remove last use of legacy global getActiveClusterEntity Signed-off-by: Sebastian Malton * Refactor ResourceMetrics to use IAsyncComputed - Introduce first use for namespace metrics Signed-off-by: Sebastian Malton * Introduce metrics details item for Ingress Signed-off-by: Sebastian Malton * Remove legacyStore for nodes Signed-off-by: Sebastian Malton * Remove legacyStore for ingresses Signed-off-by: Sebastian Malton * Replace NodeMetrics in details with injectable Signed-off-by: Sebastian Malton * Change namespace metrics details to use more injectables Signed-off-by: Sebastian Malton * Change ingress metrics details to use more injectables Signed-off-by: Sebastian Malton * Change PersistentVolumeClaim metrics to be injectable Signed-off-by: Sebastian Malton * Change DaemonSet metrics to be injectable Signed-off-by: Sebastian Malton * Change Deployment metrics to be injectable Signed-off-by: Sebastian Malton * Change Job metrics to be injectable Signed-off-by: Sebastian Malton * Convert Pod metrics to be injectable Signed-off-by: Sebastian Malton * Make ReplicaSet metrics details injectable Signed-off-by: Sebastian Malton * Make StatefulSet metrics component injectable Signed-off-by: Sebastian Malton * Remove dead code Signed-off-by: Sebastian Malton * Introduce fix for metrics components visible on all details Signed-off-by: Sebastian Malton * Use the new and correct enabled check Signed-off-by: Sebastian Malton * Remove legacy global for daemonSetStore Signed-off-by: Sebastian Malton * Switch components to be static to help with React performance Signed-off-by: Sebastian Malton * Remove legacy store of CronJobs Signed-off-by: Sebastian Malton * Make DeploymentReplicaSets injectable to fix build error Signed-off-by: Sebastian Malton * Fix up remove dead code Signed-off-by: Sebastian Malton * Allow use of ResourceMetrics without IAsyncComputed Signed-off-by: Sebastian Malton * Fix metrics not updating correctly Signed-off-by: Sebastian Malton * Update snapshots because moving KubeObjectMeta out of CustomResourceDetails Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton * Update more snapshots Signed-off-by: Sebastian Malton * Fix test failures due to newer dep versions Signed-off-by: Sebastian Malton * Fix type errors from new asyncComputed Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- ...request-pod-metrics-for-jobs.injectable.ts | 2 +- src/common/utils/sort-function.ts | 16 ++ ...when-cluster-is-not-relevant.test.tsx.snap | 70 ++++++++ ...tems-when-cluster-is-not-relevant.test.tsx | 16 +- ...vely-hide-kube-object-detail-item.test.tsx | 16 +- .../api/catalog/entity/legacy-globals.ts | 12 -- ...cs-details-component-enabled.injectable.ts | 32 ++++ .../entity/metrics-enabled.injectable.ts | 22 +++ .../+config-autoscalers/hpa-details.tsx | 3 - .../+config-leases/lease-details.tsx | 3 - .../limit-range-details.tsx | 3 - .../+config-maps/config-map-details.tsx | 2 - .../pod-disruption-budgets-details.tsx | 3 - .../priority-classes-details.tsx | 3 - .../resource-quota-details.tsx | 3 - .../runtime-classes-details.tsx | 3 - .../+config-secrets/secret-details.tsx | 2 - .../custom-resource-details.test.tsx.snap | 116 ------------- .../+custom-resources/crd-details.tsx | 3 - .../crd-resource-details.tsx | 4 +- .../+custom-resources/definition.store.ts | 1 - .../components/+events/event-details.tsx | 3 - src/renderer/components/+events/store.ts | 4 +- .../metrics-details-component.injectable.tsx | 53 ++++++ .../+namespaces/metrics.injectable.ts | 29 ++++ .../+namespaces/namespace-details.tsx | 46 +---- .../+network-endpoints/endpoint-details.tsx | 2 - .../+network-ingresses/ingress-details.tsx | 55 +----- .../+network-ingresses/ingresses.tsx | 149 ++++++++-------- .../metrics-details-component.injectable.tsx | 56 ++++++ .../+network-ingresses/metrics.injectable.ts | 29 ++++ .../network-policy-details.tsx | 3 - .../+network-services/service-details.tsx | 3 - src/renderer/components/+nodes/details.tsx | 55 +----- .../components/+nodes/legacy-store.ts | 12 -- .../metrics-details-component.injectable.tsx | 58 +++++++ .../components/+nodes/metrics.injectable.ts | 29 ++++ .../pod-security-policy-details.tsx | 3 - .../storage-class-details.tsx | 3 - .../metrics-details-component.injectable.tsx | 55 ++++++ .../metrics.injectable.ts | 29 ++++ .../volume-claim-details.tsx | 65 ++----- .../+storage-volumes/volume-details.tsx | 2 - .../+cluster-role-bindings/details.tsx | 3 - .../+cluster-roles/details.tsx | 3 - .../+role-bindings/details.tsx | 3 - .../+user-management/+roles/details.tsx | 2 - .../+service-accounts/details.tsx | 3 - .../+workloads-cronjobs/cronjob-details.tsx | 2 - .../+workloads-cronjobs/cronjobs.tsx | 130 +++++++------- .../daemonset-details.tsx | 45 +---- .../+workloads-daemonsets/daemonsets.tsx | 126 +++++++------- .../metrics-details-component.injectable.tsx | 53 ++++++ .../metrics.injectable.ts | 29 ++++ .../deployment-details.tsx | 55 +----- .../deployment-replicasets.tsx | 14 +- .../metrics-details-component.injectable.tsx | 53 ++++++ .../metrics.injectable.ts | 29 ++++ .../+workloads-jobs/job-details.tsx | 48 +----- .../components/+workloads-jobs/jobs.tsx | 115 ++++++------- .../metrics-details-component.injectable.tsx | 52 ++++++ .../+workloads-jobs/metrics.injectable.ts | 29 ++++ .../container-metrics.injectable.ts | 29 ++++ .../metrics-detail-container.injectable.tsx | 52 ++++++ .../+workloads-pods/metrics.injectable.ts | 29 ++++ .../+workloads-pods/pod-details.tsx | 75 ++------ .../metrics-details-component.injectable.tsx | 52 ++++++ .../metrics.injectable.ts | 29 ++++ .../replicaset-details.tsx | 51 +----- .../+workloads-replicasets/replicasets.tsx | 111 ++++++------ .../+workloads-replicasets/store.ts | 4 +- .../metrics-details-component.injectable.tsx | 52 ++++++ .../metrics.injectable.ts | 29 ++++ .../statefulset-details.tsx | 50 +----- .../+workloads-statefulsets/statefulsets.tsx | 116 ++++++------- ...rrent-kube-object-in-details.injectable.ts | 35 ++-- ...custom-resource-detail-item.injectable.tsx | 37 ++++ ...fault-kube-meta-details-item.injectable.ts | 20 +++ ...ter-role-binding-detail-item.injectable.ts | 2 +- .../cluster-role-detail-item.injectable.ts | 2 +- .../config-map-detail-item.injectable.ts | 2 +- .../cron-job-detail-item.injectable.ts | 2 +- ...urce-definitions-detail-item.injectable.ts | 2 +- .../daemon-set-detail-item.injectable.ts | 2 +- .../deployment-detail-item.injectable.ts | 2 +- .../endpoints-detail-item.injectable.ts | 2 +- .../events-detail-item.injectable.ts | 2 +- ...l-pod-autoscaler-detail-item.injectable.ts | 2 +- .../ingress-detail-item.injectable.ts | 2 +- .../job-detail-item.injectable.ts | 2 +- .../kube-event-detail-item.injectable.ts | 72 +------- .../lease-detail-item.injectable.ts | 2 +- .../limit-range-detail-item.injectable.ts | 2 +- .../namespaces-detail-item.injectable.ts | 2 +- .../network-policy-detail-item.injectable.ts | 2 +- .../node-detail-item.injectable.ts | 2 +- ...ent-volume-claim-detail-item.injectable.ts | 2 +- ...ersistent-volume-detail-item.injectable.ts | 2 +- .../pod-detail-item.injectable.ts | 2 +- ...isruption-budget-detail-item.injectable.ts | 2 +- ...-security-policy-detail-item.injectable.ts | 2 +- .../priority-class-detail-item.injectable.ts | 2 +- .../replica-set-detail-item.injectable.ts | 2 +- .../resource-quota-detail-item.injectable.ts | 2 +- .../role-binding-detail-item.injectable.ts | 2 +- .../role-detail-item.injectable.ts | 2 +- .../runtime-class-detail-item.injectable.ts | 2 +- .../secrets-detail-item.injectable.ts | 2 +- .../service-account-detail-item.injectable.ts | 2 +- .../service-detail-item.injectable.ts | 2 +- .../stateful-set-detail-item.injectable.ts | 2 +- .../storage-class-detail-item.injectable.ts | 2 +- ...ject-detail-item-registrator.injectable.ts | 2 +- .../kube-object-detail-items.injectable.ts | 17 +- .../kube-object-details.tsx | 162 +++++------------- .../components/resource-metrics/index.ts | 1 - .../resource-metrics-text.tsx | 46 ----- .../resource-metrics/resource-metrics.tsx | 95 +++++----- 118 files changed, 1664 insertions(+), 1440 deletions(-) create mode 100644 src/common/utils/sort-function.ts delete mode 100644 src/renderer/api/catalog/entity/legacy-globals.ts create mode 100644 src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts create mode 100644 src/renderer/api/catalog/entity/metrics-enabled.injectable.ts create mode 100644 src/renderer/components/+namespaces/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+namespaces/metrics.injectable.ts create mode 100644 src/renderer/components/+network-ingresses/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+network-ingresses/metrics.injectable.ts delete mode 100644 src/renderer/components/+nodes/legacy-store.ts create mode 100644 src/renderer/components/+nodes/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+nodes/metrics.injectable.ts create mode 100644 src/renderer/components/+storage-volume-claims/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+storage-volume-claims/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+workloads-daemonsets/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+workloads-deployments/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+workloads-jobs/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-pods/container-metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx create mode 100644 src/renderer/components/+workloads-pods/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+workloads-replicasets/metrics.injectable.ts create mode 100644 src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx create mode 100644 src/renderer/components/+workloads-statefulsets/metrics.injectable.ts create mode 100644 src/renderer/components/kube-object-details/custom-resource-detail-item.injectable.tsx create mode 100644 src/renderer/components/kube-object-details/default-kube-meta-details-item.injectable.ts delete mode 100644 src/renderer/components/resource-metrics/resource-metrics-text.tsx diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts index a812fae59c..d52f028cf0 100644 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts +++ b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts @@ -24,7 +24,7 @@ const requestPodMetricsForJobsInjectable = getInjectable({ instantiate: (di): RequestPodMetricsForJobs => { const requestMetrics = di.inject(requestMetricsInjectable); - return (jobs, namespace, selector) => { + return (jobs, namespace, selector = "") => { const podSelector = jobs.map(job => `${job.getName()}-[[:alnum:]]{5}`).join("|"); const opts = { category: "pods", pods: podSelector, namespace, selector }; diff --git a/src/common/utils/sort-function.ts b/src/common/utils/sort-function.ts new file mode 100644 index 0000000000..4eeab57746 --- /dev/null +++ b/src/common/utils/sort-function.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +/** + * Get an ordering function based on the function getter + */ +export function byValue(getOrderValue: (src: T) => number): (left: T, right: T) => number { + return (left, right) => { + const leftValue = getOrderValue(left); + const rightValue = getOrderValue(right); + + return leftValue - rightValue; + }; +} diff --git a/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap b/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap index 0221082e02..fb177c820c 100644 --- a/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap +++ b/src/features/cluster/kube-object-details/extension-api/__snapshots__/disable-kube-object-detail-items-when-cluster-is-not-relevant.test.tsx.snap @@ -302,11 +302,63 @@ exports[`disable kube object detail items when cluster is not relevant given ext
+
+ + Created + + + <unknown> + ago + +
+
+ + Name + + + some-name + +
+
+ + Namespace + + + some-namespace + +
Some detail
+
+
+ + Events + +
+
+
+
+ + Events + +
+
+
+
+ + Events + +
+
{ let builder: ApplicationBuilder; @@ -32,16 +33,11 @@ describe("disable kube object detail items when cluster is not relevant", () => builder.setEnvironmentToClusterFrame(); builder.beforeWindowStart((windowDi) => { - windowDi.override( - apiManagerInjectable, - () => - ({ - getStore: () => ({ - getByPath: () => - getKubeObjectStub("some-kind", "some-api-version"), - }), - } as unknown as ApiManager), - ); + windowDi.override(apiManagerInjectable, () => ({ + getStore: () => ({ + loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"), + }) as Partial as KubeObjectStore, + }) as Partial as ApiManager); windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); diff --git a/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx b/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx index a2dbee433e..e78fb05783 100644 --- a/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx +++ b/src/features/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx @@ -16,6 +16,7 @@ import { KubeObject } from "../../../../common/k8s-api/kube-object"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable"; import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details"; import type { ApiManager } from "../../../../common/k8s-api/api-manager"; +import type { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; describe("reactively hide kube object detail item", () => { let builder: ApplicationBuilder; @@ -28,16 +29,11 @@ describe("reactively hide kube object detail item", () => { builder.setEnvironmentToClusterFrame(); builder.beforeWindowStart((windowDi) => { - windowDi.override( - apiManagerInjectable, - () => - ({ - getStore: () => ({ - getByPath: () => - getKubeObjectStub("some-kind", "some-api-version"), - }), - } as unknown as ApiManager), - ); + windowDi.override(apiManagerInjectable, () => ({ + getStore: () => ({ + loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"), + }) as Partial as KubeObjectStore, + }) as Partial as ApiManager); runInAction(() => { windowDi.register(testRouteInjectable, testRouteComponentInjectable); diff --git a/src/renderer/api/catalog/entity/legacy-globals.ts b/src/renderer/api/catalog/entity/legacy-globals.ts deleted file mode 100644 index 70a30f92f8..0000000000 --- a/src/renderer/api/catalog/entity/legacy-globals.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalFunctionForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import getActiveClusterEntityInjectable from "./get-active-cluster-entity.injectable"; - -/** - * @deprecated use `di.inject(getActiveClusterEntityInjectable)` instead - */ -export const getActiveClusterEntity = asLegacyGlobalFunctionForExtensionApi(getActiveClusterEntityInjectable); diff --git a/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts b/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts new file mode 100644 index 0000000000..e480eadc78 --- /dev/null +++ b/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts @@ -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, lifecycleEnum } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import type { ClusterMetricsResourceType } from "../../../../common/cluster-types"; +import currentKubeObjectInDetailsInjectable from "../../../components/kube-object-details/current-kube-object-in-details.injectable"; +import enabledMetricsInjectable from "./metrics-enabled.injectable"; + +const metricsDetailsComponentEnabledInjectable = getInjectable({ + id: "metrics-details-component-enabled", + instantiate: (di, kind) => { + const metricsEnabled = di.inject(enabledMetricsInjectable, kind); + const currentKubeObjectInDetails = di.inject(currentKubeObjectInDetailsInjectable); + + return computed(() => { + const current = currentKubeObjectInDetails.value.get(); + + if (!current?.object) { + return false; + } + + return current.object.kind == kind && metricsEnabled.get(); + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, kind: ClusterMetricsResourceType) => kind, + }), +}); + +export default metricsDetailsComponentEnabledInjectable; diff --git a/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts b/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts new file mode 100644 index 0000000000..d267132fcd --- /dev/null +++ b/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import type { ClusterMetricsResourceType } from "../../../../common/cluster-types"; +import getActiveClusterEntityInjectable from "./get-active-cluster-entity.injectable"; + +const enabledMetricsInjectable = getInjectable({ + id: "enabled-metrics", + instantiate: (di, kind) => { + const getActiveClusterEntity = di.inject(getActiveClusterEntityInjectable); + + return computed(() => !getActiveClusterEntity()?.isMetricHidden(kind)); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, kind: ClusterMetricsResourceType) => kind, + }), +}); + +export default enabledMetricsInjectable; diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index 887280d298..ab7ea2c726 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -16,7 +16,6 @@ import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTa import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; @@ -121,8 +120,6 @@ class NonInjectedHpaDetails extends React.Component - - {scaleTargetRef && ( diff --git a/src/renderer/components/+config-leases/lease-details.tsx b/src/renderer/components/+config-leases/lease-details.tsx index 617021edbf..d86ecabaf8 100644 --- a/src/renderer/components/+config-leases/lease-details.tsx +++ b/src/renderer/components/+config-leases/lease-details.tsx @@ -9,7 +9,6 @@ import React from "react"; import { observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectMeta } from "../kube-object-meta"; import type { Lease } from "../../../common/k8s-api/endpoints"; export interface LeaseDetailsProps extends KubeObjectDetailsProps { @@ -23,8 +22,6 @@ export class LeaseDetails extends React.Component { return (
- - {lease.getHolderIdentity()} diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx index 313da25667..2da12a3d31 100644 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx @@ -10,7 +10,6 @@ import { observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-range.api"; import { LimitPart, LimitRange, Resource } from "../../../common/k8s-api/endpoints/limit-range.api"; -import { KubeObjectMeta } from "../kube-object-meta"; import { DrawerItem } from "../drawer/drawer-item"; import { Badge } from "../badge"; import logger from "../../../common/logger"; @@ -73,8 +72,6 @@ export class LimitRangeDetails extends React.Component { return (
- - {containerLimits.length > 0 && ( { diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx index 36c479ad99..f5b4007281 100644 --- a/src/renderer/components/+config-maps/config-map-details.tsx +++ b/src/renderer/components/+config-maps/config-map-details.tsx @@ -14,7 +14,6 @@ import { Input } from "../input"; import { Button } from "../button"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { ConfigMap } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger"; import type { ConfigMapStore } from "./store"; import { withInjectables } from "@ogre-tools/injectable-react"; @@ -95,7 +94,6 @@ class NonInjectedConfigMapDetails extends React.Component - { data.length > 0 && ( <> diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx index 901daf5411..b58a42b0d9 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx @@ -11,7 +11,6 @@ import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps { @@ -37,8 +36,6 @@ export class PodDisruptionBudgetDetails extends React.Component - - {selectors.length > 0 && ( { diff --git a/src/renderer/components/+config-priority-classes/priority-classes-details.tsx b/src/renderer/components/+config-priority-classes/priority-classes-details.tsx index 5bfcc4ca07..7caf5190a1 100644 --- a/src/renderer/components/+config-priority-classes/priority-classes-details.tsx +++ b/src/renderer/components/+config-priority-classes/priority-classes-details.tsx @@ -10,7 +10,6 @@ import { observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { PriorityClass } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; export interface PriorityClassesDetailsProps extends KubeObjectDetailsProps { } @@ -23,8 +22,6 @@ export class PriorityClassesDetails extends React.Component - - {pc.getDescription()} diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx index 30a7d3496c..c02f9b221b 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx @@ -13,7 +13,6 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { ResourceQuota } from "../../../common/k8s-api/endpoints/resource-quota.api"; import { LineProgress } from "../line-progress"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps { @@ -93,8 +92,6 @@ export class ResourceQuotaDetails extends React.Component - - {renderQuotas(quota)} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx index 309c2a73bd..f6fc61faeb 100644 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx @@ -10,7 +10,6 @@ import { observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { RuntimeClass } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import { Badge } from "../badge"; import { RuntimeClassDetailsTolerations } from "./runtime-classes-details-tolerations"; @@ -26,8 +25,6 @@ export class RuntimeClassesDetails extends React.Component - - {rc.getHandler()} diff --git a/src/renderer/components/+config-secrets/secret-details.tsx b/src/renderer/components/+config-secrets/secret-details.tsx index c22f7befe8..6b4873f187 100644 --- a/src/renderer/components/+config-secrets/secret-details.tsx +++ b/src/renderer/components/+config-secrets/secret-details.tsx @@ -16,7 +16,6 @@ import { base64, toggle } from "../../utils"; import { Icon } from "../icon"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Secret } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger"; import type { SecretStore } from "./store"; import { withInjectables } from "@ogre-tools/injectable-react"; @@ -161,7 +160,6 @@ class NonInjectedSecretDetails extends React.Component - {secret.type} diff --git a/src/renderer/components/+custom-resources/__tests__/__snapshots__/custom-resource-details.test.tsx.snap b/src/renderer/components/+custom-resources/__tests__/__snapshots__/custom-resource-details.test.tsx.snap index f15bde7996..5be5bd6bf7 100644 --- a/src/renderer/components/+custom-resources/__tests__/__snapshots__/custom-resource-details.test.tsx.snap +++ b/src/renderer/components/+custom-resources/__tests__/__snapshots__/custom-resource-details.test.tsx.snap @@ -5,35 +5,6 @@ exports[` with a CRD with a boolean field should displa
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - first-crd - -
@@ -57,35 +28,6 @@ exports[` with a CRD with a boolean field should displa
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - first-crd - -
@@ -109,35 +51,6 @@ exports[` with a CRD with a number field should display
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - first-crd - -
@@ -161,35 +74,6 @@ exports[` with a CRD with a number field should display
-
- - Created - - - <unknown> - ago - -
-
- - Name - - - first-crd - -
diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index 09676c2101..d3023a020e 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -14,7 +14,6 @@ import { DrawerItem, DrawerTitle } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Table, TableCell, TableHead, TableRow } from "../table"; import { Input } from "../input"; -import { KubeObjectMeta } from "../kube-object-meta"; import { MonacoEditor } from "../monaco-editor"; import logger from "../../../common/logger"; @@ -42,8 +41,6 @@ export class CRDDetails extends React.Component { return (
- - {crd.getGroup()} diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index 3bd1e821fa..01f8b3a175 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -11,7 +11,6 @@ import { cssNames } from "../../utils"; import { Badge } from "../badge"; import { DrawerItem } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectMeta } from "../kube-object-meta"; import { Input } from "../input"; import type { AdditionalPrinterColumnsV1 } from "../../../common/k8s-api/endpoints/custom-resource-definition.api"; import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/custom-resource-definition.api"; @@ -21,7 +20,7 @@ import { KubeObject } from "../../../common/k8s-api/kube-object"; import logger from "../../../common/logger"; export interface CustomResourceDetailsProps extends KubeObjectDetailsProps { - crd: CustomResourceDefinition; + crd?: CustomResourceDefinition; } function convertSpecValue(value: unknown): React.ReactNode { @@ -129,7 +128,6 @@ export class CustomResourceDetails extends React.Component - {this.renderAdditionalColumns(object, extraColumns)} {this.renderStatus(object, extraColumns)}
diff --git a/src/renderer/components/+custom-resources/definition.store.ts b/src/renderer/components/+custom-resources/definition.store.ts index 6a4b88a70e..778f895214 100644 --- a/src/renderer/components/+custom-resources/definition.store.ts +++ b/src/renderer/components/+custom-resources/definition.store.ts @@ -58,7 +58,6 @@ export class CustomResourceDefinitionStore extends KubeObjectStore ( diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index cf45d7a666..5334744054 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -12,7 +12,6 @@ import { Link } from "react-router-dom"; import { observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api"; -import { KubeObjectMeta } from "../kube-object-meta"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; import logger from "../../../common/logger"; @@ -52,8 +51,6 @@ const NonInjectedEventDetails = observer(({ return (
- - {message} diff --git a/src/renderer/components/+events/store.ts b/src/renderer/components/+events/store.ts index a639ea80be..6910ffb153 100644 --- a/src/renderer/components/+events/store.ts +++ b/src/renderer/components/+events/store.ts @@ -13,13 +13,13 @@ import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { Pod } from "../../../common/k8s-api/endpoints/pod.api"; import type { GetPodById } from "../+workloads-pods/get-pod-by-id.injectable"; -export interface EventStoreDependencies { +interface Dependencies { getPodById: GetPodById; } export class EventStore extends KubeObjectStore { constructor( - protected readonly dependencies: EventStoreDependencies, + protected readonly dependencies: Dependencies, api: KubeEventApi, opts: KubeObjectStoreOptions = {}, ) { diff --git a/src/renderer/components/+namespaces/metrics-details-component.injectable.tsx b/src/renderer/components/+namespaces/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..026aace4f5 --- /dev/null +++ b/src/renderer/components/+namespaces/metrics-details-component.injectable.tsx @@ -0,0 +1,53 @@ +/** + * 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 { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Namespace } from "../../../common/k8s-api/endpoints"; +import type { PodMetricInNamespaceData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import namespaceMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedNamespaceMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const NamespaceMetricsDetailsComponent = withInjectables>(NonInjectedNamespaceMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(namespaceMetricsInjectable, props.object), + ...props, + }), +}); + +const namespaceMetricsDetailsComponentInjectable = getInjectable({ + id: "namespace-metrics-details-component", + instantiate: (di) => ({ + Component: NamespaceMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Namespace), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default namespaceMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+namespaces/metrics.injectable.ts b/src/renderer/components/+namespaces/metrics.injectable.ts new file mode 100644 index 0000000000..98bf94883f --- /dev/null +++ b/src/renderer/components/+namespaces/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Namespace } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsInNamespaceInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable"; + +const namespaceMetricsInjectable = getInjectable({ + id: "namespace-metrics", + instantiate: (di, namespace) => { + const requestPodMetricsInNamespace = di.inject(requestPodMetricsInNamespaceInjectable); + + return asyncComputed({ + getValueFromObservedPromise: async () => { + now(60 * 1000); // Update every minute + + return requestPodMetricsInNamespace(namespace.getName()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, namespace: Namespace) => namespace.getId(), + }), +}); + +export default namespaceMetricsInjectable; diff --git a/src/renderer/components/+namespaces/namespace-details.tsx b/src/renderer/components/+namespaces/namespace-details.tsx index a11d7f26a5..33712e9b2b 100644 --- a/src/renderer/components/+namespaces/namespace-details.tsx +++ b/src/renderer/components/+namespaces/namespace-details.tsx @@ -6,7 +6,7 @@ import "./namespace-details.scss"; import React from "react"; -import { computed, makeObservable, observable, reaction } from "mobx"; +import { computed, makeObservable } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import { cssNames } from "../../utils"; @@ -14,42 +14,32 @@ import { Namespace } from "../../../common/k8s-api/endpoints"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Link } from "react-router-dom"; import { Spinner } from "../spinner"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ResourceMetrics } from "../resource-metrics"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import type { ResourceQuotaStore } from "../+config-resource-quotas/store"; import type { LimitRangeStore } from "../+config-limit-ranges/store"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import limitRangeStoreInjectable from "../+config-limit-ranges/store.injectable"; import resourceQuotaStoreInjectable from "../+config-resource-quotas/store.injectable"; -import type { PodMetricInNamespaceData, RequestPodMetricsInNamespace } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable"; -import requestPodMetricsInNamespaceInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable"; +import type { Logger } from "../../../common/logger"; +import loggerInjectable from "../../../common/logger.injectable"; export interface NamespaceDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { subscribeStores: SubscribeStores; - getActiveClusterEntity: GetActiveClusterEntity; getDetailsUrl: GetDetailsUrl; resourceQuotaStore: ResourceQuotaStore; limitRangeStore: LimitRangeStore; - requestPodMetricsInNamespace: RequestPodMetricsInNamespace; + logger: Logger; } @observer class NonInjectedNamespaceDetails extends React.Component { - @observable metrics: PodMetricInNamespaceData | null = null; - constructor(props: NamespaceDetailsProps & Dependencies) { super(props); makeObservable(this); @@ -57,10 +47,6 @@ class NonInjectedNamespaceDetails extends React.Component this.props.object, () => { - this.metrics = null; - }), - this.props.subscribeStores([ this.props.resourceQuotaStore, this.props.limitRangeStore, @@ -80,40 +66,23 @@ class NonInjectedNamespaceDetails extends React.Component { - this.metrics = await this.props.requestPodMetricsInNamespace(this.props.object.getName()); - }; - render() { - const { object: namespace, getActiveClusterEntity, resourceQuotaStore, getDetailsUrl, limitRangeStore } = this.props; + const { object: namespace, resourceQuotaStore, getDetailsUrl, limitRangeStore } = this.props; if (!namespace) { return null; } if (!(namespace instanceof Namespace)) { - logger.error("[NamespaceDetails]: passed object that is not an instanceof Namespace", namespace); + this.props.logger.error("[NamespaceDetails]: passed object that is not an instanceof Namespace", namespace); return null; } const status = namespace.getStatus(); - const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Namespace); return (
- {!isMetricHidden && ( - - - - )} - - {status} @@ -143,11 +112,10 @@ export const NamespaceDetails = withInjectables ({ ...props, subscribeStores: di.inject(subscribeStoresInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), limitRangeStore: di.inject(limitRangeStoreInjectable), resourceQuotaStore: di.inject(resourceQuotaStoreInjectable), - requestPodMetricsInNamespace: di.inject(requestPodMetricsInNamespaceInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+network-endpoints/endpoint-details.tsx b/src/renderer/components/+network-endpoints/endpoint-details.tsx index 3821c68047..4e014595b7 100644 --- a/src/renderer/components/+network-endpoints/endpoint-details.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-details.tsx @@ -10,7 +10,6 @@ import { observer } from "mobx-react"; import { DrawerTitle } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Endpoints } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import { EndpointSubsetList } from "./endpoint-subset-list"; import logger from "../../../common/logger"; @@ -34,7 +33,6 @@ export class EndpointsDetails extends React.Component { return (
- Subsets { endpoint.getEndpointSubsets().map((subset) => ( diff --git a/src/renderer/components/+network-ingresses/ingress-details.tsx b/src/renderer/components/+network-ingresses/ingress-details.tsx index e8b916cac2..d49d1e49a5 100644 --- a/src/renderer/components/+network-ingresses/ingress-details.tsx +++ b/src/renderer/components/+network-ingresses/ingress-details.tsx @@ -6,54 +6,26 @@ import "./ingress-details.scss"; import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { makeObservable, observable, reaction } from "mobx"; +import { observer } from "mobx-react"; import { DrawerItem, DrawerTitle } from "../drawer"; import type { ILoadBalancerIngress } from "../../../common/k8s-api/endpoints"; import { Ingress } from "../../../common/k8s-api/endpoints"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { ResourceMetrics } from "../resource-metrics"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { IngressCharts } from "./ingress-charts"; -import { KubeObjectMeta } from "../kube-object-meta"; import { computeRuleDeclarations } from "../../../common/k8s-api/endpoints/ingress.api"; -import { getActiveClusterEntity } from "../../api/catalog/entity/legacy-globals"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; -import type { IngressMetricData, RequestIngressMetrics } from "../../../common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; -import requestIngressMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface IngressDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { - requestIngressMetrics: RequestIngressMetrics; + logger: Logger; } @observer class NonInjectedIngressDetails extends React.Component { - @observable metrics: IngressMetricData | null = null; - - constructor(props: IngressDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), - ]); - } - - loadMetrics = async () => { - const { object: ingress, requestIngressMetrics } = this.props; - - this.metrics = await requestIngressMetrics(ingress.getName(), ingress.getNs()); - }; - renderPaths(ingress: Ingress) { return ingress.getRules() .map((rule, index) => ( @@ -124,7 +96,7 @@ class NonInjectedIngressDetails extends React.Component - {!isMetricHidden && ( - - - - )} - {ingress.getPorts()} @@ -181,6 +138,6 @@ class NonInjectedIngressDetails extends React.Component(NonInjectedIngressDetails, { getProps: (di, props) => ({ ...props, - requestIngressMetrics: di.inject(requestIngressMetricsInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+network-ingresses/ingresses.tsx b/src/renderer/components/+network-ingresses/ingresses.tsx index a8403e142b..6956944483 100644 --- a/src/renderer/components/+network-ingresses/ingresses.tsx +++ b/src/renderer/components/+network-ingresses/ingresses.tsx @@ -32,85 +32,86 @@ interface Dependencies { filterByNamespace: FilterByNamespace; } -@observer -class NonInjectedIngresses extends React.Component { - render() { - return ( - - ingress.getName(), - [columnId.namespace]: ingress => ingress.getNs(), - [columnId.age]: ingress => -ingress.getCreationTimestamp(), - }} - searchFilters={[ - ingress => ingress.getSearchFields(), - ingress => ingress.getPorts(), - ]} - renderHeaderTitle="Ingresses" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers }, - { title: "Rules", className: "rules", id: columnId.rules }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={ingress => [ - ingress.getName(), - , - this.props.filterByNamespace(ingress.getNs()))} - > - {ingress.getNs()} - , - ingress.getLoadBalancers().map(lb =>

{lb}

), - computeRouteDeclarations(ingress).map(decl => ( - decl.displayAsLink - ? ( -
- e.stopPropagation()} - > - {decl.url} - - {` ⇢ ${decl.service}`} -
- ) - : ( -
- {`${decl.url} ⇢ ${decl.service}`} -
- ) - )), - , - ]} - tableProps={{ - customRowHeights: (item, lineHeight, paddings) => { - const lines = item.getRoutes().length || 1; +const NonInjectedIngresses = observer((props: Dependencies) => { + const { + ingressStore, + filterByNamespace, + } = props; - return lines * lineHeight + paddings; - }, - }} - /> -
- ); - } -} + return ( + + ingress.getName(), + [columnId.namespace]: ingress => ingress.getNs(), + [columnId.age]: ingress => -ingress.getCreationTimestamp(), + } } + searchFilters={ [ + ingress => ingress.getSearchFields(), + ingress => ingress.getPorts(), + ] } + renderHeaderTitle="Ingresses" + renderTableHeader={ [ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers }, + { title: "Rules", className: "rules", id: columnId.rules }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ] } + renderTableContents={ ingress => [ + ingress.getName(), + , + filterByNamespace(ingress.getNs()))} + > + {ingress.getNs()} + , + ingress.getLoadBalancers().map(lb =>

{ lb }

), + computeRouteDeclarations(ingress).map(decl => ( + decl.displayAsLink + ? ( +
+ e.stopPropagation() } + > + { decl.url } + + { ` ⇢ ${decl.service}` } +
+ ) + : ( +
+ { `${decl.url} ⇢ ${decl.service}` } +
+ ) + )), + , + ] } + tableProps={ { + customRowHeights: (item, lineHeight, paddings) => { + const lines = item.getRoutes().length || 1; + + return lines * lineHeight + paddings; + }, + } } /> +
+ ); +}); export const Ingresses = withInjectables(NonInjectedIngresses, { getProps: (di, props) => ({ ...props, - filterByNamespace: di.inject(filterByNamespaceInjectable), ingressStore: di.inject(ingressStoreInjectable), + filterByNamespace: di.inject(filterByNamespaceInjectable), }), }); diff --git a/src/renderer/components/+network-ingresses/metrics-details-component.injectable.tsx b/src/renderer/components/+network-ingresses/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..62d8569ee5 --- /dev/null +++ b/src/renderer/components/+network-ingresses/metrics-details-component.injectable.tsx @@ -0,0 +1,56 @@ +/** + * 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 { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Ingress } from "../../../common/k8s-api/endpoints"; +import type { IngressMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import { IngressCharts } from "./ingress-charts"; +import ingressMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedIngressMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const IngressMetricsDetailsComponent = withInjectables>(NonInjectedIngressMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(ingressMetricsInjectable, props.object), + ...props, + }), +}); + +const ingressMetricsDetailsComponentInjectable = getInjectable({ + id: "ingress-metrics-details-component", + instantiate: (di) => ({ + Component: IngressMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Ingress), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default ingressMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+network-ingresses/metrics.injectable.ts b/src/renderer/components/+network-ingresses/metrics.injectable.ts new file mode 100644 index 0000000000..602f5088b4 --- /dev/null +++ b/src/renderer/components/+network-ingresses/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Ingress } from "../../../common/k8s-api/endpoints"; +import requestIngressMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable"; + +const ingressMetricsInjectable = getInjectable({ + id: "ingress-metrics", + instantiate: (di, ingress) => { + const requestIngressMetrics = di.inject(requestIngressMetricsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: async () => { + now(60 * 1000); // Update every minute + + return requestIngressMetrics(ingress.getName(), ingress.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, ingress: Ingress) => ingress.getId(), + }), +}); + +export default ingressMetricsInjectable; diff --git a/src/renderer/components/+network-policies/network-policy-details.tsx b/src/renderer/components/+network-policies/network-policy-details.tsx index 264d626ba9..60eec92e91 100644 --- a/src/renderer/components/+network-policies/network-policy-details.tsx +++ b/src/renderer/components/+network-policies/network-policy-details.tsx @@ -13,7 +13,6 @@ import { Badge } from "../badge"; import { SubTitle } from "../layout/sub-title"; import { observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object"; import { isEmpty } from "lodash"; @@ -170,8 +169,6 @@ export class NetworkPolicyDetails extends React.Component - - 0}> { selector.length > 0 diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 61a45c4040..998a8c6576 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -11,7 +11,6 @@ import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Service } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import { ServicePortComponent } from "./service-port-component"; import type { EndpointsStore } from "../+network-endpoints/store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; @@ -75,8 +74,6 @@ class NonInjectedServiceDetails extends React.Component - - {service.getSelector().map(selector => )} diff --git a/src/renderer/components/+nodes/details.tsx b/src/renderer/components/+nodes/details.tsx index f71d5bfa5f..4e1eeddb65 100644 --- a/src/renderer/components/+nodes/details.tsx +++ b/src/renderer/components/+nodes/details.tsx @@ -11,26 +11,18 @@ import kebabCase from "lodash/kebabCase"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem, DrawerItemLabels } from "../drawer"; import { Badge } from "../badge"; -import { ResourceMetrics } from "../resource-metrics"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { formatNodeTaint, Node } from "../../../common/k8s-api/endpoints"; -import { NodeCharts } from "./node-charts"; -import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { NodeDetailsResources } from "./details-resources"; import { DrawerTitle } from "../drawer/drawer-title"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import type { PodStore } from "../+workloads-pods/store"; import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { ClusterMetricData, RequestClusterMetricsByNodeNames } 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 loggerInjectable from "../../../common/logger.injectable"; export interface NodeDetailsProps extends KubeObjectDetailsProps { } @@ -38,39 +30,21 @@ export interface NodeDetailsProps extends KubeObjectDetailsProps { interface Dependencies { subscribeStores: SubscribeStores; podStore: PodStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames; + logger: Logger; } @observer class NonInjectedNodeDetails extends React.Component { - @observable metrics: ClusterMetricData | null = null; - - constructor(props: NodeDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object.getName(), () => { - this.metrics = null; - }), - this.props.subscribeStores([ this.props.podStore, ]), ]); } - loadMetrics = async () => { - const { object: node, requestClusterMetricsByNodeNames } = this.props; - - this.metrics = await requestClusterMetricsByNodeNames([node.getName()]); - }; - render() { - const { object: node, podStore, getActiveClusterEntity } = this.props; + const { object: node, podStore, logger } = this.props; if (!node) { return null; @@ -86,27 +60,9 @@ class NonInjectedNodeDetails extends React.Component - {!isMetricHidden && podStore.isLoaded && ( - - - - )} - {addresses && ( { @@ -197,8 +153,7 @@ export const NodeDetails = withInjectables(NonIn ...props, subscribeStores: di.inject(subscribeStoresInjectable), podStore: di.inject(podStoreInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), - requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+nodes/legacy-store.ts b/src/renderer/components/+nodes/legacy-store.ts deleted file mode 100644 index 416bd2a36b..0000000000 --- a/src/renderer/components/+nodes/legacy-store.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import nodeStoreInjectable from "./store.injectable"; - -/** - * @deprecated use `di.inject(nodeStoreInjectable)` instead - */ -export const nodeStore = asLegacyGlobalForExtensionApi(nodeStoreInjectable); diff --git a/src/renderer/components/+nodes/metrics-details-component.injectable.tsx b/src/renderer/components/+nodes/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..9a8d593d74 --- /dev/null +++ b/src/renderer/components/+nodes/metrics-details-component.injectable.tsx @@ -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 type { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Node } from "../../../common/k8s-api/endpoints"; +import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import nodeMetricsInjectable from "./metrics.injectable"; +import { NodeCharts } from "./node-charts"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedNodeMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const NodeMetricsDetailsComponent = withInjectables>(NonInjectedNodeMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(nodeMetricsInjectable, props.object), + ...props, + }), +}); + +const nodeMetricsDetailsComponentInjectable = getInjectable({ + id: "node-metrics-details-component", + instantiate: (di) => ({ + Component: NodeMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Node), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default nodeMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+nodes/metrics.injectable.ts b/src/renderer/components/+nodes/metrics.injectable.ts new file mode 100644 index 0000000000..988ca84e9e --- /dev/null +++ b/src/renderer/components/+nodes/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Node } from "../../../common/k8s-api/endpoints"; +import requestClusterMetricsByNodeNamesInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; + +const nodeMetricsInjectable = getInjectable({ + id: "node-metrics", + instantiate: (di, node) => { + const requestClusterMetricsByNodeNames = di.inject(requestClusterMetricsByNodeNamesInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); + + return requestClusterMetricsByNodeNames([node.getName()]); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, node: Node) => node.getId(), + }), +}); + +export default nodeMetricsInjectable; diff --git a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx index 775eb02680..03cce618a0 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx @@ -12,7 +12,6 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodSecurityPolicy } from "../../../common/k8s-api/endpoints"; import { Badge } from "../badge"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps { @@ -74,8 +73,6 @@ export class PodSecurityPolicyDetails extends React.Component - - {allowedCapabilities && ( {allowedCapabilities.join(", ")} diff --git a/src/renderer/components/+storage-classes/storage-class-details.tsx b/src/renderer/components/+storage-classes/storage-class-details.tsx index 2a6202845d..0df28e13f8 100644 --- a/src/renderer/components/+storage-classes/storage-class-details.tsx +++ b/src/renderer/components/+storage-classes/storage-class-details.tsx @@ -12,7 +12,6 @@ import { Badge } from "../badge"; import { disposeOnUnmount, observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { StorageClass } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import type { StorageClassStore } from "./store"; import { VolumeDetailsList } from "../+storage-volumes/volume-details-list"; import type { PersistentVolumeStore } from "../+storage-volumes/store"; @@ -60,8 +59,6 @@ class NonInjectedStorageClassDetails extends React.Component - - {provisioner && ( diff --git a/src/renderer/components/+storage-volume-claims/metrics-details-component.injectable.tsx b/src/renderer/components/+storage-volume-claims/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..b9edddf157 --- /dev/null +++ b/src/renderer/components/+storage-volume-claims/metrics-details-component.injectable.tsx @@ -0,0 +1,55 @@ +/** + * 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 { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { PersistentVolumeClaim } from "../../../common/k8s-api/endpoints"; +import type { PersistentVolumeClaimMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import persistentVolumeClaimMetricsInjectable from "./metrics.injectable"; +import { VolumeClaimDiskChart } from "./volume-claim-disk-chart"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedPersistentVolumeClaimMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const PersistentVolumeClaimMetricsDetailsComponent = withInjectables>(NonInjectedPersistentVolumeClaimMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(persistentVolumeClaimMetricsInjectable, props.object), + ...props, + }), +}); + +const persistentVolumeClaimMetricsDetailsComponentInjectable = getInjectable({ + id: "persistent-volume-claim-metrics-details-component", + instantiate: (di) => ({ + Component: PersistentVolumeClaimMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.VolumeClaim), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default persistentVolumeClaimMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+storage-volume-claims/metrics.injectable.ts b/src/renderer/components/+storage-volume-claims/metrics.injectable.ts new file mode 100644 index 0000000000..a39e13ce00 --- /dev/null +++ b/src/renderer/components/+storage-volume-claims/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { PersistentVolumeClaim } from "../../../common/k8s-api/endpoints"; +import requestPersistentVolumeClaimMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable"; + +const persistentVolumeClaimMetricsInjectable = getInjectable({ + id: "persistent-volume-claim-metrics", + instantiate: (di, persistentVolumeClaim) => { + const requestPersistentVolumeClaimMetrics = di.inject(requestPersistentVolumeClaimMetricsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); // update every minute + + return requestPersistentVolumeClaimMetrics(persistentVolumeClaim); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, persistentVolumeClaim: PersistentVolumeClaim) => persistentVolumeClaim.getId(), + }), +}); + +export default persistentVolumeClaimMetricsInjectable; diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx index 250ab2e713..954bad9bc5 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx @@ -6,64 +6,37 @@ import "./volume-claim-details.scss"; import React, { Fragment } from "react"; -import { makeObservable, observable, reaction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; +import { observer } from "mobx-react"; import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; import { Link } from "react-router-dom"; -import { ResourceMetrics } from "../resource-metrics"; -import { VolumeClaimDiskChart } from "./volume-claim-disk-chart"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { PersistentVolumeClaim, storageClassApi } from "../../../common/k8s-api/endpoints"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import { KubeObjectMeta } from "../kube-object-meta"; -import logger from "../../../common/logger"; -import type { PersistentVolumeClaimMetricData, RequestPersistentVolumeClaimMetrics } from "../../../common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable"; +import type { StorageClassApi } from "../../../common/k8s-api/endpoints"; +import { PersistentVolumeClaim } from "../../../common/k8s-api/endpoints"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; -import requestPersistentVolumeClaimMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import type { PodStore } from "../+workloads-pods/store"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable"; import { stopPropagation } from "../../../renderer/utils"; +import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface PersistentVolumeClaimDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { - requestPersistentVolumeClaimMetrics: RequestPersistentVolumeClaimMetrics; - getActiveClusterEntity: GetActiveClusterEntity; getDetailsUrl: GetDetailsUrl; podStore: PodStore; + storageClassApi: StorageClassApi; + logger: Logger; } @observer class NonInjectedPersistentVolumeClaimDetails extends React.Component { - @observable metrics: PersistentVolumeClaimMetricData | null = null; - - constructor(props: PersistentVolumeClaimDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), - ]); - } - - loadMetrics = async () => { - const { object: volumeClaim, requestPersistentVolumeClaimMetrics } = this.props; - - this.metrics = await requestPersistentVolumeClaimMetrics(volumeClaim); - }; - render() { - const { object: volumeClaim, getActiveClusterEntity, podStore, getDetailsUrl } = this.props; + const { object: volumeClaim, podStore, getDetailsUrl, storageClassApi, logger } = this.props; if (!volumeClaim) { return null; @@ -77,27 +50,13 @@ class NonInjectedPersistentVolumeClaimDetails extends React.Component - {!isMetricHidden && ( - - - - )} - {accessModes?.join(", ")} @@ -147,9 +106,9 @@ class NonInjectedPersistentVolumeClaimDetails extends React.Component(NonInjectedPersistentVolumeClaimDetails, { getProps: (di, props) => ({ ...props, - requestPersistentVolumeClaimMetrics: di.inject(requestPersistentVolumeClaimMetricsInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), podStore: di.inject(podStoreInjectable), + storageClassApi: di.inject(storageClassApiInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+storage-volumes/volume-details.tsx b/src/renderer/components/+storage-volumes/volume-details.tsx index 31d15c24a1..2149d200f7 100644 --- a/src/renderer/components/+storage-volumes/volume-details.tsx +++ b/src/renderer/components/+storage-volumes/volume-details.tsx @@ -13,7 +13,6 @@ import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; import { PersistentVolume, persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectMeta } from "../kube-object-meta"; import { getDetailsUrl } from "../kube-detail-params"; import logger from "../../../common/logger"; import { stopPropagation } from "../../../renderer/utils"; @@ -44,7 +43,6 @@ export class PersistentVolumeDetails extends React.Component - {capacity?.storage} diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx index 7fcb29094b..d78bae4fb1 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx @@ -14,7 +14,6 @@ import { autoBind, ObservableHashSet, prevDefault } from "../../../utils"; import { AddRemoveButtons } from "../../add-remove-buttons"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { KubeObjectMeta } from "../../kube-object-meta"; import { Table, TableCell, TableHead, TableRow } from "../../table"; import { ClusterRoleBindingDialog } from "./dialog"; import { clusterRoleBindingStore } from "./legacy-store"; @@ -76,8 +75,6 @@ class NonInjectedClusterRoleBindingDetails extends React.Component - - Reference diff --git a/src/renderer/components/+user-management/+cluster-roles/details.tsx b/src/renderer/components/+user-management/+cluster-roles/details.tsx index f06e9785d1..fa0e21143e 100644 --- a/src/renderer/components/+user-management/+cluster-roles/details.tsx +++ b/src/renderer/components/+user-management/+cluster-roles/details.tsx @@ -10,7 +10,6 @@ import React from "react"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { KubeObjectMeta } from "../../kube-object-meta"; import type { ClusterRole } from "../../../../common/k8s-api/endpoints"; export interface ClusterRoleDetailsProps extends KubeObjectDetailsProps { @@ -26,8 +25,6 @@ export class ClusterRoleDetails extends React.Component return (
- - Rules {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { return ( diff --git a/src/renderer/components/+user-management/+role-bindings/details.tsx b/src/renderer/components/+user-management/+role-bindings/details.tsx index bf0f573b5e..eeadae080c 100644 --- a/src/renderer/components/+user-management/+role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+role-bindings/details.tsx @@ -13,7 +13,6 @@ import { prevDefault } from "../../../utils"; import { AddRemoveButtons } from "../../add-remove-buttons"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { KubeObjectMeta } from "../../kube-object-meta"; import { Table, TableCell, TableHead, TableRow } from "../../table"; import { RoleBindingDialog } from "./dialog"; import { roleBindingStore } from "./legacy-store"; @@ -71,8 +70,6 @@ class NonInjectedRoleBindingDetails extends React.Component - - Reference
diff --git a/src/renderer/components/+user-management/+roles/details.tsx b/src/renderer/components/+user-management/+roles/details.tsx index 0f24e9e711..9257d7095e 100644 --- a/src/renderer/components/+user-management/+roles/details.tsx +++ b/src/renderer/components/+user-management/+roles/details.tsx @@ -11,7 +11,6 @@ import React from "react"; import type { Role } from "../../../../common/k8s-api/endpoints"; import { DrawerTitle } from "../../drawer"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { KubeObjectMeta } from "../../kube-object-meta"; export interface RoleDetailsProps extends KubeObjectDetailsProps { } @@ -26,7 +25,6 @@ export class RoleDetails extends React.Component { return (
- Rules {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { return ( diff --git a/src/renderer/components/+user-management/+service-accounts/details.tsx b/src/renderer/components/+user-management/+service-accounts/details.tsx index 788be09e54..2c0bec0f0a 100644 --- a/src/renderer/components/+user-management/+service-accounts/details.tsx +++ b/src/renderer/components/+user-management/+service-accounts/details.tsx @@ -14,7 +14,6 @@ import type { Secret, ServiceAccount } from "../../../../common/k8s-api/endpoint import { DrawerItem, DrawerTitle } from "../../drawer"; import { Icon } from "../../icon"; import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { KubeObjectMeta } from "../../kube-object-meta"; import { Spinner } from "../../spinner"; import { ServiceAccountsSecret } from "./secret"; import type { SecretStore } from "../../+config-secrets/store"; @@ -137,8 +136,6 @@ class NonInjectedServiceAccountsDetails extends React.Component - - {tokens.length > 0 && ( {this.renderSecretLinks(tokens)} diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx index 4b1d188d48..4701869ab6 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx @@ -17,7 +17,6 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { getDetailsUrl } from "../kube-detail-params"; import type { Job } from "../../../common/k8s-api/endpoints"; import { CronJob } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMeta } from "../kube-object-meta"; import logger from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; @@ -61,7 +60,6 @@ class NonInjectedCronJobDetails extends React.Component - { cronJob.isNeverRun() diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index 10d51efb33..1dba28c478 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -13,11 +13,9 @@ import moment from "moment"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../kube-object/age"; import type { CronJobStore } from "./store"; -import type { JobStore } from "../+workloads-jobs/store"; import type { EventStore } from "../+events/store"; import { withInjectables } from "@ogre-tools/injectable-react"; import cronJobStoreInjectable from "./store.injectable"; -import jobStoreInjectable from "../+workloads-jobs/store.injectable"; import eventStoreInjectable from "../+events/store.injectable"; import { prevDefault } from "../../utils"; import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; @@ -35,84 +33,78 @@ enum columnId { interface Dependencies { cronJobStore: CronJobStore; - jobStore: JobStore; eventStore: EventStore; filterByNamespace: FilterByNamespace; } -@observer -class NonInjectedCronJobs extends React.Component{ - render() { - const { - cronJobStore, - eventStore, - jobStore, - filterByNamespace, - } = this.props; +const NonInjectedCronJobs = observer((props: Dependencies) => { + const { + cronJobStore, + eventStore, + filterByNamespace, + } = props; - return ( - - cronJob.getName(), - [columnId.namespace]: cronJob => cronJob.getNs(), - [columnId.suspend]: cronJob => cronJob.getSuspendFlag(), - [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), - [columnId.lastSchedule]: cronJob => ( - cronJob.status?.lastScheduleTime - ? moment().diff(cronJob.status.lastScheduleTime) - : 0 - ), - [columnId.age]: cronJob => -cronJob.getCreationTimestamp(), - }} - searchFilters={[ - cronJob => cronJob.getSearchFields(), - cronJob => cronJob.getSchedule(), - ]} - renderHeaderTitle="Cron Jobs" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Schedule", className: "schedule", id: columnId.schedule }, - { title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend }, - { title: "Active", className: "active", sortBy: columnId.active, id: columnId.active }, - { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={cronJob => [ - cronJob.getName(), - , - filterByNamespace(cronJob.getNs()))} - > - {cronJob.getNs()} - , - cronJob.isNeverRun() ? "never" : cronJob.getSchedule(), - cronJob.getSuspendFlag(), - cronJobStore.getActiveJobsNum(cronJob), - cronJob.getLastScheduleTime(), - , - ]} - /> - - ); - } -} + return ( + + cronJob.getName(), + [columnId.namespace]: cronJob => cronJob.getNs(), + [columnId.suspend]: cronJob => cronJob.getSuspendFlag(), + [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), + [columnId.lastSchedule]: cronJob => ( + cronJob.status?.lastScheduleTime + ? moment().diff(cronJob.status.lastScheduleTime) + : 0 + ), + [columnId.age]: cronJob => -cronJob.getCreationTimestamp(), + }} + searchFilters={[ + cronJob => cronJob.getSearchFields(), + cronJob => cronJob.getSchedule(), + ]} + renderHeaderTitle="Cron Jobs" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Schedule", className: "schedule", id: columnId.schedule }, + { title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend }, + { title: "Active", className: "active", sortBy: columnId.active, id: columnId.active }, + { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={cronJob => [ + cronJob.getName(), + , + filterByNamespace(cronJob.getNs()))} + > + {cronJob.getNs()} + , + cronJob.isNeverRun() ? "never" : cronJob.getSchedule(), + cronJob.getSuspendFlag(), + cronJobStore.getActiveJobsNum(cronJob), + cronJob.getLastScheduleTime(), + , + ]} + /> + + ); +}); export const CronJobs = withInjectables(NonInjectedCronJobs, { getProps: (di, props) => ({ ...props, cronJobStore: di.inject(cronJobStoreInjectable), eventStore: di.inject(eventStoreInjectable), - jobStore: di.inject(jobStoreInjectable), filterByNamespace: di.inject(filterByNamespaceInjectable), }), }); diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx index 40c6026659..e159d72ab3 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx @@ -16,22 +16,16 @@ import type { DaemonSetStore } from "./store"; import type { PodStore } from "../+workloads-pods/store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { DaemonSet } from "../../../common/k8s-api/endpoints"; -import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import daemonSetStoreInjectable from "./store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { DaemonSetPodMetricData, RequestPodMetricsForDaemonSets } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; import requestPodMetricsForDaemonSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface DaemonSetDetailsProps extends KubeObjectDetailsProps { } @@ -40,38 +34,21 @@ interface Dependencies { subscribeStores: SubscribeStores; daemonSetStore: DaemonSetStore; podStore: PodStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestPodMetricsForDaemonSets: RequestPodMetricsForDaemonSets; + logger: Logger; } @observer class NonInjectedDaemonSetDetails extends React.Component { - @observable metrics: DaemonSetPodMetricData | null = null; - - constructor(props: DaemonSetDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), this.props.subscribeStores([ this.props.podStore, ]), ]); } - loadMetrics = async () => { - const { object: daemonSet, requestPodMetricsForDaemonSets } = this.props; - - this.metrics = await requestPodMetricsForDaemonSets([daemonSet], daemonSet.getNs()); - }; - render() { - const { object: daemonSet, daemonSetStore, podStore, getActiveClusterEntity } = this.props; + const { object: daemonSet, daemonSetStore, logger } = this.props; if (!daemonSet) { return null; @@ -88,21 +65,9 @@ class NonInjectedDaemonSetDetails extends React.Component - {!isMetricHidden && podStore.isLoaded && ( - - - - )} - {selectors.length > 0 && ( { @@ -132,7 +97,6 @@ class NonInjectedDaemonSetDetails extends React.Component -
); @@ -147,5 +111,6 @@ export const DaemonSetDetails = withInjectables { - getPodsLength(daemonSet: DaemonSet) { - return this.props.daemonSetStore.getChildPods(daemonSet).length; - } +const NonInjectedDaemonSets = observer((props: Dependencies) => { + const { + daemonSetStore, + eventStore, + filterByNamespace, + } = props; - render() { - const { - daemonSetStore, - eventStore, - filterByNamespace, - podStore, - } = this.props; + const getPodsLength = (daemonSet: DaemonSet) => daemonSetStore.getChildPods(daemonSet).length; - return ( - - daemonSet.getName(), - [columnId.namespace]: daemonSet => daemonSet.getNs(), - [columnId.pods]: daemonSet => this.getPodsLength(daemonSet), - [columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(), - }} - searchFilters={[ - daemonSet => daemonSet.getSearchFields(), - daemonSet => daemonSet.getLabels(), - ]} - renderHeaderTitle="Daemon Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, - { className: "warning", showWithColumn: columnId.pods }, - { title: "Node Selector", className: "labels scrollable", id: columnId.labels }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={daemonSet => [ - daemonSet.getName(), - filterByNamespace(daemonSet.getNs()))} - > - {daemonSet.getNs()} - , - this.getPodsLength(daemonSet), - , - daemonSet.getNodeSelectors().map(selector => ( - - )), - , - ]} - /> - - ); - } -} + return ( + + daemonSet.getName(), + [columnId.namespace]: daemonSet => daemonSet.getNs(), + [columnId.pods]: daemonSet => getPodsLength(daemonSet), + [columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(), + }} + searchFilters={[ + daemonSet => daemonSet.getSearchFields(), + daemonSet => daemonSet.getLabels(), + ]} + renderHeaderTitle="Daemon Sets" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, + { className: "warning", showWithColumn: columnId.pods }, + { title: "Node Selector", className: "labels scrollable", id: columnId.labels }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={daemonSet => [ + daemonSet.getName(), + filterByNamespace(daemonSet.getNs()))} + > + {daemonSet.getNs()} + , + getPodsLength(daemonSet), + , + daemonSet.getNodeSelectors().map(selector => ( + + )), + , + ]} + /> + + ); +}); export const DaemonSets = withInjectables(NonInjectedDaemonSets, { getProps: (di, props) => ({ @@ -112,6 +103,5 @@ export const DaemonSets = withInjectables(NonInjectedDaemonSets, { daemonSetStore: di.inject(daemonSetStoreInjectable), eventStore: di.inject(eventStoreInjectable), filterByNamespace: di.inject(filterByNamespaceInjectable), - podStore: di.inject(podStoreInjectable), }), }); diff --git a/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..3975308f46 --- /dev/null +++ b/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx @@ -0,0 +1,53 @@ +/** + * 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 { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { DaemonSet } from "../../../common/k8s-api/endpoints"; +import type { DaemonSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import daemonSetMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedDaemonSetMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const DaemonSetMetricsDetailsComponent = withInjectables>(NonInjectedDaemonSetMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(daemonSetMetricsInjectable, props.object), + ...props, + }), +}); + +const daemonSetMetricsDetailsComponentInjectable = getInjectable({ + id: "daemon-set-metrics-details-component", + instantiate: (di) => ({ + Component: DaemonSetMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.DaemonSet), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default daemonSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts b/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts new file mode 100644 index 0000000000..71f4f54ee9 --- /dev/null +++ b/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { DaemonSet } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsForDaemonSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; + +const daemonSetMetricsInjectable = getInjectable({ + id: "daemon-set-metrics", + instantiate: (di, daemonSet) => { + const requestPodMetricsForDaemonSets = di.inject(requestPodMetricsForDaemonSetsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); // update every minute + + return requestPodMetricsForDaemonSets([daemonSet], daemonSet.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, daemonSet: DaemonSet) => daemonSet.getId(), + }), +}); + +export default daemonSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx index d21d7648d1..6137c7af48 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx @@ -14,70 +14,40 @@ import { Deployment } from "../../../common/k8s-api/endpoints"; import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import type { DeploymentStore } from "./store"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; import type { ReplicaSetStore } from "../+workloads-replicasets/store"; import { DeploymentReplicaSets } from "./deployment-replicasets"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import type { PodStore } from "../+workloads-pods/store"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable"; import deploymentStoreInjectable from "./store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { DeploymentPodMetricData, RequestPodMetricsForDeployments } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; -import requestPodMetricsForDeploymentsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface DeploymentDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { subscribeStores: SubscribeStores; - podStore: PodStore; replicaSetStore: ReplicaSetStore; deploymentStore: DeploymentStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestPodMetricsForDeployments: RequestPodMetricsForDeployments; + logger: Logger; } @observer class NonInjectedDeploymentDetails extends React.Component { - @observable metrics: DeploymentPodMetricData | null = null; - - constructor(props: DeploymentDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), - this.props.subscribeStores([ - this.props.podStore, this.props.replicaSetStore, ]), ]); } - loadMetrics = async () => { - const { object: deployment, requestPodMetricsForDeployments } = this.props; - - this.metrics = await requestPodMetricsForDeployments([deployment], deployment.getNs()); - }; - render() { - const { object: deployment, podStore, replicaSetStore, deploymentStore, getActiveClusterEntity } = this.props; + const { object: deployment, replicaSetStore, deploymentStore, logger } = this.props; if (!deployment) { return null; @@ -94,21 +64,9 @@ class NonInjectedDeploymentDetails extends React.Component - {!isMetricHidden && podStore.isLoaded && ( - - - - )} - {`${spec.replicas} desired, ${status?.updatedReplicas ?? 0} updated, `} {`${status?.replicas ?? 0} total, ${status?.availableReplicas ?? 0} available, `} @@ -159,7 +117,6 @@ class NonInjectedDeploymentDetails extends React.Component - @@ -171,11 +128,9 @@ export const DeploymentDetails = withInjectables ({ ...props, subscribeStores: di.inject(subscribeStoresInjectable), - podStore: di.inject(podStoreInjectable), replicaSetStore: di.inject(replicaSetStoreInjectable), deploymentStore: di.inject(deploymentStoreInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), - requestPodMetricsForDeployments: di.inject(requestPodMetricsForDeploymentsInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx b/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx index 1f73e176db..d46e3e7f31 100644 --- a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx @@ -8,7 +8,6 @@ import "./deployment-replicasets.scss"; import React from "react"; import { observer } from "mobx-react"; import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; import { KubeObjectMenu } from "../kube-object-menu"; import { Spinner } from "../spinner"; import { prevDefault, stopPropagation } from "../../utils"; @@ -30,6 +29,11 @@ enum sortBy { age = "age", } +interface Dependencies { + replicaSetStore: ReplicaSetStore; + showDetails: ShowDetails; +} + export interface DeploymentReplicaSetsProps { replicaSets: ReplicaSet[]; } @@ -96,7 +100,7 @@ class NonInjectedDeploymentReplicaSets extends React.Component{this.getPodsLength(replica)} - + )) @@ -114,9 +118,3 @@ export const DeploymentReplicaSets = withInjectables) { - return ( - - ); -} diff --git a/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..d40e93641d --- /dev/null +++ b/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx @@ -0,0 +1,53 @@ +/** + * 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 { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Deployment } from "../../../common/k8s-api/endpoints"; +import type { DeploymentPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import deploymentMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedDeploymentMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const DeploymentMetricsDetailsComponent = withInjectables>(NonInjectedDeploymentMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(deploymentMetricsInjectable, props.object), + ...props, + }), +}); + +const deploymentMetricsDetailsComponentInjectable = getInjectable({ + id: "deployment-metrics-details-component", + instantiate: (di) => ({ + Component: DeploymentMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Deployment), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default deploymentMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/metrics.injectable.ts b/src/renderer/components/+workloads-deployments/metrics.injectable.ts new file mode 100644 index 0000000000..fecd67c5db --- /dev/null +++ b/src/renderer/components/+workloads-deployments/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Deployment } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsForDeploymentsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; + +const deploymentMetricsInjectable = getInjectable({ + id: "deployment-metrics", + instantiate: (di, deployment) => { + const requestPodMetricsForDeployments = di.inject(requestPodMetricsForDeploymentsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); + + return requestPodMetricsForDeployments([deployment], deployment.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, deployment: Deployment) => deployment.getId(), + }), +}); + +export default deploymentMetricsInjectable; diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index fbfb272eab..388032cac7 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -17,22 +17,14 @@ import type { JobStore } from "./store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Job } from "../../../common/k8s-api/endpoints"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { makeObservable, observable, reaction } from "mobx"; -import { podMetricTabs, PodCharts } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import { ResourceMetrics } from "../resource-metrics"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import type { PodStore } from "../+workloads-pods/store"; import podStoreInjectable from "../+workloads-pods/store.injectable"; import jobStoreInjectable from "./store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { JobPodMetricData, RequestPodMetricsForJobs } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; -import requestPodMetricsForJobsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface JobDetailsProps extends KubeObjectDetailsProps { } @@ -41,38 +33,21 @@ interface Dependencies { subscribeStores: SubscribeStores; podStore: PodStore; jobStore: JobStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestPodMetricsForJobs: RequestPodMetricsForJobs; + logger: Logger; } @observer class NonInjectedJobDetails extends React.Component { - @observable metrics: JobPodMetricData | null = null; - - constructor(props: JobDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), this.props.subscribeStores([ this.props.podStore, ]), ]); } - loadMetrics = async () => { - const { object: job, requestPodMetricsForJobs } = this.props; - - this.metrics = await requestPodMetricsForJobs([job], job.getNs(), ""); - }; - render() { - const { object: job, jobStore, getActiveClusterEntity } = this.props; + const { object: job, jobStore, logger } = this.props; if (!job) { return null; @@ -89,21 +64,9 @@ class NonInjectedJobDetails extends React.Component - {!isMetricHidden && ( - - - - )} - { Object.keys(selectors).map(label => ) @@ -161,8 +124,7 @@ export const JobDetails = withInjectables(NonInje subscribeStores: di.inject(subscribeStoresInjectable), podStore: di.inject(podStoreInjectable), jobStore: di.inject(jobStoreInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), - requestPodMetricsForJobs: di.inject(requestPodMetricsForJobsInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+workloads-jobs/jobs.tsx b/src/renderer/components/+workloads-jobs/jobs.tsx index f1455bd2f8..c0bc9e2d66 100644 --- a/src/renderer/components/+workloads-jobs/jobs.tsx +++ b/src/renderer/components/+workloads-jobs/jobs.tsx @@ -35,67 +35,64 @@ interface Dependencies { filterByNamespace: FilterByNamespace; } -@observer -class NonInjectedJobs extends React.Component { - render() { - const { - eventStore, - filterByNamespace, - jobStore, - } = this.props; +const NonInjectedJobs = observer((props: Dependencies) => { + const { + eventStore, + filterByNamespace, + jobStore, + } = props; - return ( - - job.getName(), - [columnId.namespace]: job => job.getNs(), - [columnId.conditions]: job => job.getCondition()?.type, - [columnId.age]: job => -job.getCreationTimestamp(), - }} - searchFilters={[ - job => job.getSearchFields(), - ]} - renderHeaderTitle="Jobs" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Completions", className: "completions", id: columnId.completions }, - { className: "warning", showWithColumn: columnId.completions }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, - ]} - renderTableContents={job => { - const condition = job.getCondition(); + return ( + + job.getName(), + [columnId.namespace]: job => job.getNs(), + [columnId.conditions]: job => job.getCondition()?.type, + [columnId.age]: job => -job.getCreationTimestamp(), + }} + searchFilters={[ + job => job.getSearchFields(), + ]} + renderHeaderTitle="Jobs" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Completions", className: "completions", id: columnId.completions }, + { className: "warning", showWithColumn: columnId.completions }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, + ]} + renderTableContents={job => { + const condition = job.getCondition(); - return [ - job.getName(), - filterByNamespace(job.getNs()))} - > - {job.getNs()} - , - `${job.getCompletions()} / ${job.getDesiredCompletions()}`, - , - , - condition && { - title: condition.type, - className: kebabCase(condition.type), - }, - ]; - }} - /> - - ); - } -} + return [ + job.getName(), + filterByNamespace(job.getNs()))} + > + {job.getNs()} + , + `${job.getCompletions()} / ${job.getDesiredCompletions()}`, + , + , + condition && { + title: condition.type, + className: kebabCase(condition.type), + }, + ]; + }} + /> + + ); +}); export const Jobs = withInjectables(NonInjectedJobs, { getProps: (di, props) => ({ diff --git a/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..479086c17c --- /dev/null +++ b/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx @@ -0,0 +1,52 @@ +/** + * 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 IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Job } from "../../../common/k8s-api/endpoints"; +import type { JobPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import jobMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedJobMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const JobMetricsDetailsComponent = withInjectables>(NonInjectedJobMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(jobMetricsInjectable, props.object), + ...props, + }), +}); + +const jobMetricsDetailsComponentInjectable = getInjectable({ + id: "job-metrics-details-component", + instantiate: (di) => ({ + Component: JobMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Job), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default jobMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-jobs/metrics.injectable.ts b/src/renderer/components/+workloads-jobs/metrics.injectable.ts new file mode 100644 index 0000000000..9922dc6ad1 --- /dev/null +++ b/src/renderer/components/+workloads-jobs/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Job } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsForJobsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; + +const jobMetricsInjectable = getInjectable({ + id: "job-metrics", + instantiate: (di, job) => { + const requestPodMetricsForJobs = di.inject(requestPodMetricsForJobsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); + + return requestPodMetricsForJobs([job], job.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, job: Job) => job.getId(), + }), +}); + +export default jobMetricsInjectable; diff --git a/src/renderer/components/+workloads-pods/container-metrics.injectable.ts b/src/renderer/components/+workloads-pods/container-metrics.injectable.ts new file mode 100644 index 0000000000..dccd1668ef --- /dev/null +++ b/src/renderer/components/+workloads-pods/container-metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Pod } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; + +const podContainerMetricsInjectable = getInjectable({ + id: "pod-container-metrics", + instantiate: (di, pod) => { + const requestPodMetrics = di.inject(requestPodMetricsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); + + return requestPodMetrics([pod], pod.getNs(), "container, namespace"); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, pod: Pod) => pod.getId(), + }), +}); + +export default podContainerMetricsInjectable; diff --git a/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx b/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx new file mode 100644 index 0000000000..aab325ab9b --- /dev/null +++ b/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx @@ -0,0 +1,52 @@ +/** + * 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 IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { Pod } from "../../../common/k8s-api/endpoints"; +import type { PodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import podMetricsInjectable from "./metrics.injectable"; +import { PodCharts, podMetricTabs } from "./pod-charts"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedPodMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const PodMetricsDetailsComponent = withInjectables>(NonInjectedPodMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(podMetricsInjectable, props.object), + ...props, + }), +}); + +const podMetricsDetailsComponentInjectable = getInjectable({ + id: "pod-metrics-details-container", + instantiate: (di) => ({ + Component: PodMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Pod), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default podMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-pods/metrics.injectable.ts b/src/renderer/components/+workloads-pods/metrics.injectable.ts new file mode 100644 index 0000000000..a06bc9e7e9 --- /dev/null +++ b/src/renderer/components/+workloads-pods/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { Pod } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; + +const podMetricsInjectable = getInjectable({ + id: "pod-metrics", + instantiate: (di, pod) => { + const requestPodMetrics = di.inject(requestPodMetricsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: () => { + now(60 * 1000); + + return requestPodMetrics([pod], pod.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, pod: Pod) => pod.getId(), + }), +}); + +export default podMetricsInjectable; diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx index 726c5326b6..604cec5cd5 100644 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ b/src/renderer/components/+workloads-pods/pod-details.tsx @@ -7,9 +7,8 @@ import "./pod-details.scss"; import React from "react"; import kebabCase from "lodash/kebabCase"; -import { disposeOnUnmount, observer } from "mobx-react"; +import { observer } from "mobx-react"; import { Link } from "react-router-dom"; -import { observable, reaction, makeObservable } from "mobx"; import { Pod } from "../../../common/k8s-api/endpoints"; import type { NodeApi, PriorityClassApi, RuntimeClassApi, ServiceAccountApi } from "../../../common/k8s-api/endpoints"; import { DrawerItem, DrawerTitle } from "../drawer"; @@ -19,67 +18,39 @@ import { PodDetailsContainer } from "./pod-details-container"; import { PodDetailsAffinities } from "./pod-details-affinities"; import { PodDetailsTolerations } from "./pod-details-tolerations"; import { PodDetailsSecrets } from "./pod-details-secrets"; -import { ResourceMetrics } from "../resource-metrics"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { getItemMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; -import { PodCharts, podMetricTabs } from "./pod-charts"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { PodVolumes } from "./details/volumes/view"; -import type { PodMetricData, RequestPodMetrics } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; +import type { IAsyncComputed } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react"; -import requestPodMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable"; import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable"; import serviceAccountApiInjectable from "../../../common/k8s-api/endpoints/service-account.api.injectable"; import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { PodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; +import podContainerMetricsInjectable from "./container-metrics.injectable"; export interface PodDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { - requestPodMetrics: RequestPodMetrics; - getActiveClusterEntity: GetActiveClusterEntity; getDetailsUrl: GetDetailsUrl; nodeApi: NodeApi; priorityClassApi: PriorityClassApi; runtimeClassApi: RuntimeClassApi; serviceAccountApi: ServiceAccountApi; + logger: Logger; + containerMetrics: IAsyncComputed; } @observer class NonInjectedPodDetails extends React.Component { - @observable metrics: PodMetricData | null = null; - @observable containerMetrics: PodMetricData | null = null; - - constructor(props: PodDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - this.containerMetrics = null; - }), - ]); - } - - loadMetrics = async () => { - const { object: pod, requestPodMetrics } = this.props; - - this.metrics = await requestPodMetrics([pod], pod.getNs()); - this.containerMetrics = await requestPodMetrics([pod], pod.getNs(), "container, namespace"); - }; - render() { - const { object: pod, getActiveClusterEntity, getDetailsUrl, nodeApi } = this.props; + const { object: pod, getDetailsUrl, nodeApi, logger, containerMetrics } = this.props; if (!pod) { return null; @@ -96,7 +67,6 @@ class NonInjectedPodDetails extends React.Component - {!isMetricHidden && ( - - - - )} - - - {pod.getStatusMessage()} @@ -230,7 +187,7 @@ class NonInjectedPodDetails extends React.Component ))} @@ -243,12 +200,12 @@ class NonInjectedPodDetails extends React.Component(NonInjectedPodDetails, { getProps: (di, props) => ({ ...props, - requestPodMetrics: di.inject(requestPodMetricsInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), nodeApi: di.inject(nodeApiInjectable), priorityClassApi: di.inject(priorityClassApiInjectable), runtimeClassApi: di.inject(runtimeClassApiInjectable), serviceAccountApi: di.inject(serviceAccountApiInjectable), + logger: di.inject(loggerInjectable), + containerMetrics: di.inject(podContainerMetricsInjectable, props.object), }), }); diff --git a/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..9c57bea78e --- /dev/null +++ b/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx @@ -0,0 +1,52 @@ +/** + * 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 IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; +import type { ReplicaSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import replicaSetMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedReplicaSetMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const ReplicaSetMetricsDetailsComponent = withInjectables>(NonInjectedReplicaSetMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(replicaSetMetricsInjectable, props.object), + ...props, + }), +}); + +const replicaSetMetricsDetailsComponentInjectable = getInjectable({ + id: "replica-set-metrics-details-component", + instantiate: (di) => ({ + Component: ReplicaSetMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.ReplicaSet), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default replicaSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-replicasets/metrics.injectable.ts b/src/renderer/components/+workloads-replicasets/metrics.injectable.ts new file mode 100644 index 0000000000..47a41bd253 --- /dev/null +++ b/src/renderer/components/+workloads-replicasets/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsForReplicaSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; + +const replicaSetMetricsInjectable = getInjectable({ + id: "replica-set-metrics", + instantiate: (di, replicaSet) => { + const requestPodMetricsForReplicaSets = di.inject(requestPodMetricsForReplicaSetsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: async () => { + now(60 * 1000); // update every minute + + return requestPodMetricsForReplicaSets([replicaSet], replicaSet.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, replicaSet: ReplicaSet) => replicaSet.getId(), + }), +}); + +export default replicaSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx index cf92b7976c..72e73c0726 100644 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx +++ b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx @@ -5,7 +5,6 @@ import "./replicaset-details.scss"; import React from "react"; -import { makeObservable, observable, reaction } from "mobx"; import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; @@ -14,12 +13,8 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import { disposeOnUnmount, observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; @@ -27,10 +22,7 @@ import type { PodStore } from "../+workloads-pods/store"; import podStoreInjectable from "../+workloads-pods/store.injectable"; import type { ReplicaSetStore } from "./store"; import replicaSetStoreInjectable from "./store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { ReplicaSetPodMetricData, RequestPodMetricsForReplicaSets } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; -import requestPodMetricsForReplicaSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface ReplicaSetDetailsProps extends KubeObjectDetailsProps { } @@ -39,39 +31,20 @@ interface Dependencies { subscribeStores: SubscribeStores; podStore: PodStore; replicaSetStore: ReplicaSetStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestPodMetricsForReplicaSets: RequestPodMetricsForReplicaSets; + logger: Logger; } @observer class NonInjectedReplicaSetDetails extends React.Component { - @observable metrics: ReplicaSetPodMetricData | null = null; - - constructor(props: ReplicaSetDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), - this.props.subscribeStores([ this.props.podStore, ]), ]); } - - loadMetrics = async () => { - const { object: replicaSet, requestPodMetricsForReplicaSets } = this.props; - - this.metrics = await requestPodMetricsForReplicaSets([replicaSet], replicaSet.getNs()); - }; - render() { - const { object: replicaSet, podStore, replicaSetStore, getActiveClusterEntity } = this.props; + const { object: replicaSet, replicaSetStore, logger } = this.props; if (!replicaSet) { return null; @@ -88,21 +61,9 @@ class NonInjectedReplicaSetDetails extends React.Component - {!isMetricHidden && podStore.isLoaded && ( - - - - )} - {selectors.length > 0 && ( { @@ -132,7 +93,6 @@ class NonInjectedReplicaSetDetails extends React.Component - ); @@ -145,7 +105,6 @@ export const ReplicaSetDetails = withInjectables { - render() { - const { - eventStore, - filterByNamespace, - replicaSetStore, - } = this.props; +const NonInjectedReplicaSets = observer((props: Dependencies) => { + const { + eventStore, + filterByNamespace, + replicaSetStore, + } = props; - return ( - - replicaSet.getName(), - [columnId.namespace]: replicaSet => replicaSet.getNs(), - [columnId.desired]: replicaSet => replicaSet.getDesired(), - [columnId.current]: replicaSet => replicaSet.getCurrent(), - [columnId.ready]: replicaSet => replicaSet.getReady(), - [columnId.age]: replicaSet => -replicaSet.getCreationTimestamp(), - }} - searchFilters={[ - replicaSet => replicaSet.getSearchFields(), - ]} - renderHeaderTitle="Replica Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Desired", className: "desired", sortBy: columnId.desired, id: columnId.desired }, - { title: "Current", className: "current", sortBy: columnId.current, id: columnId.current }, - { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={replicaSet => [ - replicaSet.getName(), - , - filterByNamespace(replicaSet.getNs()))} - > - {replicaSet.getNs()} - , - replicaSet.getDesired(), - replicaSet.getCurrent(), - replicaSet.getReady(), - , - ]} - /> - - ); - } -} + return ( + + replicaSet.getName(), + [columnId.namespace]: replicaSet => replicaSet.getNs(), + [columnId.desired]: replicaSet => replicaSet.getDesired(), + [columnId.current]: replicaSet => replicaSet.getCurrent(), + [columnId.ready]: replicaSet => replicaSet.getReady(), + [columnId.age]: replicaSet => -replicaSet.getCreationTimestamp(), + }} + searchFilters={[ + replicaSet => replicaSet.getSearchFields(), + ]} + renderHeaderTitle="Replica Sets" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Desired", className: "desired", sortBy: columnId.desired, id: columnId.desired }, + { title: "Current", className: "current", sortBy: columnId.current, id: columnId.current }, + { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={replicaSet => [ + replicaSet.getName(), + , + filterByNamespace(replicaSet.getNs()))} + > + {replicaSet.getNs()} + , + replicaSet.getDesired(), + replicaSet.getCurrent(), + replicaSet.getReady(), + , + ]} + /> + + ); +}); export const ReplicaSets = withInjectables(NonInjectedReplicaSets, { getProps: (di, props) => ({ diff --git a/src/renderer/components/+workloads-replicasets/store.ts b/src/renderer/components/+workloads-replicasets/store.ts index 6db50809f4..d782846cd0 100644 --- a/src/renderer/components/+workloads-replicasets/store.ts +++ b/src/renderer/components/+workloads-replicasets/store.ts @@ -9,12 +9,12 @@ import { PodStatusPhase } from "../../../common/k8s-api/endpoints/pod.api"; import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -export interface ReplicaSetStoreDependencies { +interface Dependencies { getPodsByOwnerId: GetPodsByOwnerId; } export class ReplicaSetStore extends KubeObjectStore { - constructor(protected readonly dependencies: ReplicaSetStoreDependencies, api: ReplicaSetApi, opts?: KubeObjectStoreOptions) { + constructor(protected readonly dependencies: Dependencies, api: ReplicaSetApi, opts?: KubeObjectStoreOptions) { super(api, opts); } diff --git a/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx new file mode 100644 index 0000000000..ad4bb6d677 --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx @@ -0,0 +1,52 @@ +/** + * 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 IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; +import React from "react"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import type { StatefulSet } from "../../../common/k8s-api/endpoints"; +import type { StatefulSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; +import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; +import { ResourceMetrics } from "../resource-metrics"; +import statefulSetMetricsInjectable from "./metrics.injectable"; + +interface Dependencies { + metrics: IAsyncComputed; +} + +const NonInjectedStatefulSetMetricsDetailsComponent = ({ + object, + metrics, +}: KubeObjectDetailsProps & Dependencies) => ( + + + +); + +const StatefulSetMetricsDetailsComponent = withInjectables>(NonInjectedStatefulSetMetricsDetailsComponent, { + getProps: (di, props) => ({ + metrics: di.inject(statefulSetMetricsInjectable, props.object), + ...props, + }), +}); + +const statefulSetMetricsDetailsComponentInjectable = getInjectable({ + id: "stateful-set-metrics-details-component", + instantiate: (di) => ({ + Component: StatefulSetMetricsDetailsComponent, + enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.StatefulSet), + orderNumber: -1, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default statefulSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts b/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts new file mode 100644 index 0000000000..b12cd152e9 --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import { now } from "mobx-utils"; +import type { StatefulSet } from "../../../common/k8s-api/endpoints"; +import requestPodMetricsForStatefulSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; + +const statefulSetMetricsInjectable = getInjectable({ + id: "stateful-set-metrics", + instantiate: (di, statefulSet) => { + const requestPodMetricsForStatefulSets = di.inject(requestPodMetricsForStatefulSetsInjectable); + + return asyncComputed({ + getValueFromObservedPromise: async () => { + now(60 * 1000); + + return requestPodMetricsForStatefulSets([statefulSet], statefulSet.getNs()); + }, + }); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, statefulSet: StatefulSet) => statefulSet.getId(), + }), +}); + +export default statefulSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx index 83fb856619..5c9eff2e84 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx @@ -7,7 +7,6 @@ import "./statefulset-details.scss"; import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; -import { makeObservable, observable, reaction } from "mobx"; import { Badge } from "../badge"; import { DrawerItem } from "../drawer"; import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; @@ -16,22 +15,15 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import type { StatefulSetStore } from "./store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { StatefulSet } from "../../../common/k8s-api/endpoints"; -import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { KubeObjectMeta } from "../kube-object-meta"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import type { PodStore } from "../+workloads-pods/store"; import podStoreInjectable from "../+workloads-pods/store.injectable"; import statefulSetStoreInjectable from "./store.injectable"; -import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable"; -import type { RequestPodMetricsForStatefulSets, StatefulSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; -import requestPodMetricsForStatefulSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface StatefulSetDetailsProps extends KubeObjectDetailsProps { } @@ -40,39 +32,21 @@ interface Dependencies { subscribeStores: SubscribeStores; podStore: PodStore; statefulSetStore: StatefulSetStore; - getActiveClusterEntity: GetActiveClusterEntity; - requestPodMetricsForStatefulSets: RequestPodMetricsForStatefulSets; + logger: Logger; } @observer class NonInjectedStatefulSetDetails extends React.Component { - @observable metrics: StatefulSetPodMetricData | null = null; - - constructor(props: StatefulSetDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.metrics = null; - }), - this.props.subscribeStores([ this.props.podStore, ]), ]); } - loadMetrics = async () => { - const { object: statefulSet, requestPodMetricsForStatefulSets } = this.props; - - this.metrics = await requestPodMetricsForStatefulSets([statefulSet], statefulSet.getNs()); - }; - render() { - const { object: statefulSet, podStore, statefulSetStore, getActiveClusterEntity } = this.props; + const { object: statefulSet, statefulSetStore, logger } = this.props; if (!statefulSet) { return null; @@ -88,21 +62,9 @@ class NonInjectedStatefulSetDetails extends React.Component - {!isMetricHidden && podStore.isLoaded && ( - - - - )} - {selectors.length && ( { @@ -131,7 +93,6 @@ class NonInjectedStatefulSetDetails extends React.Component - ); @@ -144,8 +105,7 @@ export const StatefulSetDetails = withInjectables { - renderPods(statefulSet: StatefulSet) { - const { readyReplicas, currentReplicas } = statefulSet.status ?? {}; +const renderPodCounts = (statefulSet: StatefulSet) => { + const { readyReplicas, currentReplicas } = statefulSet.status ?? {}; - return `${readyReplicas || 0}/${currentReplicas || 0}`; - } + return `${readyReplicas || 0}/${currentReplicas || 0}`; +}; - render() { - const { - eventStore, - filterByNamespace, - podStore, - statefulSetStore, - } = this.props; +const NonInjectedStatefulSets = observer((props: Dependencies) => { + const { + eventStore, + filterByNamespace, + statefulSetStore, + } = props; - return ( - - statefulSet.getName(), - [columnId.namespace]: statefulSet => statefulSet.getNs(), - [columnId.age]: statefulSet => -statefulSet.getCreationTimestamp(), - [columnId.replicas]: statefulSet => statefulSet.getReplicas(), - }} - searchFilters={[ - statefulSet => statefulSet.getSearchFields(), - ]} - renderHeaderTitle="Stateful Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Pods", className: "pods", id: columnId.pods }, - { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, - { className: "warning", showWithColumn: columnId.replicas }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={statefulSet => [ - statefulSet.getName(), - filterByNamespace(statefulSet.getNs()))} - > - {statefulSet.getNs()} - , - this.renderPods(statefulSet), - statefulSet.getReplicas(), - , - , - ]} - /> - - ); - } -} + return ( + + statefulSet.getName(), + [columnId.namespace]: statefulSet => statefulSet.getNs(), + [columnId.age]: statefulSet => -statefulSet.getCreationTimestamp(), + [columnId.replicas]: statefulSet => statefulSet.getReplicas(), + }} + searchFilters={[ + statefulSet => statefulSet.getSearchFields(), + ]} + renderHeaderTitle="Stateful Sets" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Pods", className: "pods", id: columnId.pods }, + { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, + { className: "warning", showWithColumn: columnId.replicas }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={statefulSet => [ + statefulSet.getName(), + filterByNamespace(statefulSet.getNs()))} + > + {statefulSet.getNs()} + , + renderPodCounts(statefulSet), + statefulSet.getReplicas(), + , + , + ]} + /> + + ); +}); export const StatefulSets = withInjectables(NonInjectedStatefulSets, { getProps: (di, props) => ({ ...props, eventStore: di.inject(eventStoreInjectable), filterByNamespace: di.inject(filterByNamespaceInjectable), - podStore: di.inject(podStoreInjectable), statefulSetStore: di.inject(statefulSetStoreInjectable), }), }); diff --git a/src/renderer/components/kube-object-details/current-kube-object-in-details.injectable.ts b/src/renderer/components/kube-object-details/current-kube-object-in-details.injectable.ts index 3727c90395..a68e1672b0 100644 --- a/src/renderer/components/kube-object-details/current-kube-object-in-details.injectable.ts +++ b/src/renderer/components/kube-object-details/current-kube-object-in-details.injectable.ts @@ -3,10 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; +import { asyncComputed } from "@ogre-tools/injectable-react"; +import type { KubeObject } from "../../../common/k8s-api/kube-object"; + +export type CurrentKubeObject = + | undefined + | { object: KubeObject; error?: undefined } + | { object?: undefined; error: string }; const currentKubeObjectInDetailsInjectable = getInjectable({ id: "current-kube-object-in-details", @@ -14,20 +19,24 @@ const currentKubeObjectInDetailsInjectable = getInjectable({ instantiate: (di) => { const urlParam = di.inject(kubeDetailsUrlParamInjectable); const apiManager = di.inject(apiManagerInjectable); - const logger = di.inject(loggerInjectable); - return computed(() => { - const path = urlParam.get(); + return asyncComputed({ + getValueFromObservedPromise: async (): Promise => { + const path = urlParam.get(); + const store = apiManager.getStore(path); - try { - return apiManager.getStore(path)?.getByPath(path); - } catch (error) { - logger.error( - `[KUBE-OBJECT-DETAILS]: failed to get store or object ${path}: ${error}`, - ); + if (!store) { + return undefined; + } - return undefined; - } + try { + const object = await store.loadFromPath(path); + + return { object }; + } catch (error) { + return { error: String(error) }; + } + }, }); }, }); diff --git a/src/renderer/components/kube-object-details/custom-resource-detail-item.injectable.tsx b/src/renderer/components/kube-object-details/custom-resource-detail-item.injectable.tsx new file mode 100644 index 0000000000..62b469d5de --- /dev/null +++ b/src/renderer/components/kube-object-details/custom-resource-detail-item.injectable.tsx @@ -0,0 +1,37 @@ +/** + * 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 React from "react"; +import { CustomResourceDetails } from "../+custom-resources"; +import customResourceDefinitionStoreInjectable from "../+custom-resources/definition.store.injectable"; +import currentKubeObjectInDetailsInjectable from "./current-kube-object-in-details.injectable"; +import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-items/kube-object-detail-item-injection-token"; + +const customResourceDetailItemInjectable = getInjectable({ + id: "custom-resource-detail-item", + instantiate: (di) => { + const customResourceDefinitionStore = di.inject(customResourceDefinitionStoreInjectable); + const currentKubeObjectInDetails = di.inject(currentKubeObjectInDetailsInjectable); + const currentCustomResourceDefinition = computed(() => { + const { object } = currentKubeObjectInDetails.value.get() ?? {}; + + if (!object) { + return undefined; + } + + return customResourceDefinitionStore.getByObject(object); + }); + + return { + Component: ({ object }) => , + enabled: computed(() => Boolean(currentCustomResourceDefinition.get())), + orderNumber: 100, + }; + }, + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default customResourceDetailItemInjectable; diff --git a/src/renderer/components/kube-object-details/default-kube-meta-details-item.injectable.ts b/src/renderer/components/kube-object-details/default-kube-meta-details-item.injectable.ts new file mode 100644 index 0000000000..03e413c28d --- /dev/null +++ b/src/renderer/components/kube-object-details/default-kube-meta-details-item.injectable.ts @@ -0,0 +1,20 @@ +/** + * 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 { KubeObjectMeta } from "../kube-object-meta"; +import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-items/kube-object-detail-item-injection-token"; + +const defaultKubeObjectMetaDetailsItemInjectable = getInjectable({ + id: "default-kube-object-meta-details-item", + instantiate: () => ({ + Component: KubeObjectMeta, + enabled: computed(() => true), + orderNumber: 0, + }), + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export default defaultKubeObjectMetaDetailsItemInjectable; diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-binding-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-binding-detail-item.injectable.ts index ad70d9245a..eba49112c1 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-binding-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-binding-detail-item.injectable.ts @@ -17,7 +17,7 @@ const clusterRoleBindingDetailItemInjectable = getInjectable({ return { Component: ClusterRoleBindingDetails, - enabled: computed(() => isClusterRoleBinding(kubeObject.get())), + enabled: computed(() => isClusterRoleBinding(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-detail-item.injectable.ts index 11c0769e58..dff631a1d9 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cluster-role-detail-item.injectable.ts @@ -17,7 +17,7 @@ const clusterRoleDetailItemInjectable = getInjectable({ return { Component: ClusterRoleDetails, - enabled: computed(() => isClusterRole(kubeObject.get())), + enabled: computed(() => isClusterRole(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/config-map-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/config-map-detail-item.injectable.ts index 7f0f37a589..111aa1f3a1 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/config-map-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/config-map-detail-item.injectable.ts @@ -17,7 +17,7 @@ const configMapDetailItemInjectable = getInjectable({ return { Component: ConfigMapDetails, - enabled: computed(() => isConfigMap(kubeObject.get())), + enabled: computed(() => isConfigMap(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cron-job-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cron-job-detail-item.injectable.ts index 1d268b4b0d..350dafcb64 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cron-job-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/cron-job-detail-item.injectable.ts @@ -17,7 +17,7 @@ const cronJobDetailItemInjectable = getInjectable({ return { Component: CronJobDetails, - enabled: computed(() => isCronJob(kubeObject.get())), + enabled: computed(() => isCronJob(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/custom-resource-definitions-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/custom-resource-definitions-detail-item.injectable.ts index b459408ed3..432978cc13 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/custom-resource-definitions-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/custom-resource-definitions-detail-item.injectable.ts @@ -17,7 +17,7 @@ const customResourceDefinitionsDetailItemInjectable = getInjectable({ return { Component: CRDDetails, - enabled: computed(() => isCustomResourceDefinition(kubeObject.get())), + enabled: computed(() => isCustomResourceDefinition(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/daemon-set-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/daemon-set-detail-item.injectable.ts index 32ca10f938..4845dd3a46 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/daemon-set-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/daemon-set-detail-item.injectable.ts @@ -17,7 +17,7 @@ const daemonSetDetailItemInjectable = getInjectable({ return { Component: DaemonSetDetails, - enabled: computed(() => isDaemonSet(kubeObject.get())), + enabled: computed(() => isDaemonSet(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/deployment-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/deployment-detail-item.injectable.ts index 6ecb291702..8100b27c9e 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/deployment-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/deployment-detail-item.injectable.ts @@ -17,7 +17,7 @@ const deploymentDetailItemInjectable = getInjectable({ return { Component: DeploymentDetails, - enabled: computed(() => isDeployment(kubeObject.get())), + enabled: computed(() => isDeployment(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/endpoints-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/endpoints-detail-item.injectable.ts index c8b0e149e3..be7a3de219 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/endpoints-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/endpoints-detail-item.injectable.ts @@ -17,7 +17,7 @@ const endpointsDetailItemInjectable = getInjectable({ return { Component: EndpointsDetails, - enabled: computed(() => isEndpoint(kubeObject.get())), + enabled: computed(() => isEndpoint(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/events-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/events-detail-item.injectable.ts index 9263a5796f..fbb792ae38 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/events-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/events-detail-item.injectable.ts @@ -17,7 +17,7 @@ const eventsDetailItemInjectable = getInjectable({ return { Component: EventDetails, - enabled: computed(() => isEvent(kubeObject.get())), + enabled: computed(() => isEvent(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts index 85566eedf7..66dbebe7c5 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts @@ -17,7 +17,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({ return { Component: HpaDetails, - enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.get())), + enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-detail-item.injectable.ts index 86d4103197..77fa5ec86b 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-detail-item.injectable.ts @@ -17,7 +17,7 @@ const ingressDetailItemInjectable = getInjectable({ return { Component: IngressDetails, - enabled: computed(() => isIngress(kubeObject.get())), + enabled: computed(() => isIngress(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/job-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/job-detail-item.injectable.ts index 7c32c726e9..1d5d7642ca 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/job-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/job-detail-item.injectable.ts @@ -17,7 +17,7 @@ const jobDetailItemInjectable = getInjectable({ return { Component: JobDetails, - enabled: computed(() => isJob(kubeObject.get())), + enabled: computed(() => isJob(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/kube-event-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/kube-event-detail-item.injectable.ts index 1ca98ad704..f0d20862e8 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/kube-event-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/kube-event-detail-item.injectable.ts @@ -6,77 +6,15 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token"; import { KubeEventDetails } from "../../../+events/kube-event-details"; -import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable"; -import { isClusterRole } from "./cluster-role-detail-item.injectable"; -import { isClusterRoleBinding } from "./cluster-role-binding-detail-item.injectable"; -import { isCronJob } from "./cron-job-detail-item.injectable"; -import { isDaemonSet } from "./daemon-set-detail-item.injectable"; -import { isDeployment } from "./deployment-detail-item.injectable"; -import { isEndpoint } from "./endpoints-detail-item.injectable"; -import { isHorizontalPodAutoscaler } from "./horizontal-pod-autoscaler-detail-item.injectable"; -import { isIngress } from "./ingress-detail-item.injectable"; -import { isJob } from "./job-detail-item.injectable"; -import { isNetworkPolicy } from "./network-policy-detail-item.injectable"; -import { isPersistentVolume } from "./persistent-volume-detail-item.injectable"; -import { isPersistentVolumeClaim } from "./persistent-volume-claim-detail-item.injectable"; -import { isNode } from "./node-detail-item.injectable"; -import { isPod } from "./pod-detail-item.injectable"; -import { isReplicaSet } from "./replica-set-detail-item.injectable"; -import { isRole } from "./role-detail-item.injectable"; -import { isRoleBinding } from "./role-binding-detail-item.injectable"; -import { isService } from "./service-detail-item.injectable"; -import { isServiceAccount } from "./service-account-detail-item.injectable"; -import { isStatefulSet } from "./stateful-set-detail-item.injectable"; -import { isStorageClass } from "./storage-class-detail-item.injectable"; const kubeEventDetailItemInjectable = getInjectable({ id: "kube-event-detail-item", - instantiate: (di) => { - const currentKubeObjectInDetails = di.inject( - currentKubeObjectInDetailsInjectable, - ); - - return { - Component: KubeEventDetails, - - enabled: computed(() => { - const kubeObject = currentKubeObjectInDetails.get(); - - if (!kubeObject) { - return false; - } - - const predicates = [ - isClusterRole, - isClusterRoleBinding, - isCronJob, - isDaemonSet, - isDeployment, - isEndpoint, - isHorizontalPodAutoscaler, - isIngress, - isJob, - isNetworkPolicy, - isNode, - isPersistentVolume, - isPersistentVolumeClaim, - isPod, - isReplicaSet, - isRole, - isRoleBinding, - isService, - isServiceAccount, - isStatefulSet, - isStorageClass, - ]; - - return predicates.some((predicate) => predicate(kubeObject)); - }), - - orderNumber: 355, - }; - }, + instantiate: () => ({ + Component: KubeEventDetails, + enabled: computed(() => true), + orderNumber: Infinity, + }), injectionToken: kubeObjectDetailItemInjectionToken, }); diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/lease-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/lease-detail-item.injectable.ts index baaabbf730..2bb7153028 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/lease-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/lease-detail-item.injectable.ts @@ -17,7 +17,7 @@ const leaseDetailItemInjectable = getInjectable({ return { Component: LeaseDetails, - enabled: computed(() => isLease(kubeObject.get())), + enabled: computed(() => isLease(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/limit-range-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/limit-range-detail-item.injectable.ts index 209bb86dde..f264ba32cf 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/limit-range-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/limit-range-detail-item.injectable.ts @@ -17,7 +17,7 @@ const limitRangeDetailItemInjectable = getInjectable({ return { Component: LimitRangeDetails, - enabled: computed(() => isLimitRange(kubeObject.get())), + enabled: computed(() => isLimitRange(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/namespaces-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/namespaces-detail-item.injectable.ts index 180a05805c..4a7b13c6d1 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/namespaces-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/namespaces-detail-item.injectable.ts @@ -17,7 +17,7 @@ const namespacesDetailItemInjectable = getInjectable({ return { Component: NamespaceDetails, - enabled: computed(() => isNamespace(kubeObject.get())), + enabled: computed(() => isNamespace(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/network-policy-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/network-policy-detail-item.injectable.ts index ff7d7f4a7f..53b6d57355 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/network-policy-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/network-policy-detail-item.injectable.ts @@ -17,7 +17,7 @@ const networkPolicyDetailItemInjectable = getInjectable({ return { Component: NetworkPolicyDetails, - enabled: computed(() => isNetworkPolicy(kubeObject.get())), + enabled: computed(() => isNetworkPolicy(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/node-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/node-detail-item.injectable.ts index 254187d01e..4ed6f66d7e 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/node-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/node-detail-item.injectable.ts @@ -17,7 +17,7 @@ const nodeDetailItemInjectable = getInjectable({ return { Component: NodeDetails, - enabled: computed(() => isNode(kubeObject.get())), + enabled: computed(() => isNode(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-claim-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-claim-detail-item.injectable.ts index 52283738dd..18c489a6d9 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-claim-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-claim-detail-item.injectable.ts @@ -17,7 +17,7 @@ const persistentVolumeClaimDetailItemInjectable = getInjectable({ return { Component: PersistentVolumeClaimDetails, - enabled: computed(() => isPersistentVolumeClaim(kubeObject.get())), + enabled: computed(() => isPersistentVolumeClaim(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-detail-item.injectable.ts index dd85502c05..536380a9a0 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/persistent-volume-detail-item.injectable.ts @@ -17,7 +17,7 @@ const persistentVolumeDetailItemInjectable = getInjectable({ return { Component: PersistentVolumeDetails, - enabled: computed(() => isPersistentVolume(kubeObject.get())), + enabled: computed(() => isPersistentVolume(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-detail-item.injectable.ts index 7f98c6a9b7..c75f35217d 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-detail-item.injectable.ts @@ -17,7 +17,7 @@ const podDetailItemInjectable = getInjectable({ return { Component: PodDetails, - enabled: computed(() => isPod(kubeObject.get())), + enabled: computed(() => isPod(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-disruption-budget-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-disruption-budget-detail-item.injectable.ts index f320ba5be6..c35e2f6d15 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-disruption-budget-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-disruption-budget-detail-item.injectable.ts @@ -17,7 +17,7 @@ const podDisruptionBudgetDetailItemInjectable = getInjectable({ return { Component: PodDisruptionBudgetDetails, - enabled: computed(() => isPodDisruptionBudget(kubeObject.get())), + enabled: computed(() => isPodDisruptionBudget(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-security-policy-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-security-policy-detail-item.injectable.ts index 177745e5bb..54cd7691c9 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-security-policy-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/pod-security-policy-detail-item.injectable.ts @@ -17,7 +17,7 @@ const podSecurityPolicyDetailItemInjectable = getInjectable({ return { Component: PodSecurityPolicyDetails, - enabled: computed(() => isPodSecurityPolicy(kubeObject.get())), + enabled: computed(() => isPodSecurityPolicy(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts index 63fa5c18b8..803c0655b6 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts @@ -17,7 +17,7 @@ const priorityClassDetailItemInjectable = getInjectable({ return { Component: PriorityClassesDetails, - enabled: computed(() => isPriorityClass(kubeObject.get())), + enabled: computed(() => isPriorityClass(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/replica-set-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/replica-set-detail-item.injectable.ts index 9091507ed5..e10d57fb11 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/replica-set-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/replica-set-detail-item.injectable.ts @@ -17,7 +17,7 @@ const replicaSetDetailItemInjectable = getInjectable({ return { Component: ReplicaSetDetails, - enabled: computed(() => isReplicaSet(kubeObject.get())), + enabled: computed(() => isReplicaSet(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/resource-quota-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/resource-quota-detail-item.injectable.ts index fd495285b5..eae2f0c48f 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/resource-quota-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/resource-quota-detail-item.injectable.ts @@ -17,7 +17,7 @@ const resourceQuotaDetailItemInjectable = getInjectable({ return { Component: ResourceQuotaDetails, - enabled: computed(() => isResourceQuota(kubeObject.get())), + enabled: computed(() => isResourceQuota(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-binding-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-binding-detail-item.injectable.ts index a64e727926..ae83190b61 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-binding-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-binding-detail-item.injectable.ts @@ -17,7 +17,7 @@ const roleBindingDetailItemInjectable = getInjectable({ return { Component: RoleBindingDetails, - enabled: computed(() => isRoleBinding(kubeObject.get())), + enabled: computed(() => isRoleBinding(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-detail-item.injectable.ts index c6acc8aa9f..77781640c5 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/role-detail-item.injectable.ts @@ -17,7 +17,7 @@ const roleDetailItemInjectable = getInjectable({ return { Component: RoleDetails, - enabled: computed(() => isRole(kubeObject.get())), + enabled: computed(() => isRole(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/runtime-class-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/runtime-class-detail-item.injectable.ts index 446d515dbc..a06949094b 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/runtime-class-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/runtime-class-detail-item.injectable.ts @@ -17,7 +17,7 @@ const runtimeClassDetailItemInjectable = getInjectable({ return { Component: RuntimeClassesDetails, - enabled: computed(() => isRuntimeClass(kubeObject.get())), + enabled: computed(() => isRuntimeClass(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/secrets-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/secrets-detail-item.injectable.ts index 7cd57f98b1..5645b40f0b 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/secrets-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/secrets-detail-item.injectable.ts @@ -17,7 +17,7 @@ const secretsDetailItemInjectable = getInjectable({ return { Component: SecretDetails, - enabled: computed(() => isSecret(kubeObject.get())), + enabled: computed(() => isSecret(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-account-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-account-detail-item.injectable.ts index 3d7ecdffef..77aa073c8f 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-account-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-account-detail-item.injectable.ts @@ -17,7 +17,7 @@ const serviceAccountDetailItemInjectable = getInjectable({ return { Component: ServiceAccountsDetails, - enabled: computed(() => isServiceAccount(kubeObject.get())), + enabled: computed(() => isServiceAccount(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-detail-item.injectable.ts index dad93b0064..108da710a9 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/service-detail-item.injectable.ts @@ -17,7 +17,7 @@ const serviceDetailItemInjectable = getInjectable({ return { Component: ServiceDetails, - enabled: computed(() => isService(kubeObject.get())), + enabled: computed(() => isService(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/stateful-set-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/stateful-set-detail-item.injectable.ts index f206a48aad..35970e5697 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/stateful-set-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/stateful-set-detail-item.injectable.ts @@ -17,7 +17,7 @@ const statefulSetDetailItemInjectable = getInjectable({ return { Component: StatefulSetDetails, - enabled: computed(() => isStatefulSet(kubeObject.get())), + enabled: computed(() => isStatefulSet(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/storage-class-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/storage-class-detail-item.injectable.ts index 2493b0eb8d..bc0c71ec5b 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/storage-class-detail-item.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/storage-class-detail-item.injectable.ts @@ -17,7 +17,7 @@ const storageClassDetailItemInjectable = getInjectable({ return { Component: StorageClassDetails, - enabled: computed(() => isStorageClass(kubeObject.get())), + enabled: computed(() => isStorageClass(kubeObject.value.get()?.object)), orderNumber: 10, }; }, diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts index bf7e296921..45b37d2ca1 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts @@ -55,7 +55,7 @@ const kubeObjectDetailItemRegistratorInjectable = getInjectable({ return false; } - if (!isRelevantKubeObject(kubeObject.get())) { + if (!isRelevantKubeObject(kubeObject.value.get()?.object)) { return false; } diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-items.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-items.injectable.ts index 1fa11b6826..4d312eb30b 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-items.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-items.injectable.ts @@ -6,9 +6,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; -import { filter, map, sortBy } from "lodash/fp"; -import { pipeline } from "@ogre-tools/fp"; import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-item-injection-token"; +import { byValue } from "../../../../common/utils/sort-function"; const kubeObjectDetailItemsInjectable = getInjectable({ id: "kube-object-detail-items", @@ -17,14 +16,12 @@ const kubeObjectDetailItemsInjectable = getInjectable({ const computedInjectMany = di.inject(computedInjectManyInjectable); const items = computedInjectMany(kubeObjectDetailItemInjectionToken); - return computed(() => { - return pipeline( - items.get(), - filter((item) => item.enabled.get()), - sortBy((item) => item.orderNumber), - map((item) => item.Component), - ); - }); + return computed(() => ( + items.get() + .filter(item => item.enabled.get()) + .sort(byValue(item => item.orderNumber)) + .map(item => item.Component) + )); }, }); diff --git a/src/renderer/components/kube-object-details/kube-object-details.tsx b/src/renderer/components/kube-object-details/kube-object-details.tsx index e049cc4640..26f9ab7568 100644 --- a/src/renderer/components/kube-object-details/kube-object-details.tsx +++ b/src/renderer/components/kube-object-details/kube-object-details.tsx @@ -6,25 +6,18 @@ import "./kube-object-details.scss"; import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; +import { observer } from "mobx-react"; import type { IComputedValue } from "mobx"; -import { observable, reaction, makeObservable } from "mobx"; import { Drawer } from "../drawer"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { Spinner } from "../spinner"; -import type { ApiManager } from "../../../common/k8s-api/api-manager"; import { KubeObjectMenu } from "../kube-object-menu"; -import { CustomResourceDetails } from "../+custom-resources"; -import { KubeObjectMeta } from "../kube-object-meta"; -import type { PageParam } from "../../navigation"; import type { HideDetails } from "../kube-detail-params/hide-details.injectable"; -import type { CustomResourceDefinitionStore } from "../+custom-resources/definition.store"; +import type { IAsyncComputed } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react"; -import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; -import customResourceDefinitionStoreInjectable from "../+custom-resources/definition.store.injectable"; import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable"; -import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable"; import kubeObjectDetailItemsInjectable from "./kube-object-detail-items/kube-object-detail-items.injectable"; +import type { CurrentKubeObject } from "./current-kube-object-in-details.injectable"; import currentKubeObjectInDetailsInjectable from "./current-kube-object-in-details.injectable"; export interface KubeObjectDetailsProps { @@ -34,128 +27,55 @@ export interface KubeObjectDetailsProps { interface Dependencies { detailComponents: IComputedValue; - kubeObject: IComputedValue; - kubeDetailsUrlParam: PageParam; - apiManager: ApiManager; + kubeObject: IAsyncComputed; hideDetails: HideDetails; - customResourceDefinitionStore: CustomResourceDefinitionStore; } -@observer -class NonInjectedKubeObjectDetails extends React.Component { - @observable isLoading = false; - @observable.ref loadingError: React.ReactNode; +const NonInjectedKubeObjectDetails = observer((props: Dependencies) => { + const { + detailComponents, + hideDetails, + kubeObject, + } = props; - constructor(props: Dependencies) { - super(props); - makeObservable(this); - } + const currentKubeObject = kubeObject.value.get(); + const isLoading = kubeObject.pending.get(); - get path() { - return this.props.kubeDetailsUrlParam.get(); - } - - get object() { - return this.props.kubeObject.get(); - } - - componentDidMount(): void { - disposeOnUnmount(this, [ - reaction(() => [ - this.path, - this.object, // resource might be updated via watch-event or from already opened details - this.props.customResourceDefinitionStore.items.length, // crd stores initialized after loading - ], async () => { - this.loadingError = ""; - const { path, object } = this; - - if (!object) { - const store = this.props.apiManager.getStore(path); - - if (store) { - this.isLoading = true; - - try { - await store.loadFromPath(path); - } catch (err) { - this.loadingError = ( - <> - Resource loading has failed: - {String(err)} - - ); - } finally { - this.isLoading = false; - } + return ( + } + onClose={hideDetails} + > + {isLoading && } + {currentKubeObject?.error && ( +
+ Resource loading has failed: + {currentKubeObject.error} +
+ )} + {currentKubeObject?.object && ( + <> + { + detailComponents.get() + .map((Component, index) => ) } - } - }), - ]); - } - - renderTitle(object: KubeObject | null | undefined) { - if (!object) { - return ""; - } - - return `${object.kind}: ${object.getName()}`; - } - - renderContents(object: KubeObject) { - const details = this.props.detailComponents.get(); - - if (details.length === 0) { - const crd = this.props.customResourceDefinitionStore.getByObject(object); - - /** - * This is a fallback so that if a custom resource object doesn't have - * any defined details we should try and display at least some details - */ - if (crd) { - return ( - - ); - } else { - // if we still don't have any details to show, just show the standard object metadata - return ; - } - } - - return details.map((DetailComponent, index) => ( - - )); - } - - render() { - const { object, isLoading, loadingError } = this; - - return ( - } - onClose={this.props.hideDetails} - > - {isLoading && } - {loadingError &&
{loadingError}
} - {object && this.renderContents(object)} -
- ); - } -} + + )} +
+ ); +}); export const KubeObjectDetails = withInjectables(NonInjectedKubeObjectDetails, { getProps: (di, props) => ({ ...props, - apiManager: di.inject(apiManagerInjectable), - customResourceDefinitionStore: di.inject(customResourceDefinitionStoreInjectable), hideDetails: di.inject(hideDetailsInjectable), - kubeDetailsUrlParam: di.inject(kubeDetailsUrlParamInjectable), detailComponents: di.inject(kubeObjectDetailItemsInjectable), kubeObject: di.inject(currentKubeObjectInDetailsInjectable), }), diff --git a/src/renderer/components/resource-metrics/index.ts b/src/renderer/components/resource-metrics/index.ts index f48c4cdc90..c6fe125bcc 100644 --- a/src/renderer/components/resource-metrics/index.ts +++ b/src/renderer/components/resource-metrics/index.ts @@ -4,4 +4,3 @@ */ export * from "./resource-metrics"; -export * from "./resource-metrics-text"; diff --git a/src/renderer/components/resource-metrics/resource-metrics-text.tsx b/src/renderer/components/resource-metrics/resource-metrics-text.tsx deleted file mode 100644 index 2e10596dd3..0000000000 --- a/src/renderer/components/resource-metrics/resource-metrics-text.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import type { MetricData } from "../../../common/k8s-api/endpoints/metrics.api"; -import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api"; -import { bytesToUnits } from "../../utils"; -import { Badge } from "../badge"; -import { DrawerItem } from "../drawer"; - -export interface ResourceMetricsTextMetrics { - cpuUsage?: MetricData; - cpuRequests?: MetricData; - cpuLimits?: MetricData; - memoryUsage?: MetricData; - memoryRequests?: MetricData; - memoryLimits?: MetricData; -} - -export interface ResourceMetricsTextProps { - metrics: ResourceMetricsTextMetrics | null | undefined; -} - -export function ResourceMetricsText({ metrics }: ResourceMetricsTextProps) { - if (!metrics) { - return null; - } - - const { - cpuUsage = 0, - memoryUsage = 0, - } = getMetricLastPoints(metrics); - - return ( - <> - - {cpuUsage > 0 && } - - - {memoryUsage > 0 && } - - - ); -} diff --git a/src/renderer/components/resource-metrics/resource-metrics.tsx b/src/renderer/components/resource-metrics/resource-metrics.tsx index b845d8a4c1..c10075ac6d 100644 --- a/src/renderer/components/resource-metrics/resource-metrics.tsx +++ b/src/renderer/components/resource-metrics/resource-metrics.tsx @@ -5,27 +5,31 @@ import "./resource-metrics.scss"; -import React, { createContext, useEffect, useState } from "react"; +import React, { createContext, useState } from "react"; import { Radio, RadioGroup } from "../radio"; -import { useInterval } from "../../hooks"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; -import { cssNames, noop } from "../../utils"; +import { cssNames } from "../../utils"; import { Spinner } from "../spinner"; import type { MetricsTab } from "../chart/options"; import type { MetricData } from "../../../common/k8s-api/endpoints/metrics.api"; +import type { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { isComputed } from "mobx"; +import { observer } from "mobx-react-lite"; export type AtLeastOneMetricTab = [MetricsTab, ...MetricsTab[]]; export interface ResourceMetricsProps { tabs: AtLeastOneMetricTab; object: KubeObject; - loader?: () => void; - interval?: number; className?: string; - metrics: Partial> | null | undefined; + metrics: IAsyncComputed> | null | undefined> | Partial>; children: React.ReactChild | React.ReactChild[]; } +function isAsyncComputedMetrics(metrics: IAsyncComputed> | null | undefined> | Partial>): metrics is IAsyncComputed> | null | undefined> { + return isComputed((metrics as any).value); +} + export interface ResourceMetricsValue { object: KubeObject; tab: MetricsTab; @@ -34,48 +38,49 @@ export interface ResourceMetricsValue { export const ResourceMetricsContext = createContext(null); -export function ResourceMetrics({ object, loader = noop, interval = 60, tabs, children, className, metrics }: ResourceMetricsProps) { +export const ResourceMetrics = observer(({ + object, + tabs, + children, + className, + metrics, +}: ResourceMetricsProps) => { const [tab, setTab] = useState(tabs[0]); - // This is done just incase `loader` is actually something like `() => Promise` - useEffect(() => void loader(), [object]); - useInterval(loader, interval * 1000); - - const renderContents = () => { - return ( - <> -
- - {tabs.map((tab, index) => ( - - ))} - -
- -
- {children} -
-
-
- -
- - ); - }; - return (
- {renderContents()} +
+ + {tabs.map((tab, index) => ( + + ))} + +
+ +
+ {children} +
+
+
+ +
); -} +});