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

Refactor kube detail components and add KubeEvents automatically to all custom resources (#6468)

* Support Events on CustomResourece details panel

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove unnecessary check

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove legacy KubeObjectMeta use from non-metrics details

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change orderNumber of default KubeMetaDetails

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last use of legacy global getActiveClusterEntity

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Refactor ResourceMetrics to use IAsyncComputed

- Introduce first use for namespace metrics

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Introduce metrics details item for Ingress

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove legacyStore for nodes

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove legacyStore for ingresses

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Replace NodeMetrics in details with injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change namespace metrics details to use more injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change ingress metrics details to use more injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change PersistentVolumeClaim metrics to be injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change DaemonSet metrics to be injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change Deployment metrics to be injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Change Job metrics to be injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Convert Pod metrics to be injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make ReplicaSet metrics details injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make StatefulSet metrics component injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove dead code

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Introduce fix for metrics components visible on all details

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Use the new and correct enabled check

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove legacy global for daemonSetStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Switch components to be static to help with React performance

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove legacy store of CronJobs

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make DeploymentReplicaSets injectable to fix build error

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix up remove dead code

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Allow use of ResourceMetrics without IAsyncComputed

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix metrics not updating correctly

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshots because moving KubeObjectMeta out of CustomResourceDetails

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update more snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix test failures due to newer dep versions

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix type errors from new asyncComputed

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-12-15 07:14:00 -08:00 committed by GitHub
parent d05189d008
commit 83d2b12cca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 1664 additions and 1440 deletions

View File

@ -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 };

View File

@ -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<T>(getOrderValue: (src: T) => number): (left: T, right: T) => number {
return (left, right) => {
const leftValue = getOrderValue(left);
const rightValue = getOrderValue(right);
return leftValue - rightValue;
};
}

View File

@ -302,11 +302,63 @@ exports[`disable kube object detail items when cluster is not relevant given ext
<div
class="drawer-content flex column box grow"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Created
</span>
<span
class="value"
>
&lt;unknown&gt;
ago
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Name
</span>
<span
class="value"
>
some-name
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Namespace
</span>
<span
class="value"
>
some-namespace
</span>
</div>
<div
data-testid="some-kube-object-detail-item"
>
Some detail
</div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div>
</div>
<div
@ -792,6 +844,15 @@ exports[`disable kube object detail items when cluster is not relevant given ext
some-namespace
</span>
</div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div>
</div>
<div
@ -1277,6 +1338,15 @@ exports[`disable kube object detail items when cluster is not relevant given not
some-namespace
</span>
</div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div>
</div>
<div

View File

@ -19,6 +19,7 @@ import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../rende
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("disable kube object detail items when cluster is not relevant", () => {
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,
() =>
({
windowDi.override(apiManagerInjectable, () => ({
getStore: () => ({
getByPath: () =>
getKubeObjectStub("some-kind", "some-api-version"),
}),
} as unknown as ApiManager),
);
loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"),
}) as Partial<KubeObjectStore> as KubeObjectStore,
}) as Partial<ApiManager> as ApiManager);
windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable);

View File

@ -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,
() =>
({
windowDi.override(apiManagerInjectable, () => ({
getStore: () => ({
getByPath: () =>
getKubeObjectStub("some-kind", "some-api-version"),
}),
} as unknown as ApiManager),
);
loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"),
}) as Partial<KubeObjectStore> as KubeObjectStore,
}) as Partial<ApiManager> as ApiManager);
runInAction(() => {
windowDi.register(testRouteInjectable, testRouteComponentInjectable);

View File

@ -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);

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, 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;

View File

@ -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;

View File

@ -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<HpaDetailsProps & Dependenci
return (
<div className="HpaDetails">
<KubeObjectMeta object={hpa}/>
<DrawerItem name="Reference">
{scaleTargetRef && (
<Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>

View File

@ -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<Lease> {
@ -23,8 +22,6 @@ export class LeaseDetails extends React.Component<LeaseDetailsProps> {
return (
<div className="LeaseDetails">
<KubeObjectMeta object={lease} />
<DrawerItem name="Holder Identity">
{lease.getHolderIdentity()}
</DrawerItem>

View File

@ -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<LimitRangeDetailsProps> {
return (
<div className="LimitRangeDetails">
<KubeObjectMeta object={limitRange}/>
{containerLimits.length > 0 && (
<DrawerItem name="Container Limits" labelsOnly>
{

View File

@ -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<ConfigMapDetailsProps
return (
<div className="ConfigMapDetails">
<KubeObjectMeta object={configMap}/>
{
data.length > 0 && (
<>

View File

@ -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<PodDisruptionBudget> {
@ -37,8 +36,6 @@ export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBud
return (
<div className="PdbDetails">
<KubeObjectMeta object={pdb}/>
{selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly>
{

View File

@ -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<PriorityClass> {
}
@ -23,8 +22,6 @@ export class PriorityClassesDetails extends React.Component<PriorityClassesDetai
return (
<div className="PriorityClassesDetails">
<KubeObjectMeta object={pc} />
<DrawerItem name="Description">
{pc.getDescription()}
</DrawerItem>

View File

@ -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<ResourceQuota> {
@ -93,8 +92,6 @@ export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsPr
return (
<div className="ResourceQuotaDetails">
<KubeObjectMeta object={quota}/>
<DrawerItem name="Quotas" className="quota-list">
{renderQuotas(quota)}
</DrawerItem>

View File

@ -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<RuntimeClassesDetails
return (
<div className="RuntimeClassesDetails">
<KubeObjectMeta object={rc} />
<DrawerItem name="Handler">
{rc.getHandler()}
</DrawerItem>

View File

@ -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<SecretDetailsProps & Depe
return (
<div className="SecretDetails">
<KubeObjectMeta object={secret}/>
<DrawerItem name="Type">
{secret.type}
</DrawerItem>

View File

@ -5,35 +5,6 @@ exports[`<CustomResourceDetails /> with a CRD with a boolean field should displa
<div
class="CrdResourceDetails MyCrd"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Created
</span>
<span
class="value"
>
&lt;unknown&gt;
ago
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Name
</span>
<span
class="value"
>
first-crd
</span>
</div>
<div
class="DrawerItem"
>
@ -57,35 +28,6 @@ exports[`<CustomResourceDetails /> with a CRD with a boolean field should displa
<div
class="CrdResourceDetails MyCrd"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Created
</span>
<span
class="value"
>
&lt;unknown&gt;
ago
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Name
</span>
<span
class="value"
>
first-crd
</span>
</div>
<div
class="DrawerItem"
>
@ -109,35 +51,6 @@ exports[`<CustomResourceDetails /> with a CRD with a number field should display
<div
class="CrdResourceDetails MyCrd"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Created
</span>
<span
class="value"
>
&lt;unknown&gt;
ago
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Name
</span>
<span
class="value"
>
first-crd
</span>
</div>
<div
class="DrawerItem"
>
@ -161,35 +74,6 @@ exports[`<CustomResourceDetails /> with a CRD with a number field should display
<div
class="CrdResourceDetails MyCrd"
>
<div
class="DrawerItem"
>
<span
class="name"
>
Created
</span>
<span
class="value"
>
&lt;unknown&gt;
ago
</span>
</div>
<div
class="DrawerItem"
>
<span
class="name"
>
Name
</span>
<span
class="value"
>
first-crd
</span>
</div>
<div
class="DrawerItem"
>

View File

@ -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<CRDDetailsProps> {
return (
<div className="CRDDetails">
<KubeObjectMeta object={crd}/>
<DrawerItem name="Group">
{crd.getGroup()}
</DrawerItem>

View File

@ -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<KubeObject> {
crd: CustomResourceDefinition;
crd?: CustomResourceDefinition;
}
function convertSpecValue(value: unknown): React.ReactNode {
@ -129,7 +128,6 @@ export class CustomResourceDetails extends React.Component<CustomResourceDetails
return (
<div className={cssNames("CrdResourceDetails", crd.getResourceKind())}>
<KubeObjectMeta object={object} />
{this.renderAdditionalColumns(object, extraColumns)}
{this.renderStatus(object, extraColumns)}
</div>

View File

@ -58,7 +58,6 @@ export class CustomResourceDefinitionStore extends KubeObjectStore<CustomResourc
}
getByObject(obj: KubeObject) {
if (!obj) return null;
const { kind, apiVersion } = obj;
return this.items.find(crd => (

View File

@ -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 (
<div className={cssNames("EventDetails", className)}>
<KubeObjectMeta object={event}/>
<DrawerItem name="Message">
{message}
</DrawerItem>

View File

@ -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<KubeEvent, KubeEventApi> {
constructor(
protected readonly dependencies: EventStoreDependencies,
protected readonly dependencies: Dependencies,
api: KubeEventApi,
opts: KubeObjectStoreOptions = {},
) {

View File

@ -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<PodMetricInNamespaceData>;
}
const NonInjectedNamespaceMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Namespace> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const NamespaceMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Namespace>>(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;

View File

@ -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;

View File

@ -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<Namespace> {
}
interface Dependencies {
subscribeStores: SubscribeStores;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl;
resourceQuotaStore: ResourceQuotaStore;
limitRangeStore: LimitRangeStore;
requestPodMetricsInNamespace: RequestPodMetricsInNamespace;
logger: Logger;
}
@observer
class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps & Dependencies> {
@observable metrics: PodMetricInNamespaceData | null = null;
constructor(props: NamespaceDetailsProps & Dependencies) {
super(props);
makeObservable(this);
@ -57,10 +47,6 @@ class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps
componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([
this.props.resourceQuotaStore,
this.props.limitRangeStore,
@ -80,40 +66,23 @@ class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps
return this.props.limitRangeStore.getAllByNs(namespace);
}
loadMetrics = async () => {
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 (
<div className="NamespaceDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={namespace}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={namespace}/>
<DrawerItem name="Status">
<span className={cssNames("status", status.toLowerCase())}>{status}</span>
</DrawerItem>
@ -143,11 +112,10 @@ export const NamespaceDetails = withInjectables<Dependencies, NamespaceDetailsPr
getProps: (di, props) => ({
...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),
}),
});

View File

@ -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<EndpointsDetailsProps> {
return (
<div className="EndpointDetails">
<KubeObjectMeta object={endpoint}/>
<DrawerTitle>Subsets</DrawerTitle>
{
endpoint.getEndpointSubsets().map((subset) => (

View File

@ -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<Ingress> {
}
interface Dependencies {
requestIngressMetrics: RequestIngressMetrics;
logger: Logger;
}
@observer
class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & Dependencies> {
@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<IngressDetailsProps & De
}
render() {
const { object: ingress } = this.props;
const { object: ingress, logger } = this.props;
if (!ingress) {
return null;
@ -136,25 +108,10 @@ class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & De
return null;
}
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Ingress);
const port = ingress.getServiceNamePort();
return (
<div className="IngressDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={[
"Network",
"Duration",
]}
object={ingress}
metrics={this.metrics}
>
<IngressCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={ingress}/>
<DrawerItem name="Ports">
{ingress.getPorts()}
</DrawerItem>
@ -181,6 +138,6 @@ class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & De
export const IngressDetails = withInjectables<Dependencies, IngressDetailsProps>(NonInjectedIngressDetails, {
getProps: (di, props) => ({
...props,
requestIngressMetrics: di.inject(requestIngressMetricsInjectable),
logger: di.inject(loggerInjectable),
}),
});

View File

@ -32,16 +32,19 @@ interface Dependencies {
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedIngresses extends React.Component<Dependencies> {
render() {
const NonInjectedIngresses = observer((props: Dependencies) => {
const {
ingressStore,
filterByNamespace,
} = props;
return (
<SiblingsInTabLayout>
<KubeObjectListLayout
isConfigurable
tableId="network_ingresses"
className="Ingresses"
store={this.props.ingressStore}
store={ ingressStore }
sortingCallbacks={ {
[columnId.name]: ingress => ingress.getName(),
[columnId.namespace]: ingress => ingress.getNs(),
@ -66,7 +69,7 @@ class NonInjectedIngresses extends React.Component<Dependencies> {
<a
key="namespace"
className="filterNamespace"
onClick={prevDefault(() => this.props.filterByNamespace(ingress.getNs()))}
onClick={prevDefault(() => filterByNamespace(ingress.getNs()))}
>
{ingress.getNs()}
</a>,
@ -100,17 +103,15 @@ class NonInjectedIngresses extends React.Component<Dependencies> {
return lines * lineHeight + paddings;
},
}}
/>
} } />
</SiblingsInTabLayout>
);
}
}
});
export const Ingresses = withInjectables<Dependencies>(NonInjectedIngresses, {
getProps: (di, props) => ({
...props,
filterByNamespace: di.inject(filterByNamespaceInjectable),
ingressStore: di.inject(ingressStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable),
}),
});

View File

@ -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<IngressMetricData>;
}
const NonInjectedIngressMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Ingress> & Dependencies) => (
<ResourceMetrics
tabs={[
"Network",
"Duration",
]}
object={object}
metrics={metrics}
>
<IngressCharts />
</ResourceMetrics>
);
const IngressMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Ingress>>(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;

View File

@ -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;

View File

@ -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<NetworkPolicyDetailsPr
return (
<div className={styles.NetworkPolicyDetails}>
<KubeObjectMeta object={policy}/>
<DrawerItem name="Pod Selector" labelsOnly={selector.length > 0}>
{
selector.length > 0

View File

@ -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<ServiceDetailsProps & De
return (
<div className="ServicesDetails">
<KubeObjectMeta object={service}/>
<DrawerItem name="Selector" labelsOnly>
{service.getSelector().map(selector => <Badge key={selector} label={selector}/>)}
</DrawerItem>

View File

@ -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<Node> {
}
@ -38,39 +30,21 @@ export interface NodeDetailsProps extends KubeObjectDetailsProps<Node> {
interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
logger: Logger;
}
@observer
class NonInjectedNodeDetails extends React.Component<NodeDetailsProps & Dependencies> {
@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<NodeDetailsProps & Dependen
const conditions = node.getActiveConditions();
const taints = node.getTaints();
const childPods = podStore.getPodsByNode(node.getName());
const { metrics } = this;
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Node);
return (
<div className="NodeDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={[
"CPU",
"Memory",
"Disk",
"Pods",
]}
object={node}
metrics={metrics}
>
<NodeCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={node} hideFields={["labels", "annotations", "uid", "resourceVersion", "selfLink"]}/>
{addresses && (
<DrawerItem name="Addresses">
{
@ -197,8 +153,7 @@ export const NodeDetails = withInjectables<Dependencies, NodeDetailsProps>(NonIn
...props,
subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
logger: di.inject(loggerInjectable),
}),
});

View File

@ -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);

View File

@ -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<ClusterMetricData>;
}
const NonInjectedNodeMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Node> & Dependencies) => (
<ResourceMetrics
tabs={[
"CPU",
"Memory",
"Disk",
"Pods",
]}
object={object}
metrics={metrics}
>
<NodeCharts />
</ResourceMetrics>
);
const NodeMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Node>>(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;

View File

@ -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;

View File

@ -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<PodSecurityPolicy> {
@ -74,8 +73,6 @@ export class PodSecurityPolicyDetails extends React.Component<PodSecurityPolicyD
return (
<div className="PodSecurityPolicyDetails">
<KubeObjectMeta object={psp}/>
{allowedCapabilities && (
<DrawerItem name="Allowed Capabilities">
{allowedCapabilities.join(", ")}

View File

@ -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<StorageClassDetails
return (
<div className="StorageClassDetails">
<KubeObjectMeta object={storageClass}/>
{provisioner && (
<DrawerItem name="Provisioner" labelsOnly>
<Badge label={provisioner}/>

View File

@ -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<PersistentVolumeClaimMetricData>;
}
const NonInjectedPersistentVolumeClaimMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<PersistentVolumeClaim> & Dependencies) => (
<ResourceMetrics
tabs={[
"Disk",
]}
object={object}
metrics={metrics}
>
<VolumeClaimDiskChart />
</ResourceMetrics>
);
const PersistentVolumeClaimMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<PersistentVolumeClaim>>(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;

View File

@ -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;

View File

@ -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<PersistentVolumeClaim> {
}
interface Dependencies {
requestPersistentVolumeClaimMetrics: RequestPersistentVolumeClaimMetrics;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl;
podStore: PodStore;
storageClassApi: StorageClassApi;
logger: Logger;
}
@observer
class NonInjectedPersistentVolumeClaimDetails extends React.Component<PersistentVolumeClaimDetailsProps & Dependencies> {
@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<Persistent
const { storageClassName, accessModes } = volumeClaim.spec;
const pods = volumeClaim.getPods(podStore.items);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.VolumeClaim);
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.formatUrlForNotListing({
name: storageClassName,
}));
return (
<div className="PersistentVolumeClaimDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={[
"Disk",
]}
object={volumeClaim}
metrics={this.metrics}
>
<VolumeClaimDiskChart />
</ResourceMetrics>
)}
<KubeObjectMeta object={volumeClaim} />
<DrawerItem name="Access Modes">
{accessModes?.join(", ")}
</DrawerItem>
@ -147,9 +106,9 @@ class NonInjectedPersistentVolumeClaimDetails extends React.Component<Persistent
export const PersistentVolumeClaimDetails = withInjectables<Dependencies, PersistentVolumeClaimDetailsProps>(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),
}),
});

View File

@ -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<PersistentVolumeDet
return (
<div className="PersistentVolumeDetails">
<KubeObjectMeta object={volume} />
<DrawerItem name="Capacity">
{capacity?.storage}
</DrawerItem>

View File

@ -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<ClusterRoleBi
return (
<div className="ClusterRoleBindingDetails">
<KubeObjectMeta object={clusterRoleBinding} />
<DrawerTitle>Reference</DrawerTitle>
<Table>
<TableHead>

View File

@ -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<ClusterRole> {
@ -26,8 +25,6 @@ export class ClusterRoleDetails extends React.Component<ClusterRoleDetailsProps>
return (
<div className="ClusterRoleDetails">
<KubeObjectMeta object={clusterRole}/>
<DrawerTitle>Rules</DrawerTitle>
{rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => {
return (

View File

@ -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<RoleBindingDetailsPr
return (
<div className="RoleBindingDetails">
<KubeObjectMeta object={roleBinding} />
<DrawerTitle>Reference</DrawerTitle>
<Table>
<TableHead>

View File

@ -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<Role> {
}
@ -26,7 +25,6 @@ export class RoleDetails extends React.Component<RoleDetailsProps> {
return (
<div className="RoleDetails">
<KubeObjectMeta object={role}/>
<DrawerTitle>Rules</DrawerTitle>
{rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => {
return (

View File

@ -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<ServiceAccountsD
return (
<div className="ServiceAccountsDetails">
<KubeObjectMeta object={serviceAccount}/>
{tokens.length > 0 && (
<DrawerItem name="Tokens" className="links">
{this.renderSecretLinks(tokens)}

View File

@ -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<CronJobDetailsProps & De
return (
<div className="CronJobDetails">
<KubeObjectMeta object={cronJob}/>
<DrawerItem name="Schedule">
{
cronJob.isNeverRun()

View File

@ -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,20 +33,16 @@ enum columnId {
interface Dependencies {
cronJobStore: CronJobStore;
jobStore: JobStore;
eventStore: EventStore;
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedCronJobs extends React.Component<Dependencies>{
render() {
const NonInjectedCronJobs = observer((props: Dependencies) => {
const {
cronJobStore,
eventStore,
jobStore,
filterByNamespace,
} = this.props;
} = props;
return (
<SiblingsInTabLayout>
@ -57,7 +51,7 @@ class NonInjectedCronJobs extends React.Component<Dependencies>{
tableId="workload_cronjobs"
className="CronJobs"
store={cronJobStore}
dependentStores={[jobStore, eventStore]}
dependentStores={[eventStore]}
sortingCallbacks={{
[columnId.name]: cronJob => cronJob.getName(),
[columnId.namespace]: cronJob => cronJob.getNs(),
@ -104,15 +98,13 @@ class NonInjectedCronJobs extends React.Component<Dependencies>{
/>
</SiblingsInTabLayout>
);
}
}
});
export const CronJobs = withInjectables<Dependencies>(NonInjectedCronJobs, {
getProps: (di, props) => ({
...props,
cronJobStore: di.inject(cronJobStoreInjectable),
eventStore: di.inject(eventStoreInjectable),
jobStore: di.inject(jobStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable),
}),
});

View File

@ -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<DaemonSet> {
}
@ -40,38 +34,21 @@ interface Dependencies {
subscribeStores: SubscribeStores;
daemonSetStore: DaemonSetStore;
podStore: PodStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestPodMetricsForDaemonSets: RequestPodMetricsForDaemonSets;
logger: Logger;
}
@observer
class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps & Dependencies> {
@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<DaemonSetDetailsProps
const images = daemonSet.getImages();
const nodeSelector = daemonSet.getNodeSelectors();
const childPods = daemonSetStore.getChildPods(daemonSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.DaemonSet);
return (
<div className="DaemonSetDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={daemonSet}
metrics={this.metrics}
>
<PodCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={daemonSet}/>
{selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly>
{
@ -132,7 +97,6 @@ class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps
<DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/>
</DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={daemonSet}/>
</div>
);
@ -147,5 +111,6 @@ export const DaemonSetDetails = withInjectables<Dependencies, DaemonSetDetailsPr
podStore: di.inject(podStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
requestPodMetricsForDaemonSets: di.inject(requestPodMetricsForDaemonSetsInjectable),
logger: di.inject(loggerInjectable),
}),
});

View File

@ -14,7 +14,6 @@ import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import type { DaemonSetStore } from "./store";
import type { PodStore } from "../+workloads-pods/store";
import type { EventStore } from "../+events/store";
import { prevDefault } from "../../utils";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
@ -22,7 +21,6 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import daemonSetStoreInjectable from "./store.injectable";
import eventStoreInjectable from "../+events/store.injectable";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
enum columnId {
name = "name",
@ -34,24 +32,18 @@ enum columnId {
interface Dependencies {
daemonSetStore: DaemonSetStore;
podStore: PodStore;
eventStore: EventStore;
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedDaemonSets extends React.Component<Dependencies> {
getPodsLength(daemonSet: DaemonSet) {
return this.props.daemonSetStore.getChildPods(daemonSet).length;
}
render() {
const NonInjectedDaemonSets = observer((props: Dependencies) => {
const {
daemonSetStore,
eventStore,
filterByNamespace,
podStore,
} = this.props;
} = props;
const getPodsLength = (daemonSet: DaemonSet) => daemonSetStore.getChildPods(daemonSet).length;
return (
<SiblingsInTabLayout>
@ -60,11 +52,11 @@ class NonInjectedDaemonSets extends React.Component<Dependencies> {
tableId="workload_daemonsets"
className="DaemonSets"
store={daemonSetStore}
dependentStores={[podStore, eventStore]} // status icon component uses event store
dependentStores={[eventStore]} // status icon component uses event store
sortingCallbacks={{
[columnId.name]: daemonSet => daemonSet.getName(),
[columnId.namespace]: daemonSet => daemonSet.getNs(),
[columnId.pods]: daemonSet => this.getPodsLength(daemonSet),
[columnId.pods]: daemonSet => getPodsLength(daemonSet),
[columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(),
}}
searchFilters={[
@ -89,7 +81,7 @@ class NonInjectedDaemonSets extends React.Component<Dependencies> {
>
{daemonSet.getNs()}
</a>,
this.getPodsLength(daemonSet),
getPodsLength(daemonSet),
<KubeObjectStatusIcon key="icon" object={daemonSet} />,
daemonSet.getNodeSelectors().map(selector => (
<Badge
@ -103,8 +95,7 @@ class NonInjectedDaemonSets extends React.Component<Dependencies> {
/>
</SiblingsInTabLayout>
);
}
}
});
export const DaemonSets = withInjectables<Dependencies>(NonInjectedDaemonSets, {
getProps: (di, props) => ({
@ -112,6 +103,5 @@ export const DaemonSets = withInjectables<Dependencies>(NonInjectedDaemonSets, {
daemonSetStore: di.inject(daemonSetStoreInjectable),
eventStore: di.inject(eventStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable),
podStore: di.inject(podStoreInjectable),
}),
});

View File

@ -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<DaemonSetPodMetricData>;
}
const NonInjectedDaemonSetMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<DaemonSet> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const DaemonSetMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<DaemonSet>>(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;

View File

@ -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;

View File

@ -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<Deployment> {
}
interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
replicaSetStore: ReplicaSetStore;
deploymentStore: DeploymentStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestPodMetricsForDeployments: RequestPodMetricsForDeployments;
logger: Logger;
}
@observer
class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProps & Dependencies> {
@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<DeploymentDetailsProp
const selectors = deployment.getSelectors();
const childPods = deploymentStore.getChildPods(deployment);
const replicaSets = replicaSetStore.getReplicaSetsByOwner(deployment);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Deployment);
return (
<div className="DeploymentDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={deployment}
metrics={this.metrics}
>
<PodCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={deployment}/>
<DrawerItem name="Replicas">
{`${spec.replicas} desired, ${status?.updatedReplicas ?? 0} updated, `}
{`${status?.replicas ?? 0} total, ${status?.availableReplicas ?? 0} available, `}
@ -159,7 +117,6 @@ class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProp
</DrawerItem>
<PodDetailsTolerations workload={deployment}/>
<PodDetailsAffinities workload={deployment}/>
<ResourceMetricsText metrics={this.metrics}/>
<DeploymentReplicaSets replicaSets={replicaSets}/>
<PodDetailsList pods={childPods} owner={deployment}/>
</div>
@ -171,11 +128,9 @@ export const DeploymentDetails = withInjectables<Dependencies, DeploymentDetails
getProps: (di, props) => ({
...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),
}),
});

View File

@ -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<DeploymentReplica
<TableCell className="pods">{this.getPodsLength(replica)}</TableCell>
<TableCell className="age"><KubeObjectAge key="age" object={replica} /></TableCell>
<TableCell className="actions" onClick={stopPropagation}>
<ReplicaSetMenu object={replica} />
<KubeObjectMenu object={replica} />
</TableCell>
</TableRow>
))
@ -114,9 +118,3 @@ export const DeploymentReplicaSets = withInjectables<Dependencies, DeploymentRep
showDetails: di.inject(showDetailsInjectable),
}),
});
export function ReplicaSetMenu(props: KubeObjectMenuProps<ReplicaSet>) {
return (
<KubeObjectMenu {...props}/>
);
}

View File

@ -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<DeploymentPodMetricData>;
}
const NonInjectedDeploymentMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Deployment> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const DeploymentMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Deployment>>(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;

View File

@ -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;

View File

@ -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<Job> {
}
@ -41,38 +33,21 @@ interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
jobStore: JobStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestPodMetricsForJobs: RequestPodMetricsForJobs;
logger: Logger;
}
@observer
class NonInjectedJobDetails extends React.Component<JobDetailsProps & Dependencies> {
@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<JobDetailsProps & Dependenci
const images = job.getImages();
const childPods = jobStore.getChildPods(job);
const condition = job.getCondition();
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Job);
return (
<div className="JobDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={job}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={job}/>
<DrawerItem name="Selector" labelsOnly>
{
Object.keys(selectors).map(label => <Badge key={label} label={label}/>)
@ -161,8 +124,7 @@ export const JobDetails = withInjectables<Dependencies, JobDetailsProps>(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),
}),
});

View File

@ -35,14 +35,12 @@ interface Dependencies {
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedJobs extends React.Component<Dependencies> {
render() {
const NonInjectedJobs = observer((props: Dependencies) => {
const {
eventStore,
filterByNamespace,
jobStore,
} = this.props;
} = props;
return (
<SiblingsInTabLayout>
@ -94,8 +92,7 @@ class NonInjectedJobs extends React.Component<Dependencies> {
/>
</SiblingsInTabLayout>
);
}
}
});
export const Jobs = withInjectables<Dependencies>(NonInjectedJobs, {
getProps: (di, props) => ({

View File

@ -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<JobPodMetricData>;
}
const NonInjectedJobMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Job> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const JobMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Job>>(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;

View File

@ -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;

View File

@ -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;

View File

@ -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<PodMetricData>;
}
const NonInjectedPodMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<Pod> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const PodMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<Pod>>(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;

View File

@ -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;

View File

@ -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<Pod> {
}
interface Dependencies {
requestPodMetrics: RequestPodMetrics;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl;
nodeApi: NodeApi;
priorityClassApi: PriorityClassApi;
runtimeClassApi: RuntimeClassApi;
serviceAccountApi: ServiceAccountApi;
logger: Logger;
containerMetrics: IAsyncComputed<PodMetricData>;
}
@observer
class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependencies> {
@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<PodDetailsProps & Dependenci
const podIPs = pod.getIPs();
const { nodeName } = spec ?? {};
const nodeSelector = pod.getNodeSelectors();
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Pod);
const initContainers = pod.getInitContainers();
const containers = pod.getContainers();
@ -105,39 +75,26 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
const runtimeClassName = pod.getRuntimeClassName();
const serviceAccountName = pod.getServiceAccountName();
const priorityClassDetailsUrl = getDetailsUrl(this.props.priorityClassApi.getUrl({
const priorityClassDetailsUrl = getDetailsUrl(this.props.priorityClassApi.formatUrlForNotListing({
name: priorityClassName,
}));
const runtimeClassDetailsUrl = getDetailsUrl(this.props.runtimeClassApi.getUrl({
const runtimeClassDetailsUrl = getDetailsUrl(this.props.runtimeClassApi.formatUrlForNotListing({
name: runtimeClassName,
}));
const serviceAccountDetailsUrl = getDetailsUrl(this.props.serviceAccountApi.getUrl({
const serviceAccountDetailsUrl = getDetailsUrl(this.props.serviceAccountApi.formatUrlForNotListing({
name: serviceAccountName,
namespace,
}));
return (
<div className="PodDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={pod}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={pod} />
<DrawerItem name="Status">
<span className={cssNames("status", kebabCase(pod.getStatusMessage()))}>
{pod.getStatusMessage()}
</span>
</DrawerItem>
<DrawerItem name="Node" hidden={!nodeName}>
<Link to={getDetailsUrl(nodeApi.getUrl({ name: nodeName }))}>
<Link to={getDetailsUrl(nodeApi.formatUrlForNotListing({ name: nodeName }))}>
{nodeName}
</Link>
</DrawerItem>
@ -230,7 +187,7 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
key={container.name}
pod={pod}
container={container}
metrics={getItemMetrics(toJS(this.containerMetrics), container.name)}
metrics={getItemMetrics(toJS(containerMetrics.value.get()), container.name)}
/>
))}
@ -243,12 +200,12 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
export const PodDetails = withInjectables<Dependencies, PodDetailsProps>(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),
}),
});

View File

@ -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<ReplicaSetPodMetricData>;
}
const NonInjectedReplicaSetMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<ReplicaSet> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const ReplicaSetMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<ReplicaSet>>(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;

View File

@ -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;

View File

@ -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<ReplicaSet> {
}
@ -39,39 +31,20 @@ interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
replicaSetStore: ReplicaSetStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestPodMetricsForReplicaSets: RequestPodMetricsForReplicaSets;
logger: Logger;
}
@observer
class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProps & Dependencies> {
@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<ReplicaSetDetailsProp
const nodeSelector = replicaSet.getNodeSelectors();
const images = replicaSet.getImages();
const childPods = replicaSetStore.getChildPods(replicaSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.ReplicaSet);
return (
<div className="ReplicaSetDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={replicaSet}
metrics={this.metrics}
>
<PodCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={replicaSet}/>
{selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly>
{
@ -132,7 +93,6 @@ class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProp
<DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/>
</DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={replicaSet}/>
</div>
);
@ -145,7 +105,6 @@ export const ReplicaSetDetails = withInjectables<Dependencies, ReplicaSetDetails
subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable),
replicaSetStore: di.inject(replicaSetStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
requestPodMetricsForReplicaSets: di.inject(requestPodMetricsForReplicaSetsInjectable),
logger: di.inject(loggerInjectable),
}),
});

View File

@ -35,14 +35,12 @@ interface Dependencies {
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedReplicaSets extends React.Component<Dependencies> {
render() {
const NonInjectedReplicaSets = observer((props: Dependencies) => {
const {
eventStore,
filterByNamespace,
replicaSetStore,
} = this.props;
} = props;
return (
<SiblingsInTabLayout>
@ -91,8 +89,7 @@ class NonInjectedReplicaSets extends React.Component<Dependencies> {
/>
</SiblingsInTabLayout>
);
}
}
});
export const ReplicaSets = withInjectables<Dependencies>(NonInjectedReplicaSets, {
getProps: (di, props) => ({

View File

@ -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<ReplicaSet, ReplicaSetApi> {
constructor(protected readonly dependencies: ReplicaSetStoreDependencies, api: ReplicaSetApi, opts?: KubeObjectStoreOptions) {
constructor(protected readonly dependencies: Dependencies, api: ReplicaSetApi, opts?: KubeObjectStoreOptions) {
super(api, opts);
}

View File

@ -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<StatefulSetPodMetricData>;
}
const NonInjectedStatefulSetMetricsDetailsComponent = ({
object,
metrics,
}: KubeObjectDetailsProps<StatefulSet> & Dependencies) => (
<ResourceMetrics
tabs={podMetricTabs}
object={object}
metrics={metrics}
>
<PodCharts />
</ResourceMetrics>
);
const StatefulSetMetricsDetailsComponent = withInjectables<Dependencies, KubeObjectDetailsProps<StatefulSet>>(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;

View File

@ -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;

View File

@ -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<StatefulSet> {
}
@ -40,39 +32,21 @@ interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
statefulSetStore: StatefulSetStore;
getActiveClusterEntity: GetActiveClusterEntity;
requestPodMetricsForStatefulSets: RequestPodMetricsForStatefulSets;
logger: Logger;
}
@observer
class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsProps & Dependencies> {
@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<StatefulSetDetailsPr
const selectors = statefulSet.getSelectors();
const nodeSelector = statefulSet.getNodeSelectors();
const childPods = statefulSetStore.getChildPods(statefulSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.StatefulSet);
return (
<div className="StatefulSetDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={statefulSet}
metrics={this.metrics}
>
<PodCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={statefulSet}/>
{selectors.length && (
<DrawerItem name="Selector" labelsOnly>
{
@ -131,7 +93,6 @@ class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsPr
<DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/>
</DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={statefulSet}/>
</div>
);
@ -144,8 +105,7 @@ export const StatefulSetDetails = withInjectables<Dependencies, StatefulSetDetai
subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable),
statefulSetStore: di.inject(statefulSetStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
requestPodMetricsForStatefulSets: di.inject(requestPodMetricsForStatefulSetsInjectable),
logger: di.inject(loggerInjectable),
}),
});

View File

@ -13,13 +13,11 @@ import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import type { StatefulSetStore } from "./store";
import type { PodStore } from "../+workloads-pods/store";
import type { EventStore } from "../+events/store";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import eventStoreInjectable from "../+events/store.injectable";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import statefulSetStoreInjectable from "./store.injectable";
import { prevDefault } from "../../utils";
@ -33,26 +31,22 @@ enum columnId {
interface Dependencies {
statefulSetStore: StatefulSetStore;
podStore: PodStore;
eventStore: EventStore;
filterByNamespace: FilterByNamespace;
}
@observer
class NonInjectedStatefulSets extends React.Component<Dependencies> {
renderPods(statefulSet: StatefulSet) {
const renderPodCounts = (statefulSet: StatefulSet) => {
const { readyReplicas, currentReplicas } = statefulSet.status ?? {};
return `${readyReplicas || 0}/${currentReplicas || 0}`;
}
};
render() {
const NonInjectedStatefulSets = observer((props: Dependencies) => {
const {
eventStore,
filterByNamespace,
podStore,
statefulSetStore,
} = this.props;
} = props;
return (
<SiblingsInTabLayout>
@ -61,7 +55,7 @@ class NonInjectedStatefulSets extends React.Component<Dependencies> {
tableId="workload_statefulsets"
className="StatefulSets"
store={statefulSetStore}
dependentStores={[podStore, eventStore]} // status icon component uses event store, details component uses podStore
dependentStores={[eventStore]}
sortingCallbacks={{
[columnId.name]: statefulSet => statefulSet.getName(),
[columnId.namespace]: statefulSet => statefulSet.getNs(),
@ -89,7 +83,7 @@ class NonInjectedStatefulSets extends React.Component<Dependencies> {
>
{statefulSet.getNs()}
</a>,
this.renderPods(statefulSet),
renderPodCounts(statefulSet),
statefulSet.getReplicas(),
<KubeObjectStatusIcon key="icon" object={statefulSet} />,
<KubeObjectAge key="age" object={statefulSet} />,
@ -97,15 +91,13 @@ class NonInjectedStatefulSets extends React.Component<Dependencies> {
/>
</SiblingsInTabLayout>
);
}
}
});
export const StatefulSets = withInjectables<Dependencies>(NonInjectedStatefulSets, {
getProps: (di, props) => ({
...props,
eventStore: di.inject(eventStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable),
podStore: di.inject(podStoreInjectable),
statefulSetStore: di.inject(statefulSetStoreInjectable),
}),
});

View File

@ -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(() => {
return asyncComputed({
getValueFromObservedPromise: async (): Promise<CurrentKubeObject> => {
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;
}
try {
const object = await store.loadFromPath(path);
return { object };
} catch (error) {
return { error: String(error) };
}
},
});
},
});

View File

@ -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 }) => <CustomResourceDetails object={object} crd={currentCustomResourceDefinition.get()} />,
enabled: computed(() => Boolean(currentCustomResourceDefinition.get())),
orderNumber: 100,
};
},
injectionToken: kubeObjectDetailItemInjectionToken,
});
export default customResourceDetailItemInjectable;

View File

@ -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;

View File

@ -17,7 +17,7 @@ const clusterRoleBindingDetailItemInjectable = getInjectable({
return {
Component: ClusterRoleBindingDetails,
enabled: computed(() => isClusterRoleBinding(kubeObject.get())),
enabled: computed(() => isClusterRoleBinding(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const clusterRoleDetailItemInjectable = getInjectable({
return {
Component: ClusterRoleDetails,
enabled: computed(() => isClusterRole(kubeObject.get())),
enabled: computed(() => isClusterRole(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const configMapDetailItemInjectable = getInjectable({
return {
Component: ConfigMapDetails,
enabled: computed(() => isConfigMap(kubeObject.get())),
enabled: computed(() => isConfigMap(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const cronJobDetailItemInjectable = getInjectable({
return {
Component: CronJobDetails,
enabled: computed(() => isCronJob(kubeObject.get())),
enabled: computed(() => isCronJob(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const customResourceDefinitionsDetailItemInjectable = getInjectable({
return {
Component: CRDDetails,
enabled: computed(() => isCustomResourceDefinition(kubeObject.get())),
enabled: computed(() => isCustomResourceDefinition(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const daemonSetDetailItemInjectable = getInjectable({
return {
Component: DaemonSetDetails,
enabled: computed(() => isDaemonSet(kubeObject.get())),
enabled: computed(() => isDaemonSet(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const deploymentDetailItemInjectable = getInjectable({
return {
Component: DeploymentDetails,
enabled: computed(() => isDeployment(kubeObject.get())),
enabled: computed(() => isDeployment(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const endpointsDetailItemInjectable = getInjectable({
return {
Component: EndpointsDetails,
enabled: computed(() => isEndpoint(kubeObject.get())),
enabled: computed(() => isEndpoint(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const eventsDetailItemInjectable = getInjectable({
return {
Component: EventDetails,
enabled: computed(() => isEvent(kubeObject.get())),
enabled: computed(() => isEvent(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({
return {
Component: HpaDetails,
enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.get())),
enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const ingressDetailItemInjectable = getInjectable({
return {
Component: IngressDetails,
enabled: computed(() => isIngress(kubeObject.get())),
enabled: computed(() => isIngress(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const jobDetailItemInjectable = getInjectable({
return {
Component: JobDetails,
enabled: computed(() => isJob(kubeObject.get())),
enabled: computed(() => isJob(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -6,78 +6,16 @@ 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 {
instantiate: () => ({
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));
enabled: computed(() => true),
orderNumber: Infinity,
}),
orderNumber: 355,
};
},
injectionToken: kubeObjectDetailItemInjectionToken,
});

View File

@ -17,7 +17,7 @@ const leaseDetailItemInjectable = getInjectable({
return {
Component: LeaseDetails,
enabled: computed(() => isLease(kubeObject.get())),
enabled: computed(() => isLease(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const limitRangeDetailItemInjectable = getInjectable({
return {
Component: LimitRangeDetails,
enabled: computed(() => isLimitRange(kubeObject.get())),
enabled: computed(() => isLimitRange(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const namespacesDetailItemInjectable = getInjectable({
return {
Component: NamespaceDetails,
enabled: computed(() => isNamespace(kubeObject.get())),
enabled: computed(() => isNamespace(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const networkPolicyDetailItemInjectable = getInjectable({
return {
Component: NetworkPolicyDetails,
enabled: computed(() => isNetworkPolicy(kubeObject.get())),
enabled: computed(() => isNetworkPolicy(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const nodeDetailItemInjectable = getInjectable({
return {
Component: NodeDetails,
enabled: computed(() => isNode(kubeObject.get())),
enabled: computed(() => isNode(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const persistentVolumeClaimDetailItemInjectable = getInjectable({
return {
Component: PersistentVolumeClaimDetails,
enabled: computed(() => isPersistentVolumeClaim(kubeObject.get())),
enabled: computed(() => isPersistentVolumeClaim(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const persistentVolumeDetailItemInjectable = getInjectable({
return {
Component: PersistentVolumeDetails,
enabled: computed(() => isPersistentVolume(kubeObject.get())),
enabled: computed(() => isPersistentVolume(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const podDetailItemInjectable = getInjectable({
return {
Component: PodDetails,
enabled: computed(() => isPod(kubeObject.get())),
enabled: computed(() => isPod(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

View File

@ -17,7 +17,7 @@ const podDisruptionBudgetDetailItemInjectable = getInjectable({
return {
Component: PodDisruptionBudgetDetails,
enabled: computed(() => isPodDisruptionBudget(kubeObject.get())),
enabled: computed(() => isPodDisruptionBudget(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},

Some files were not shown because too many files have changed in this diff Show More