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 => { instantiate: (di): RequestPodMetricsForJobs => {
const requestMetrics = di.inject(requestMetricsInjectable); const requestMetrics = di.inject(requestMetricsInjectable);
return (jobs, namespace, selector) => { return (jobs, namespace, selector = "") => {
const podSelector = jobs.map(job => `${job.getName()}-[[:alnum:]]{5}`).join("|"); const podSelector = jobs.map(job => `${job.getName()}-[[:alnum:]]{5}`).join("|");
const opts = { category: "pods", pods: podSelector, namespace, selector }; 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 <div
class="drawer-content flex column box grow" 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 <div
data-testid="some-kube-object-detail-item" data-testid="some-kube-object-detail-item"
> >
Some detail Some detail
</div> </div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div> </div>
</div> </div>
<div <div
@ -792,6 +844,15 @@ exports[`disable kube object detail items when cluster is not relevant given ext
some-namespace some-namespace
</span> </span>
</div> </div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div> </div>
</div> </div>
<div <div
@ -1277,6 +1338,15 @@ exports[`disable kube object detail items when cluster is not relevant given not
some-namespace some-namespace
</span> </span>
</div> </div>
<div>
<div
class="DrawerTitle flex gaps align-center title"
>
<span>
Events
</span>
</div>
</div>
</div> </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 apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details"; import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details";
import type { ApiManager } from "../../../../common/k8s-api/api-manager"; 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", () => { describe("disable kube object detail items when cluster is not relevant", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
@ -32,16 +33,11 @@ describe("disable kube object detail items when cluster is not relevant", () =>
builder.setEnvironmentToClusterFrame(); builder.setEnvironmentToClusterFrame();
builder.beforeWindowStart((windowDi) => { builder.beforeWindowStart((windowDi) => {
windowDi.override( windowDi.override(apiManagerInjectable, () => ({
apiManagerInjectable, getStore: () => ({
() => loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"),
({ }) as Partial<KubeObjectStore> as KubeObjectStore,
getStore: () => ({ }) as Partial<ApiManager> as ApiManager);
getByPath: () =>
getKubeObjectStub("some-kind", "some-api-version"),
}),
} as unknown as ApiManager),
);
windowDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); 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 apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details"; import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details";
import type { ApiManager } from "../../../../common/k8s-api/api-manager"; 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", () => { describe("reactively hide kube object detail item", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
@ -28,16 +29,11 @@ describe("reactively hide kube object detail item", () => {
builder.setEnvironmentToClusterFrame(); builder.setEnvironmentToClusterFrame();
builder.beforeWindowStart((windowDi) => { builder.beforeWindowStart((windowDi) => {
windowDi.override( windowDi.override(apiManagerInjectable, () => ({
apiManagerInjectable, getStore: () => ({
() => loadFromPath: async () => getKubeObjectStub("some-kind", "some-api-version"),
({ }) as Partial<KubeObjectStore> as KubeObjectStore,
getStore: () => ({ }) as Partial<ApiManager> as ApiManager);
getByPath: () =>
getKubeObjectStub("some-kind", "some-api-version"),
}),
} as unknown as ApiManager),
);
runInAction(() => { runInAction(() => {
windowDi.register(testRouteInjectable, testRouteComponentInjectable); 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 { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import type { ApiManager } from "../../../common/k8s-api/api-manager"; import type { ApiManager } from "../../../common/k8s-api/api-manager";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
@ -121,8 +120,6 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
return ( return (
<div className="HpaDetails"> <div className="HpaDetails">
<KubeObjectMeta object={hpa}/>
<DrawerItem name="Reference"> <DrawerItem name="Reference">
{scaleTargetRef && ( {scaleTargetRef && (
<Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}> <Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>

View File

@ -9,7 +9,6 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
import type { Lease } from "../../../common/k8s-api/endpoints"; import type { Lease } from "../../../common/k8s-api/endpoints";
export interface LeaseDetailsProps extends KubeObjectDetailsProps<Lease> { export interface LeaseDetailsProps extends KubeObjectDetailsProps<Lease> {
@ -23,8 +22,6 @@ export class LeaseDetails extends React.Component<LeaseDetailsProps> {
return ( return (
<div className="LeaseDetails"> <div className="LeaseDetails">
<KubeObjectMeta object={lease} />
<DrawerItem name="Holder Identity"> <DrawerItem name="Holder Identity">
{lease.getHolderIdentity()} {lease.getHolderIdentity()}
</DrawerItem> </DrawerItem>

View File

@ -10,7 +10,6 @@ import { observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-range.api"; import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-range.api";
import { LimitPart, LimitRange, Resource } 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 { DrawerItem } from "../drawer/drawer-item";
import { Badge } from "../badge"; import { Badge } from "../badge";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
@ -73,8 +72,6 @@ export class LimitRangeDetails extends React.Component<LimitRangeDetailsProps> {
return ( return (
<div className="LimitRangeDetails"> <div className="LimitRangeDetails">
<KubeObjectMeta object={limitRange}/>
{containerLimits.length > 0 && ( {containerLimits.length > 0 && (
<DrawerItem name="Container Limits" labelsOnly> <DrawerItem name="Container Limits" labelsOnly>
{ {

View File

@ -14,7 +14,6 @@ import { Input } from "../input";
import { Button } from "../button"; import { Button } from "../button";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { ConfigMap } from "../../../common/k8s-api/endpoints"; import { ConfigMap } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import type { Logger } from "../../../common/logger"; import type { Logger } from "../../../common/logger";
import type { ConfigMapStore } from "./store"; import type { ConfigMapStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
@ -95,7 +94,6 @@ class NonInjectedConfigMapDetails extends React.Component<ConfigMapDetailsProps
return ( return (
<div className="ConfigMapDetails"> <div className="ConfigMapDetails">
<KubeObjectMeta object={configMap}/>
{ {
data.length > 0 && ( data.length > 0 && (
<> <>

View File

@ -11,7 +11,6 @@ import { DrawerItem } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints"; import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps<PodDisruptionBudget> { export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps<PodDisruptionBudget> {
@ -37,8 +36,6 @@ export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBud
return ( return (
<div className="PdbDetails"> <div className="PdbDetails">
<KubeObjectMeta object={pdb}/>
{selectors.length > 0 && ( {selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{ {

View File

@ -10,7 +10,6 @@ import { observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { PriorityClass } from "../../../common/k8s-api/endpoints"; import type { PriorityClass } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
export interface PriorityClassesDetailsProps extends KubeObjectDetailsProps<PriorityClass> { export interface PriorityClassesDetailsProps extends KubeObjectDetailsProps<PriorityClass> {
} }
@ -23,8 +22,6 @@ export class PriorityClassesDetails extends React.Component<PriorityClassesDetai
return ( return (
<div className="PriorityClassesDetails"> <div className="PriorityClassesDetails">
<KubeObjectMeta object={pc} />
<DrawerItem name="Description"> <DrawerItem name="Description">
{pc.getDescription()} {pc.getDescription()}
</DrawerItem> </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 { ResourceQuota } from "../../../common/k8s-api/endpoints/resource-quota.api";
import { LineProgress } from "../line-progress"; import { LineProgress } from "../line-progress";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps<ResourceQuota> { export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps<ResourceQuota> {
@ -93,8 +92,6 @@ export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsPr
return ( return (
<div className="ResourceQuotaDetails"> <div className="ResourceQuotaDetails">
<KubeObjectMeta object={quota}/>
<DrawerItem name="Quotas" className="quota-list"> <DrawerItem name="Quotas" className="quota-list">
{renderQuotas(quota)} {renderQuotas(quota)}
</DrawerItem> </DrawerItem>

View File

@ -10,7 +10,6 @@ import { observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { RuntimeClass } from "../../../common/k8s-api/endpoints"; import type { RuntimeClass } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { RuntimeClassDetailsTolerations } from "./runtime-classes-details-tolerations"; import { RuntimeClassDetailsTolerations } from "./runtime-classes-details-tolerations";
@ -26,8 +25,6 @@ export class RuntimeClassesDetails extends React.Component<RuntimeClassesDetails
return ( return (
<div className="RuntimeClassesDetails"> <div className="RuntimeClassesDetails">
<KubeObjectMeta object={rc} />
<DrawerItem name="Handler"> <DrawerItem name="Handler">
{rc.getHandler()} {rc.getHandler()}
</DrawerItem> </DrawerItem>

View File

@ -16,7 +16,6 @@ import { base64, toggle } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Secret } from "../../../common/k8s-api/endpoints"; import { Secret } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import type { Logger } from "../../../common/logger"; import type { Logger } from "../../../common/logger";
import type { SecretStore } from "./store"; import type { SecretStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
@ -161,7 +160,6 @@ class NonInjectedSecretDetails extends React.Component<SecretDetailsProps & Depe
return ( return (
<div className="SecretDetails"> <div className="SecretDetails">
<KubeObjectMeta object={secret}/>
<DrawerItem name="Type"> <DrawerItem name="Type">
{secret.type} {secret.type}
</DrawerItem> </DrawerItem>

View File

@ -5,35 +5,6 @@ exports[`<CustomResourceDetails /> with a CRD with a boolean field should displa
<div <div
class="CrdResourceDetails MyCrd" 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 <div
class="DrawerItem" class="DrawerItem"
> >
@ -57,35 +28,6 @@ exports[`<CustomResourceDetails /> with a CRD with a boolean field should displa
<div <div
class="CrdResourceDetails MyCrd" 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 <div
class="DrawerItem" class="DrawerItem"
> >
@ -109,35 +51,6 @@ exports[`<CustomResourceDetails /> with a CRD with a number field should display
<div <div
class="CrdResourceDetails MyCrd" 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 <div
class="DrawerItem" class="DrawerItem"
> >
@ -161,35 +74,6 @@ exports[`<CustomResourceDetails /> with a CRD with a number field should display
<div <div
class="CrdResourceDetails MyCrd" 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 <div
class="DrawerItem" class="DrawerItem"
> >

View File

@ -14,7 +14,6 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { Input } from "../input"; import { Input } from "../input";
import { KubeObjectMeta } from "../kube-object-meta";
import { MonacoEditor } from "../monaco-editor"; import { MonacoEditor } from "../monaco-editor";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
@ -42,8 +41,6 @@ export class CRDDetails extends React.Component<CRDDetailsProps> {
return ( return (
<div className="CRDDetails"> <div className="CRDDetails">
<KubeObjectMeta object={crd}/>
<DrawerItem name="Group"> <DrawerItem name="Group">
{crd.getGroup()} {crd.getGroup()}
</DrawerItem> </DrawerItem>

View File

@ -11,7 +11,6 @@ import { cssNames } from "../../utils";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
import { Input } from "../input"; import { Input } from "../input";
import type { AdditionalPrinterColumnsV1 } from "../../../common/k8s-api/endpoints/custom-resource-definition.api"; import type { AdditionalPrinterColumnsV1 } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
import { CustomResourceDefinition } 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"; import logger from "../../../common/logger";
export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> { export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> {
crd: CustomResourceDefinition; crd?: CustomResourceDefinition;
} }
function convertSpecValue(value: unknown): React.ReactNode { function convertSpecValue(value: unknown): React.ReactNode {
@ -129,7 +128,6 @@ export class CustomResourceDetails extends React.Component<CustomResourceDetails
return ( return (
<div className={cssNames("CrdResourceDetails", crd.getResourceKind())}> <div className={cssNames("CrdResourceDetails", crd.getResourceKind())}>
<KubeObjectMeta object={object} />
{this.renderAdditionalColumns(object, extraColumns)} {this.renderAdditionalColumns(object, extraColumns)}
{this.renderStatus(object, extraColumns)} {this.renderStatus(object, extraColumns)}
</div> </div>

View File

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

View File

@ -12,7 +12,6 @@ import { Link } from "react-router-dom";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api"; import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
import { KubeObjectMeta } from "../kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import type { ApiManager } from "../../../common/k8s-api/api-manager"; import type { ApiManager } from "../../../common/k8s-api/api-manager";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
@ -52,8 +51,6 @@ const NonInjectedEventDetails = observer(({
return ( return (
<div className={cssNames("EventDetails", className)}> <div className={cssNames("EventDetails", className)}>
<KubeObjectMeta object={event}/>
<DrawerItem name="Message"> <DrawerItem name="Message">
{message} {message}
</DrawerItem> </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 { Pod } from "../../../common/k8s-api/endpoints/pod.api";
import type { GetPodById } from "../+workloads-pods/get-pod-by-id.injectable"; import type { GetPodById } from "../+workloads-pods/get-pod-by-id.injectable";
export interface EventStoreDependencies { interface Dependencies {
getPodById: GetPodById; getPodById: GetPodById;
} }
export class EventStore extends KubeObjectStore<KubeEvent, KubeEventApi> { export class EventStore extends KubeObjectStore<KubeEvent, KubeEventApi> {
constructor( constructor(
protected readonly dependencies: EventStoreDependencies, protected readonly dependencies: Dependencies,
api: KubeEventApi, api: KubeEventApi,
opts: KubeObjectStoreOptions = {}, 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 "./namespace-details.scss";
import React from "react"; import React from "react";
import { computed, makeObservable, observable, reaction } from "mobx"; import { computed, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
@ -14,42 +14,32 @@ import { Namespace } from "../../../common/k8s-api/endpoints";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Spinner } from "../spinner"; 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 { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; 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 { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import type { ResourceQuotaStore } from "../+config-resource-quotas/store"; import type { ResourceQuotaStore } from "../+config-resource-quotas/store";
import type { LimitRangeStore } from "../+config-limit-ranges/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 getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import limitRangeStoreInjectable from "../+config-limit-ranges/store.injectable"; import limitRangeStoreInjectable from "../+config-limit-ranges/store.injectable";
import resourceQuotaStoreInjectable from "../+config-resource-quotas/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 type { Logger } from "../../../common/logger";
import requestPodMetricsInNamespaceInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable"; import loggerInjectable from "../../../common/logger.injectable";
export interface NamespaceDetailsProps extends KubeObjectDetailsProps<Namespace> { export interface NamespaceDetailsProps extends KubeObjectDetailsProps<Namespace> {
} }
interface Dependencies { interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl; getDetailsUrl: GetDetailsUrl;
resourceQuotaStore: ResourceQuotaStore; resourceQuotaStore: ResourceQuotaStore;
limitRangeStore: LimitRangeStore; limitRangeStore: LimitRangeStore;
requestPodMetricsInNamespace: RequestPodMetricsInNamespace; logger: Logger;
} }
@observer @observer
class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps & Dependencies> { class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps & Dependencies> {
@observable metrics: PodMetricInNamespaceData | null = null;
constructor(props: NamespaceDetailsProps & Dependencies) { constructor(props: NamespaceDetailsProps & Dependencies) {
super(props); super(props);
makeObservable(this); makeObservable(this);
@ -57,10 +47,6 @@ class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.resourceQuotaStore, this.props.resourceQuotaStore,
this.props.limitRangeStore, this.props.limitRangeStore,
@ -80,40 +66,23 @@ class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps
return this.props.limitRangeStore.getAllByNs(namespace); return this.props.limitRangeStore.getAllByNs(namespace);
} }
loadMetrics = async () => {
this.metrics = await this.props.requestPodMetricsInNamespace(this.props.object.getName());
};
render() { render() {
const { object: namespace, getActiveClusterEntity, resourceQuotaStore, getDetailsUrl, limitRangeStore } = this.props; const { object: namespace, resourceQuotaStore, getDetailsUrl, limitRangeStore } = this.props;
if (!namespace) { if (!namespace) {
return null; return null;
} }
if (!(namespace instanceof Namespace)) { 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; return null;
} }
const status = namespace.getStatus(); const status = namespace.getStatus();
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Namespace);
return ( return (
<div className="NamespaceDetails"> <div className="NamespaceDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={namespace}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={namespace}/>
<DrawerItem name="Status"> <DrawerItem name="Status">
<span className={cssNames("status", status.toLowerCase())}>{status}</span> <span className={cssNames("status", status.toLowerCase())}>{status}</span>
</DrawerItem> </DrawerItem>
@ -143,11 +112,10 @@ export const NamespaceDetails = withInjectables<Dependencies, NamespaceDetailsPr
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable),
limitRangeStore: di.inject(limitRangeStoreInjectable), limitRangeStore: di.inject(limitRangeStoreInjectable),
resourceQuotaStore: di.inject(resourceQuotaStoreInjectable), 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 { DrawerTitle } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Endpoints } from "../../../common/k8s-api/endpoints"; import { Endpoints } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import { EndpointSubsetList } from "./endpoint-subset-list"; import { EndpointSubsetList } from "./endpoint-subset-list";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
@ -34,7 +33,6 @@ export class EndpointsDetails extends React.Component<EndpointsDetailsProps> {
return ( return (
<div className="EndpointDetails"> <div className="EndpointDetails">
<KubeObjectMeta object={endpoint}/>
<DrawerTitle>Subsets</DrawerTitle> <DrawerTitle>Subsets</DrawerTitle>
{ {
endpoint.getEndpointSubsets().map((subset) => ( endpoint.getEndpointSubsets().map((subset) => (

View File

@ -6,54 +6,26 @@
import "./ingress-details.scss"; import "./ingress-details.scss";
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { observer } from "mobx-react";
import { makeObservable, observable, reaction } from "mobx";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import type { ILoadBalancerIngress } from "../../../common/k8s-api/endpoints"; import type { ILoadBalancerIngress } from "../../../common/k8s-api/endpoints";
import { Ingress } from "../../../common/k8s-api/endpoints"; import { Ingress } from "../../../common/k8s-api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { ResourceMetrics } from "../resource-metrics";
import type { KubeObjectDetailsProps } from "../kube-object-details"; 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 { computeRuleDeclarations } from "../../../common/k8s-api/endpoints/ingress.api";
import { getActiveClusterEntity } from "../../api/catalog/entity/legacy-globals"; import type { Logger } from "../../../common/logger";
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 { withInjectables } from "@ogre-tools/injectable-react"; 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> { export interface IngressDetailsProps extends KubeObjectDetailsProps<Ingress> {
} }
interface Dependencies { interface Dependencies {
requestIngressMetrics: RequestIngressMetrics; logger: Logger;
} }
@observer @observer
class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & Dependencies> { 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) { renderPaths(ingress: Ingress) {
return ingress.getRules() return ingress.getRules()
.map((rule, index) => ( .map((rule, index) => (
@ -124,7 +96,7 @@ class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & De
} }
render() { render() {
const { object: ingress } = this.props; const { object: ingress, logger } = this.props;
if (!ingress) { if (!ingress) {
return null; return null;
@ -136,25 +108,10 @@ class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & De
return null; return null;
} }
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Ingress);
const port = ingress.getServiceNamePort(); const port = ingress.getServiceNamePort();
return ( return (
<div className="IngressDetails"> <div className="IngressDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={[
"Network",
"Duration",
]}
object={ingress}
metrics={this.metrics}
>
<IngressCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={ingress}/>
<DrawerItem name="Ports"> <DrawerItem name="Ports">
{ingress.getPorts()} {ingress.getPorts()}
</DrawerItem> </DrawerItem>
@ -181,6 +138,6 @@ class NonInjectedIngressDetails extends React.Component<IngressDetailsProps & De
export const IngressDetails = withInjectables<Dependencies, IngressDetailsProps>(NonInjectedIngressDetails, { export const IngressDetails = withInjectables<Dependencies, IngressDetailsProps>(NonInjectedIngressDetails, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
requestIngressMetrics: di.inject(requestIngressMetricsInjectable), logger: di.inject(loggerInjectable),
}), }),
}); });

View File

@ -32,85 +32,86 @@ interface Dependencies {
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const NonInjectedIngresses = observer((props: Dependencies) => {
class NonInjectedIngresses extends React.Component<Dependencies> { const {
render() { ingressStore,
return ( filterByNamespace,
<SiblingsInTabLayout> } = props;
<KubeObjectListLayout
isConfigurable
tableId="network_ingresses"
className="Ingresses"
store={this.props.ingressStore}
sortingCallbacks={{
[columnId.name]: ingress => ingress.getName(),
[columnId.namespace]: ingress => ingress.getNs(),
[columnId.age]: ingress => -ingress.getCreationTimestamp(),
}}
searchFilters={[
ingress => ingress.getSearchFields(),
ingress => ingress.getPorts(),
]}
renderHeaderTitle="Ingresses"
renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ className: "warning", showWithColumn: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers },
{ title: "Rules", className: "rules", id: columnId.rules },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]}
renderTableContents={ingress => [
ingress.getName(),
<KubeObjectStatusIcon key="icon" object={ingress} />,
<a
key="namespace"
className="filterNamespace"
onClick={prevDefault(() => this.props.filterByNamespace(ingress.getNs()))}
>
{ingress.getNs()}
</a>,
ingress.getLoadBalancers().map(lb => <p key={lb}>{lb}</p>),
computeRouteDeclarations(ingress).map(decl => (
decl.displayAsLink
? (
<div key={decl.url} className="ingressRule">
<a
href={decl.url}
rel="noreferrer"
target="_blank"
onClick={e => e.stopPropagation()}
>
{decl.url}
</a>
{`${decl.service}`}
</div>
)
: (
<div key={decl.url} className="ingressRule">
{`${decl.url}${decl.service}`}
</div>
)
)),
<KubeObjectAge key="age" object={ingress} />,
]}
tableProps={{
customRowHeights: (item, lineHeight, paddings) => {
const lines = item.getRoutes().length || 1;
return lines * lineHeight + paddings; return (
}, <SiblingsInTabLayout>
}} <KubeObjectListLayout
/> isConfigurable
</SiblingsInTabLayout> tableId="network_ingresses"
); className="Ingresses"
} store={ ingressStore }
} sortingCallbacks={ {
[columnId.name]: ingress => ingress.getName(),
[columnId.namespace]: ingress => ingress.getNs(),
[columnId.age]: ingress => -ingress.getCreationTimestamp(),
} }
searchFilters={ [
ingress => ingress.getSearchFields(),
ingress => ingress.getPorts(),
] }
renderHeaderTitle="Ingresses"
renderTableHeader={ [
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ className: "warning", showWithColumn: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers },
{ title: "Rules", className: "rules", id: columnId.rules },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
] }
renderTableContents={ ingress => [
ingress.getName(),
<KubeObjectStatusIcon key="icon" object={ ingress } />,
<a
key="namespace"
className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(ingress.getNs()))}
>
{ingress.getNs()}
</a>,
ingress.getLoadBalancers().map(lb => <p key={ lb }>{ lb }</p>),
computeRouteDeclarations(ingress).map(decl => (
decl.displayAsLink
? (
<div key={ decl.url } className="ingressRule">
<a
href={ decl.url }
rel="noreferrer"
target="_blank"
onClick={ e => e.stopPropagation() }
>
{ decl.url }
</a>
{ `${decl.service}` }
</div>
)
: (
<div key={ decl.url } className="ingressRule">
{ `${decl.url}${decl.service}` }
</div>
)
)),
<KubeObjectAge key="age" object={ ingress } />,
] }
tableProps={ {
customRowHeights: (item, lineHeight, paddings) => {
const lines = item.getRoutes().length || 1;
return lines * lineHeight + paddings;
},
} } />
</SiblingsInTabLayout>
);
});
export const Ingresses = withInjectables<Dependencies>(NonInjectedIngresses, { export const Ingresses = withInjectables<Dependencies>(NonInjectedIngresses, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
filterByNamespace: di.inject(filterByNamespaceInjectable),
ingressStore: di.inject(ingressStoreInjectable), 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 { SubTitle } from "../layout/sub-title";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object"; import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
@ -170,8 +169,6 @@ export class NetworkPolicyDetails extends React.Component<NetworkPolicyDetailsPr
return ( return (
<div className={styles.NetworkPolicyDetails}> <div className={styles.NetworkPolicyDetails}>
<KubeObjectMeta object={policy}/>
<DrawerItem name="Pod Selector" labelsOnly={selector.length > 0}> <DrawerItem name="Pod Selector" labelsOnly={selector.length > 0}>
{ {
selector.length > 0 selector.length > 0

View File

@ -11,7 +11,6 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Service } from "../../../common/k8s-api/endpoints"; import { Service } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import { ServicePortComponent } from "./service-port-component"; import { ServicePortComponent } from "./service-port-component";
import type { EndpointsStore } from "../+network-endpoints/store"; import type { EndpointsStore } from "../+network-endpoints/store";
import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import { ServiceDetailsEndpoint } from "./service-details-endpoint";
@ -75,8 +74,6 @@ class NonInjectedServiceDetails extends React.Component<ServiceDetailsProps & De
return ( return (
<div className="ServicesDetails"> <div className="ServicesDetails">
<KubeObjectMeta object={service}/>
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{service.getSelector().map(selector => <Badge key={selector} label={selector}/>)} {service.getSelector().map(selector => <Badge key={selector} label={selector}/>)}
</DrawerItem> </DrawerItem>

View File

@ -11,26 +11,18 @@ import kebabCase from "lodash/kebabCase";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerItemLabels } from "../drawer"; import { DrawerItem, DrawerItemLabels } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { ResourceMetrics } from "../resource-metrics";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { formatNodeTaint, Node } from "../../../common/k8s-api/endpoints"; 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 { 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 { NodeDetailsResources } from "./details-resources";
import { DrawerTitle } from "../drawer/drawer-title"; 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 { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import type { PodStore } from "../+workloads-pods/store"; import type { PodStore } from "../+workloads-pods/store";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable";
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import loggerInjectable from "../../../common/logger.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";
export interface NodeDetailsProps extends KubeObjectDetailsProps<Node> { export interface NodeDetailsProps extends KubeObjectDetailsProps<Node> {
} }
@ -38,39 +30,21 @@ export interface NodeDetailsProps extends KubeObjectDetailsProps<Node> {
interface Dependencies { interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore; podStore: PodStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
} }
@observer @observer
class NonInjectedNodeDetails extends React.Component<NodeDetailsProps & Dependencies> { class NonInjectedNodeDetails extends React.Component<NodeDetailsProps & Dependencies> {
@observable metrics: ClusterMetricData | null = null;
constructor(props: NodeDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object.getName(), () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore, this.props.podStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: node, requestClusterMetricsByNodeNames } = this.props;
this.metrics = await requestClusterMetricsByNodeNames([node.getName()]);
};
render() { render() {
const { object: node, podStore, getActiveClusterEntity } = this.props; const { object: node, podStore, logger } = this.props;
if (!node) { if (!node) {
return null; return null;
@ -86,27 +60,9 @@ class NonInjectedNodeDetails extends React.Component<NodeDetailsProps & Dependen
const conditions = node.getActiveConditions(); const conditions = node.getActiveConditions();
const taints = node.getTaints(); const taints = node.getTaints();
const childPods = podStore.getPodsByNode(node.getName()); const childPods = podStore.getPodsByNode(node.getName());
const { metrics } = this;
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Node);
return ( return (
<div className="NodeDetails"> <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 && ( {addresses && (
<DrawerItem name="Addresses"> <DrawerItem name="Addresses">
{ {
@ -197,8 +153,7 @@ export const NodeDetails = withInjectables<Dependencies, NodeDetailsProps>(NonIn
...props, ...props,
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), logger: di.inject(loggerInjectable),
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
}), }),
}); });

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 { PodSecurityPolicy } from "../../../common/k8s-api/endpoints";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps<PodSecurityPolicy> { export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps<PodSecurityPolicy> {
@ -74,8 +73,6 @@ export class PodSecurityPolicyDetails extends React.Component<PodSecurityPolicyD
return ( return (
<div className="PodSecurityPolicyDetails"> <div className="PodSecurityPolicyDetails">
<KubeObjectMeta object={psp}/>
{allowedCapabilities && ( {allowedCapabilities && (
<DrawerItem name="Allowed Capabilities"> <DrawerItem name="Allowed Capabilities">
{allowedCapabilities.join(", ")} {allowedCapabilities.join(", ")}

View File

@ -12,7 +12,6 @@ import { Badge } from "../badge";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { StorageClass } from "../../../common/k8s-api/endpoints"; import { StorageClass } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import type { StorageClassStore } from "./store"; import type { StorageClassStore } from "./store";
import { VolumeDetailsList } from "../+storage-volumes/volume-details-list"; import { VolumeDetailsList } from "../+storage-volumes/volume-details-list";
import type { PersistentVolumeStore } from "../+storage-volumes/store"; import type { PersistentVolumeStore } from "../+storage-volumes/store";
@ -60,8 +59,6 @@ class NonInjectedStorageClassDetails extends React.Component<StorageClassDetails
return ( return (
<div className="StorageClassDetails"> <div className="StorageClassDetails">
<KubeObjectMeta object={storageClass}/>
{provisioner && ( {provisioner && (
<DrawerItem name="Provisioner" labelsOnly> <DrawerItem name="Provisioner" labelsOnly>
<Badge label={provisioner}/> <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 "./volume-claim-details.scss";
import React, { Fragment } from "react"; import React, { Fragment } from "react";
import { makeObservable, observable, reaction } from "mobx"; import { observer } from "mobx-react";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { Link } from "react-router-dom"; 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 type { KubeObjectDetailsProps } from "../kube-object-details";
import { PersistentVolumeClaim, storageClassApi } from "../../../common/k8s-api/endpoints"; import type { StorageClassApi } from "../../../common/k8s-api/endpoints";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { PersistentVolumeClaim } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger";
import logger from "../../../common/logger";
import type { PersistentVolumeClaimMetricData, RequestPersistentVolumeClaimMetrics } from "../../../common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable";
import { withInjectables } from "@ogre-tools/injectable-react"; 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 { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import type { PodStore } from "../+workloads-pods/store"; 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 getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable";
import { stopPropagation } from "../../../renderer/utils"; 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> { export interface PersistentVolumeClaimDetailsProps extends KubeObjectDetailsProps<PersistentVolumeClaim> {
} }
interface Dependencies { interface Dependencies {
requestPersistentVolumeClaimMetrics: RequestPersistentVolumeClaimMetrics;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl; getDetailsUrl: GetDetailsUrl;
podStore: PodStore; podStore: PodStore;
storageClassApi: StorageClassApi;
logger: Logger;
} }
@observer @observer
class NonInjectedPersistentVolumeClaimDetails extends React.Component<PersistentVolumeClaimDetailsProps & Dependencies> { 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() { render() {
const { object: volumeClaim, getActiveClusterEntity, podStore, getDetailsUrl } = this.props; const { object: volumeClaim, podStore, getDetailsUrl, storageClassApi, logger } = this.props;
if (!volumeClaim) { if (!volumeClaim) {
return null; return null;
@ -77,27 +50,13 @@ class NonInjectedPersistentVolumeClaimDetails extends React.Component<Persistent
const { storageClassName, accessModes } = volumeClaim.spec; const { storageClassName, accessModes } = volumeClaim.spec;
const pods = volumeClaim.getPods(podStore.items); const pods = volumeClaim.getPods(podStore.items);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.VolumeClaim);
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ const storageClassDetailsUrl = getDetailsUrl(storageClassApi.formatUrlForNotListing({
name: storageClassName, name: storageClassName,
})); }));
return ( return (
<div className="PersistentVolumeClaimDetails"> <div className="PersistentVolumeClaimDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={[
"Disk",
]}
object={volumeClaim}
metrics={this.metrics}
>
<VolumeClaimDiskChart />
</ResourceMetrics>
)}
<KubeObjectMeta object={volumeClaim} />
<DrawerItem name="Access Modes"> <DrawerItem name="Access Modes">
{accessModes?.join(", ")} {accessModes?.join(", ")}
</DrawerItem> </DrawerItem>
@ -147,9 +106,9 @@ class NonInjectedPersistentVolumeClaimDetails extends React.Component<Persistent
export const PersistentVolumeClaimDetails = withInjectables<Dependencies, PersistentVolumeClaimDetailsProps>(NonInjectedPersistentVolumeClaimDetails, { export const PersistentVolumeClaimDetails = withInjectables<Dependencies, PersistentVolumeClaimDetailsProps>(NonInjectedPersistentVolumeClaimDetails, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
requestPersistentVolumeClaimMetrics: di.inject(requestPersistentVolumeClaimMetricsInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable),
podStore: di.inject(podStoreInjectable), 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 { Badge } from "../badge";
import { PersistentVolume, persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints"; import { PersistentVolume, persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
import { getDetailsUrl } from "../kube-detail-params"; import { getDetailsUrl } from "../kube-detail-params";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import { stopPropagation } from "../../../renderer/utils"; import { stopPropagation } from "../../../renderer/utils";
@ -44,7 +43,6 @@ export class PersistentVolumeDetails extends React.Component<PersistentVolumeDet
return ( return (
<div className="PersistentVolumeDetails"> <div className="PersistentVolumeDetails">
<KubeObjectMeta object={volume} />
<DrawerItem name="Capacity"> <DrawerItem name="Capacity">
{capacity?.storage} {capacity?.storage}
</DrawerItem> </DrawerItem>

View File

@ -14,7 +14,6 @@ import { autoBind, ObservableHashSet, prevDefault } from "../../../utils";
import { AddRemoveButtons } from "../../add-remove-buttons"; import { AddRemoveButtons } from "../../add-remove-buttons";
import { DrawerTitle } from "../../drawer"; import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object-details"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../../table"; import { Table, TableCell, TableHead, TableRow } from "../../table";
import { ClusterRoleBindingDialog } from "./dialog"; import { ClusterRoleBindingDialog } from "./dialog";
import { clusterRoleBindingStore } from "./legacy-store"; import { clusterRoleBindingStore } from "./legacy-store";
@ -76,8 +75,6 @@ class NonInjectedClusterRoleBindingDetails extends React.Component<ClusterRoleBi
return ( return (
<div className="ClusterRoleBindingDetails"> <div className="ClusterRoleBindingDetails">
<KubeObjectMeta object={clusterRoleBinding} />
<DrawerTitle>Reference</DrawerTitle> <DrawerTitle>Reference</DrawerTitle>
<Table> <Table>
<TableHead> <TableHead>

View File

@ -10,7 +10,6 @@ import React from "react";
import { DrawerTitle } from "../../drawer"; import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object-details"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object-meta";
import type { ClusterRole } from "../../../../common/k8s-api/endpoints"; import type { ClusterRole } from "../../../../common/k8s-api/endpoints";
export interface ClusterRoleDetailsProps extends KubeObjectDetailsProps<ClusterRole> { export interface ClusterRoleDetailsProps extends KubeObjectDetailsProps<ClusterRole> {
@ -26,8 +25,6 @@ export class ClusterRoleDetails extends React.Component<ClusterRoleDetailsProps>
return ( return (
<div className="ClusterRoleDetails"> <div className="ClusterRoleDetails">
<KubeObjectMeta object={clusterRole}/>
<DrawerTitle>Rules</DrawerTitle> <DrawerTitle>Rules</DrawerTitle>
{rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => {
return ( return (

View File

@ -13,7 +13,6 @@ import { prevDefault } from "../../../utils";
import { AddRemoveButtons } from "../../add-remove-buttons"; import { AddRemoveButtons } from "../../add-remove-buttons";
import { DrawerTitle } from "../../drawer"; import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object-details"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../../table"; import { Table, TableCell, TableHead, TableRow } from "../../table";
import { RoleBindingDialog } from "./dialog"; import { RoleBindingDialog } from "./dialog";
import { roleBindingStore } from "./legacy-store"; import { roleBindingStore } from "./legacy-store";
@ -71,8 +70,6 @@ class NonInjectedRoleBindingDetails extends React.Component<RoleBindingDetailsPr
return ( return (
<div className="RoleBindingDetails"> <div className="RoleBindingDetails">
<KubeObjectMeta object={roleBinding} />
<DrawerTitle>Reference</DrawerTitle> <DrawerTitle>Reference</DrawerTitle>
<Table> <Table>
<TableHead> <TableHead>

View File

@ -11,7 +11,6 @@ import React from "react";
import type { Role } from "../../../../common/k8s-api/endpoints"; import type { Role } from "../../../../common/k8s-api/endpoints";
import { DrawerTitle } from "../../drawer"; import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object-details"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object-meta";
export interface RoleDetailsProps extends KubeObjectDetailsProps<Role> { export interface RoleDetailsProps extends KubeObjectDetailsProps<Role> {
} }
@ -26,7 +25,6 @@ export class RoleDetails extends React.Component<RoleDetailsProps> {
return ( return (
<div className="RoleDetails"> <div className="RoleDetails">
<KubeObjectMeta object={role}/>
<DrawerTitle>Rules</DrawerTitle> <DrawerTitle>Rules</DrawerTitle>
{rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => {
return ( return (

View File

@ -14,7 +14,6 @@ import type { Secret, ServiceAccount } from "../../../../common/k8s-api/endpoint
import { DrawerItem, DrawerTitle } from "../../drawer"; import { DrawerItem, DrawerTitle } from "../../drawer";
import { Icon } from "../../icon"; import { Icon } from "../../icon";
import type { KubeObjectDetailsProps } from "../../kube-object-details"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object-meta";
import { Spinner } from "../../spinner"; import { Spinner } from "../../spinner";
import { ServiceAccountsSecret } from "./secret"; import { ServiceAccountsSecret } from "./secret";
import type { SecretStore } from "../../+config-secrets/store"; import type { SecretStore } from "../../+config-secrets/store";
@ -137,8 +136,6 @@ class NonInjectedServiceAccountsDetails extends React.Component<ServiceAccountsD
return ( return (
<div className="ServiceAccountsDetails"> <div className="ServiceAccountsDetails">
<KubeObjectMeta object={serviceAccount}/>
{tokens.length > 0 && ( {tokens.length > 0 && (
<DrawerItem name="Tokens" className="links"> <DrawerItem name="Tokens" className="links">
{this.renderSecretLinks(tokens)} {this.renderSecretLinks(tokens)}

View File

@ -17,7 +17,6 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { getDetailsUrl } from "../kube-detail-params"; import { getDetailsUrl } from "../kube-detail-params";
import type { Job } from "../../../common/k8s-api/endpoints"; import type { Job } from "../../../common/k8s-api/endpoints";
import { CronJob } from "../../../common/k8s-api/endpoints"; import { CronJob } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
@ -61,7 +60,6 @@ class NonInjectedCronJobDetails extends React.Component<CronJobDetailsProps & De
return ( return (
<div className="CronJobDetails"> <div className="CronJobDetails">
<KubeObjectMeta object={cronJob}/>
<DrawerItem name="Schedule"> <DrawerItem name="Schedule">
{ {
cronJob.isNeverRun() cronJob.isNeverRun()

View File

@ -13,11 +13,9 @@ import moment from "moment";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age"; import { KubeObjectAge } from "../kube-object/age";
import type { CronJobStore } from "./store"; import type { CronJobStore } from "./store";
import type { JobStore } from "../+workloads-jobs/store";
import type { EventStore } from "../+events/store"; import type { EventStore } from "../+events/store";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import cronJobStoreInjectable from "./store.injectable"; import cronJobStoreInjectable from "./store.injectable";
import jobStoreInjectable from "../+workloads-jobs/store.injectable";
import eventStoreInjectable from "../+events/store.injectable"; import eventStoreInjectable from "../+events/store.injectable";
import { prevDefault } from "../../utils"; import { prevDefault } from "../../utils";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
@ -35,84 +33,78 @@ enum columnId {
interface Dependencies { interface Dependencies {
cronJobStore: CronJobStore; cronJobStore: CronJobStore;
jobStore: JobStore;
eventStore: EventStore; eventStore: EventStore;
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const NonInjectedCronJobs = observer((props: Dependencies) => {
class NonInjectedCronJobs extends React.Component<Dependencies>{ const {
render() { cronJobStore,
const { eventStore,
cronJobStore, filterByNamespace,
eventStore, } = props;
jobStore,
filterByNamespace,
} = this.props;
return ( return (
<SiblingsInTabLayout> <SiblingsInTabLayout>
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="workload_cronjobs" tableId="workload_cronjobs"
className="CronJobs" className="CronJobs"
store={cronJobStore} store={cronJobStore}
dependentStores={[jobStore, eventStore]} dependentStores={[eventStore]}
sortingCallbacks={{ sortingCallbacks={{
[columnId.name]: cronJob => cronJob.getName(), [columnId.name]: cronJob => cronJob.getName(),
[columnId.namespace]: cronJob => cronJob.getNs(), [columnId.namespace]: cronJob => cronJob.getNs(),
[columnId.suspend]: cronJob => cronJob.getSuspendFlag(), [columnId.suspend]: cronJob => cronJob.getSuspendFlag(),
[columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob),
[columnId.lastSchedule]: cronJob => ( [columnId.lastSchedule]: cronJob => (
cronJob.status?.lastScheduleTime cronJob.status?.lastScheduleTime
? moment().diff(cronJob.status.lastScheduleTime) ? moment().diff(cronJob.status.lastScheduleTime)
: 0 : 0
), ),
[columnId.age]: cronJob => -cronJob.getCreationTimestamp(), [columnId.age]: cronJob => -cronJob.getCreationTimestamp(),
}} }}
searchFilters={[ searchFilters={[
cronJob => cronJob.getSearchFields(), cronJob => cronJob.getSearchFields(),
cronJob => cronJob.getSchedule(), cronJob => cronJob.getSchedule(),
]} ]}
renderHeaderTitle="Cron Jobs" renderHeaderTitle="Cron Jobs"
renderTableHeader={[ renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ className: "warning", showWithColumn: columnId.name }, { className: "warning", showWithColumn: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "Schedule", className: "schedule", id: columnId.schedule }, { title: "Schedule", className: "schedule", id: columnId.schedule },
{ title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend }, { title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend },
{ title: "Active", className: "active", sortBy: columnId.active, id: columnId.active }, { title: "Active", className: "active", sortBy: columnId.active, id: columnId.active },
{ title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]} ]}
renderTableContents={cronJob => [ renderTableContents={cronJob => [
cronJob.getName(), cronJob.getName(),
<KubeObjectStatusIcon key="icon" object={cronJob} />, <KubeObjectStatusIcon key="icon" object={cronJob} />,
<a <a
key="namespace" key="namespace"
className="filterNamespace" className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(cronJob.getNs()))} onClick={prevDefault(() => filterByNamespace(cronJob.getNs()))}
> >
{cronJob.getNs()} {cronJob.getNs()}
</a>, </a>,
cronJob.isNeverRun() ? "never" : cronJob.getSchedule(), cronJob.isNeverRun() ? "never" : cronJob.getSchedule(),
cronJob.getSuspendFlag(), cronJob.getSuspendFlag(),
cronJobStore.getActiveJobsNum(cronJob), cronJobStore.getActiveJobsNum(cronJob),
cronJob.getLastScheduleTime(), cronJob.getLastScheduleTime(),
<KubeObjectAge key="age" object={cronJob} />, <KubeObjectAge key="age" object={cronJob} />,
]} ]}
/> />
</SiblingsInTabLayout> </SiblingsInTabLayout>
); );
} });
}
export const CronJobs = withInjectables<Dependencies>(NonInjectedCronJobs, { export const CronJobs = withInjectables<Dependencies>(NonInjectedCronJobs, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
cronJobStore: di.inject(cronJobStoreInjectable), cronJobStore: di.inject(cronJobStoreInjectable),
eventStore: di.inject(eventStoreInjectable), eventStore: di.inject(eventStoreInjectable),
jobStore: di.inject(jobStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable), filterByNamespace: di.inject(filterByNamespaceInjectable),
}), }),
}); });

View File

@ -16,22 +16,16 @@ import type { DaemonSetStore } from "./store";
import type { PodStore } from "../+workloads-pods/store"; import type { PodStore } from "../+workloads-pods/store";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { DaemonSet } from "../../../common/k8s-api/endpoints"; 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 { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger";
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import daemonSetStoreInjectable from "./store.injectable"; import daemonSetStoreInjectable from "./store.injectable";
import podStoreInjectable from "../+workloads-pods/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 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 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> { export interface DaemonSetDetailsProps extends KubeObjectDetailsProps<DaemonSet> {
} }
@ -40,38 +34,21 @@ interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
daemonSetStore: DaemonSetStore; daemonSetStore: DaemonSetStore;
podStore: PodStore; podStore: PodStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestPodMetricsForDaemonSets: RequestPodMetricsForDaemonSets;
} }
@observer @observer
class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps & Dependencies> { class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps & Dependencies> {
@observable metrics: DaemonSetPodMetricData | null = null;
constructor(props: DaemonSetDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore, this.props.podStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: daemonSet, requestPodMetricsForDaemonSets } = this.props;
this.metrics = await requestPodMetricsForDaemonSets([daemonSet], daemonSet.getNs());
};
render() { render() {
const { object: daemonSet, daemonSetStore, podStore, getActiveClusterEntity } = this.props; const { object: daemonSet, daemonSetStore, logger } = this.props;
if (!daemonSet) { if (!daemonSet) {
return null; return null;
@ -88,21 +65,9 @@ class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps
const images = daemonSet.getImages(); const images = daemonSet.getImages();
const nodeSelector = daemonSet.getNodeSelectors(); const nodeSelector = daemonSet.getNodeSelectors();
const childPods = daemonSetStore.getChildPods(daemonSet); const childPods = daemonSetStore.getChildPods(daemonSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.DaemonSet);
return ( return (
<div className="DaemonSetDetails"> <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 && ( {selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{ {
@ -132,7 +97,6 @@ class NonInjectedDaemonSetDetails extends React.Component<DaemonSetDetailsProps
<DrawerItem name="Pod Status" className="pod-status"> <DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/> <PodDetailsStatuses pods={childPods}/>
</DrawerItem> </DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={daemonSet}/> <PodDetailsList pods={childPods} owner={daemonSet}/>
</div> </div>
); );
@ -147,5 +111,6 @@ export const DaemonSetDetails = withInjectables<Dependencies, DaemonSetDetailsPr
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
requestPodMetricsForDaemonSets: di.inject(requestPodMetricsForDaemonSetsInjectable), 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 { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age"; import { KubeObjectAge } from "../kube-object/age";
import type { DaemonSetStore } from "./store"; import type { DaemonSetStore } from "./store";
import type { PodStore } from "../+workloads-pods/store";
import type { EventStore } from "../+events/store"; import type { EventStore } from "../+events/store";
import { prevDefault } from "../../utils"; import { prevDefault } from "../../utils";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; 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 daemonSetStoreInjectable from "./store.injectable";
import eventStoreInjectable from "../+events/store.injectable"; import eventStoreInjectable from "../+events/store.injectable";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
enum columnId { enum columnId {
name = "name", name = "name",
@ -34,77 +32,70 @@ enum columnId {
interface Dependencies { interface Dependencies {
daemonSetStore: DaemonSetStore; daemonSetStore: DaemonSetStore;
podStore: PodStore;
eventStore: EventStore; eventStore: EventStore;
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const NonInjectedDaemonSets = observer((props: Dependencies) => {
class NonInjectedDaemonSets extends React.Component<Dependencies> { const {
getPodsLength(daemonSet: DaemonSet) { daemonSetStore,
return this.props.daemonSetStore.getChildPods(daemonSet).length; eventStore,
} filterByNamespace,
} = props;
render() { const getPodsLength = (daemonSet: DaemonSet) => daemonSetStore.getChildPods(daemonSet).length;
const {
daemonSetStore,
eventStore,
filterByNamespace,
podStore,
} = this.props;
return ( return (
<SiblingsInTabLayout> <SiblingsInTabLayout>
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="workload_daemonsets" tableId="workload_daemonsets"
className="DaemonSets" className="DaemonSets"
store={daemonSetStore} store={daemonSetStore}
dependentStores={[podStore, eventStore]} // status icon component uses event store dependentStores={[eventStore]} // status icon component uses event store
sortingCallbacks={{ sortingCallbacks={{
[columnId.name]: daemonSet => daemonSet.getName(), [columnId.name]: daemonSet => daemonSet.getName(),
[columnId.namespace]: daemonSet => daemonSet.getNs(), [columnId.namespace]: daemonSet => daemonSet.getNs(),
[columnId.pods]: daemonSet => this.getPodsLength(daemonSet), [columnId.pods]: daemonSet => getPodsLength(daemonSet),
[columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(), [columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(),
}} }}
searchFilters={[ searchFilters={[
daemonSet => daemonSet.getSearchFields(), daemonSet => daemonSet.getSearchFields(),
daemonSet => daemonSet.getLabels(), daemonSet => daemonSet.getLabels(),
]} ]}
renderHeaderTitle="Daemon Sets" renderHeaderTitle="Daemon Sets"
renderTableHeader={[ renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods },
{ className: "warning", showWithColumn: columnId.pods }, { className: "warning", showWithColumn: columnId.pods },
{ title: "Node Selector", className: "labels scrollable", id: columnId.labels }, { title: "Node Selector", className: "labels scrollable", id: columnId.labels },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]} ]}
renderTableContents={daemonSet => [ renderTableContents={daemonSet => [
daemonSet.getName(), daemonSet.getName(),
<a <a
key="namespace" key="namespace"
className="filterNamespace" className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(daemonSet.getNs()))} onClick={prevDefault(() => filterByNamespace(daemonSet.getNs()))}
> >
{daemonSet.getNs()} {daemonSet.getNs()}
</a>, </a>,
this.getPodsLength(daemonSet), getPodsLength(daemonSet),
<KubeObjectStatusIcon key="icon" object={daemonSet} />, <KubeObjectStatusIcon key="icon" object={daemonSet} />,
daemonSet.getNodeSelectors().map(selector => ( daemonSet.getNodeSelectors().map(selector => (
<Badge <Badge
key={selector} key={selector}
label={selector} label={selector}
scrollable scrollable
/> />
)), )),
<KubeObjectAge key="age" object={daemonSet} />, <KubeObjectAge key="age" object={daemonSet} />,
]} ]}
/> />
</SiblingsInTabLayout> </SiblingsInTabLayout>
); );
} });
}
export const DaemonSets = withInjectables<Dependencies>(NonInjectedDaemonSets, { export const DaemonSets = withInjectables<Dependencies>(NonInjectedDaemonSets, {
getProps: (di, props) => ({ getProps: (di, props) => ({
@ -112,6 +103,5 @@ export const DaemonSets = withInjectables<Dependencies>(NonInjectedDaemonSets, {
daemonSetStore: di.inject(daemonSetStoreInjectable), daemonSetStore: di.inject(daemonSetStoreInjectable),
eventStore: di.inject(eventStoreInjectable), eventStore: di.inject(eventStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable), 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 { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations";
import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics";
import type { DeploymentStore } from "./store"; 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 { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object-meta";
import type { ReplicaSetStore } from "../+workloads-replicasets/store"; import type { ReplicaSetStore } from "../+workloads-replicasets/store";
import { DeploymentReplicaSets } from "./deployment-replicasets"; import { DeploymentReplicaSets } from "./deployment-replicasets";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import type { Logger } from "../../../common/logger";
import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; 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 replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable";
import deploymentStoreInjectable from "./store.injectable"; import deploymentStoreInjectable from "./store.injectable";
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import loggerInjectable from "../../../common/logger.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";
export interface DeploymentDetailsProps extends KubeObjectDetailsProps<Deployment> { export interface DeploymentDetailsProps extends KubeObjectDetailsProps<Deployment> {
} }
interface Dependencies { interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore;
replicaSetStore: ReplicaSetStore; replicaSetStore: ReplicaSetStore;
deploymentStore: DeploymentStore; deploymentStore: DeploymentStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestPodMetricsForDeployments: RequestPodMetricsForDeployments;
} }
@observer @observer
class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProps & Dependencies> { class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProps & Dependencies> {
@observable metrics: DeploymentPodMetricData | null = null;
constructor(props: DeploymentDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore,
this.props.replicaSetStore, this.props.replicaSetStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: deployment, requestPodMetricsForDeployments } = this.props;
this.metrics = await requestPodMetricsForDeployments([deployment], deployment.getNs());
};
render() { render() {
const { object: deployment, podStore, replicaSetStore, deploymentStore, getActiveClusterEntity } = this.props; const { object: deployment, replicaSetStore, deploymentStore, logger } = this.props;
if (!deployment) { if (!deployment) {
return null; return null;
@ -94,21 +64,9 @@ class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProp
const selectors = deployment.getSelectors(); const selectors = deployment.getSelectors();
const childPods = deploymentStore.getChildPods(deployment); const childPods = deploymentStore.getChildPods(deployment);
const replicaSets = replicaSetStore.getReplicaSetsByOwner(deployment); const replicaSets = replicaSetStore.getReplicaSetsByOwner(deployment);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Deployment);
return ( return (
<div className="DeploymentDetails"> <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"> <DrawerItem name="Replicas">
{`${spec.replicas} desired, ${status?.updatedReplicas ?? 0} updated, `} {`${spec.replicas} desired, ${status?.updatedReplicas ?? 0} updated, `}
{`${status?.replicas ?? 0} total, ${status?.availableReplicas ?? 0} available, `} {`${status?.replicas ?? 0} total, ${status?.availableReplicas ?? 0} available, `}
@ -159,7 +117,6 @@ class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProp
</DrawerItem> </DrawerItem>
<PodDetailsTolerations workload={deployment}/> <PodDetailsTolerations workload={deployment}/>
<PodDetailsAffinities workload={deployment}/> <PodDetailsAffinities workload={deployment}/>
<ResourceMetricsText metrics={this.metrics}/>
<DeploymentReplicaSets replicaSets={replicaSets}/> <DeploymentReplicaSets replicaSets={replicaSets}/>
<PodDetailsList pods={childPods} owner={deployment}/> <PodDetailsList pods={childPods} owner={deployment}/>
</div> </div>
@ -171,11 +128,9 @@ export const DeploymentDetails = withInjectables<Dependencies, DeploymentDetails
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable),
replicaSetStore: di.inject(replicaSetStoreInjectable), replicaSetStore: di.inject(replicaSetStoreInjectable),
deploymentStore: di.inject(deploymentStoreInjectable), deploymentStore: di.inject(deploymentStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), logger: di.inject(loggerInjectable),
requestPodMetricsForDeployments: di.inject(requestPodMetricsForDeploymentsInjectable),
}), }),
}); });

View File

@ -8,7 +8,6 @@ import "./deployment-replicasets.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; import type { ReplicaSet } from "../../../common/k8s-api/endpoints";
import type { KubeObjectMenuProps } from "../kube-object-menu";
import { KubeObjectMenu } from "../kube-object-menu"; import { KubeObjectMenu } from "../kube-object-menu";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { prevDefault, stopPropagation } from "../../utils"; import { prevDefault, stopPropagation } from "../../utils";
@ -30,6 +29,11 @@ enum sortBy {
age = "age", age = "age",
} }
interface Dependencies {
replicaSetStore: ReplicaSetStore;
showDetails: ShowDetails;
}
export interface DeploymentReplicaSetsProps { export interface DeploymentReplicaSetsProps {
replicaSets: ReplicaSet[]; replicaSets: ReplicaSet[];
} }
@ -96,7 +100,7 @@ class NonInjectedDeploymentReplicaSets extends React.Component<DeploymentReplica
<TableCell className="pods">{this.getPodsLength(replica)}</TableCell> <TableCell className="pods">{this.getPodsLength(replica)}</TableCell>
<TableCell className="age"><KubeObjectAge key="age" object={replica} /></TableCell> <TableCell className="age"><KubeObjectAge key="age" object={replica} /></TableCell>
<TableCell className="actions" onClick={stopPropagation}> <TableCell className="actions" onClick={stopPropagation}>
<ReplicaSetMenu object={replica} /> <KubeObjectMenu object={replica} />
</TableCell> </TableCell>
</TableRow> </TableRow>
)) ))
@ -114,9 +118,3 @@ export const DeploymentReplicaSets = withInjectables<Dependencies, DeploymentRep
showDetails: di.inject(showDetailsInjectable), 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 type { KubeObjectDetailsProps } from "../kube-object-details";
import { Job } from "../../../common/k8s-api/endpoints"; import { Job } from "../../../common/k8s-api/endpoints";
import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger";
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 { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import type { PodStore } from "../+workloads-pods/store"; import type { PodStore } from "../+workloads-pods/store";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable";
import jobStoreInjectable from "./store.injectable"; import jobStoreInjectable from "./store.injectable";
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import loggerInjectable from "../../../common/logger.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";
export interface JobDetailsProps extends KubeObjectDetailsProps<Job> { export interface JobDetailsProps extends KubeObjectDetailsProps<Job> {
} }
@ -41,38 +33,21 @@ interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore; podStore: PodStore;
jobStore: JobStore; jobStore: JobStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestPodMetricsForJobs: RequestPodMetricsForJobs;
} }
@observer @observer
class NonInjectedJobDetails extends React.Component<JobDetailsProps & Dependencies> { class NonInjectedJobDetails extends React.Component<JobDetailsProps & Dependencies> {
@observable metrics: JobPodMetricData | null = null;
constructor(props: JobDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore, this.props.podStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: job, requestPodMetricsForJobs } = this.props;
this.metrics = await requestPodMetricsForJobs([job], job.getNs(), "");
};
render() { render() {
const { object: job, jobStore, getActiveClusterEntity } = this.props; const { object: job, jobStore, logger } = this.props;
if (!job) { if (!job) {
return null; return null;
@ -89,21 +64,9 @@ class NonInjectedJobDetails extends React.Component<JobDetailsProps & Dependenci
const images = job.getImages(); const images = job.getImages();
const childPods = jobStore.getChildPods(job); const childPods = jobStore.getChildPods(job);
const condition = job.getCondition(); const condition = job.getCondition();
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Job);
return ( return (
<div className="JobDetails"> <div className="JobDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={job}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={job}/>
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{ {
Object.keys(selectors).map(label => <Badge key={label} label={label}/>) 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), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
jobStore: di.inject(jobStoreInjectable), jobStore: di.inject(jobStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), logger: di.inject(loggerInjectable),
requestPodMetricsForJobs: di.inject(requestPodMetricsForJobsInjectable),
}), }),
}); });

View File

@ -35,67 +35,64 @@ interface Dependencies {
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const NonInjectedJobs = observer((props: Dependencies) => {
class NonInjectedJobs extends React.Component<Dependencies> { const {
render() { eventStore,
const { filterByNamespace,
eventStore, jobStore,
filterByNamespace, } = props;
jobStore,
} = this.props;
return ( return (
<SiblingsInTabLayout> <SiblingsInTabLayout>
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="workload_jobs" tableId="workload_jobs"
className="Jobs" className="Jobs"
store={jobStore} store={jobStore}
dependentStores={[eventStore]} // status icon component uses event store dependentStores={[eventStore]} // status icon component uses event store
sortingCallbacks={{ sortingCallbacks={{
[columnId.name]: job => job.getName(), [columnId.name]: job => job.getName(),
[columnId.namespace]: job => job.getNs(), [columnId.namespace]: job => job.getNs(),
[columnId.conditions]: job => job.getCondition()?.type, [columnId.conditions]: job => job.getCondition()?.type,
[columnId.age]: job => -job.getCreationTimestamp(), [columnId.age]: job => -job.getCreationTimestamp(),
}} }}
searchFilters={[ searchFilters={[
job => job.getSearchFields(), job => job.getSearchFields(),
]} ]}
renderHeaderTitle="Jobs" renderHeaderTitle="Jobs"
renderTableHeader={[ renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "Completions", className: "completions", id: columnId.completions }, { title: "Completions", className: "completions", id: columnId.completions },
{ className: "warning", showWithColumn: columnId.completions }, { className: "warning", showWithColumn: columnId.completions },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
{ title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions },
]} ]}
renderTableContents={job => { renderTableContents={job => {
const condition = job.getCondition(); const condition = job.getCondition();
return [ return [
job.getName(), job.getName(),
<a <a
key="namespace" key="namespace"
className="filterNamespace" className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(job.getNs()))} onClick={prevDefault(() => filterByNamespace(job.getNs()))}
> >
{job.getNs()} {job.getNs()}
</a>, </a>,
`${job.getCompletions()} / ${job.getDesiredCompletions()}`, `${job.getCompletions()} / ${job.getDesiredCompletions()}`,
<KubeObjectStatusIcon key="icon" object={job} />, <KubeObjectStatusIcon key="icon" object={job} />,
<KubeObjectAge key="age" object={job} />, <KubeObjectAge key="age" object={job} />,
condition && { condition && {
title: condition.type, title: condition.type,
className: kebabCase(condition.type), className: kebabCase(condition.type),
}, },
]; ];
}} }}
/> />
</SiblingsInTabLayout> </SiblingsInTabLayout>
); );
} });
}
export const Jobs = withInjectables<Dependencies>(NonInjectedJobs, { export const Jobs = withInjectables<Dependencies>(NonInjectedJobs, {
getProps: (di, props) => ({ 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 React from "react";
import kebabCase from "lodash/kebabCase"; import kebabCase from "lodash/kebabCase";
import { disposeOnUnmount, observer } from "mobx-react"; import { observer } from "mobx-react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { observable, reaction, makeObservable } from "mobx";
import { Pod } from "../../../common/k8s-api/endpoints"; import { Pod } from "../../../common/k8s-api/endpoints";
import type { NodeApi, PriorityClassApi, RuntimeClassApi, ServiceAccountApi } from "../../../common/k8s-api/endpoints"; import type { NodeApi, PriorityClassApi, RuntimeClassApi, ServiceAccountApi } from "../../../common/k8s-api/endpoints";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
@ -19,67 +18,39 @@ import { PodDetailsContainer } from "./pod-details-container";
import { PodDetailsAffinities } from "./pod-details-affinities"; import { PodDetailsAffinities } from "./pod-details-affinities";
import { PodDetailsTolerations } from "./pod-details-tolerations"; import { PodDetailsTolerations } from "./pod-details-tolerations";
import { PodDetailsSecrets } from "./pod-details-secrets"; import { PodDetailsSecrets } from "./pod-details-secrets";
import { ResourceMetrics } from "../resource-metrics";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { getItemMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; import { getItemMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
import { PodCharts, podMetricTabs } from "./pod-charts"; import type { Logger } from "../../../common/logger";
import { KubeObjectMeta } from "../kube-object-meta";
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import logger from "../../../common/logger";
import { PodVolumes } from "./details/volumes/view"; 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 { 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 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 getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable"; import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.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 serviceAccountApiInjectable from "../../../common/k8s-api/endpoints/service-account.api.injectable";
import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.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> { export interface PodDetailsProps extends KubeObjectDetailsProps<Pod> {
} }
interface Dependencies { interface Dependencies {
requestPodMetrics: RequestPodMetrics;
getActiveClusterEntity: GetActiveClusterEntity;
getDetailsUrl: GetDetailsUrl; getDetailsUrl: GetDetailsUrl;
nodeApi: NodeApi; nodeApi: NodeApi;
priorityClassApi: PriorityClassApi; priorityClassApi: PriorityClassApi;
runtimeClassApi: RuntimeClassApi; runtimeClassApi: RuntimeClassApi;
serviceAccountApi: ServiceAccountApi; serviceAccountApi: ServiceAccountApi;
logger: Logger;
containerMetrics: IAsyncComputed<PodMetricData>;
} }
@observer @observer
class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependencies> { 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() { render() {
const { object: pod, getActiveClusterEntity, getDetailsUrl, nodeApi } = this.props; const { object: pod, getDetailsUrl, nodeApi, logger, containerMetrics } = this.props;
if (!pod) { if (!pod) {
return null; return null;
@ -96,7 +67,6 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
const podIPs = pod.getIPs(); const podIPs = pod.getIPs();
const { nodeName } = spec ?? {}; const { nodeName } = spec ?? {};
const nodeSelector = pod.getNodeSelectors(); const nodeSelector = pod.getNodeSelectors();
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Pod);
const initContainers = pod.getInitContainers(); const initContainers = pod.getInitContainers();
const containers = pod.getContainers(); const containers = pod.getContainers();
@ -105,39 +75,26 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
const runtimeClassName = pod.getRuntimeClassName(); const runtimeClassName = pod.getRuntimeClassName();
const serviceAccountName = pod.getServiceAccountName(); const serviceAccountName = pod.getServiceAccountName();
const priorityClassDetailsUrl = getDetailsUrl(this.props.priorityClassApi.getUrl({ const priorityClassDetailsUrl = getDetailsUrl(this.props.priorityClassApi.formatUrlForNotListing({
name: priorityClassName, name: priorityClassName,
})); }));
const runtimeClassDetailsUrl = getDetailsUrl(this.props.runtimeClassApi.getUrl({ const runtimeClassDetailsUrl = getDetailsUrl(this.props.runtimeClassApi.formatUrlForNotListing({
name: runtimeClassName, name: runtimeClassName,
})); }));
const serviceAccountDetailsUrl = getDetailsUrl(this.props.serviceAccountApi.getUrl({ const serviceAccountDetailsUrl = getDetailsUrl(this.props.serviceAccountApi.formatUrlForNotListing({
name: serviceAccountName, name: serviceAccountName,
namespace, namespace,
})); }));
return ( return (
<div className="PodDetails"> <div className="PodDetails">
{!isMetricHidden && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={pod}
metrics={this.metrics}
>
<PodCharts />
</ResourceMetrics>
)}
<KubeObjectMeta object={pod} />
<DrawerItem name="Status"> <DrawerItem name="Status">
<span className={cssNames("status", kebabCase(pod.getStatusMessage()))}> <span className={cssNames("status", kebabCase(pod.getStatusMessage()))}>
{pod.getStatusMessage()} {pod.getStatusMessage()}
</span> </span>
</DrawerItem> </DrawerItem>
<DrawerItem name="Node" hidden={!nodeName}> <DrawerItem name="Node" hidden={!nodeName}>
<Link to={getDetailsUrl(nodeApi.getUrl({ name: nodeName }))}> <Link to={getDetailsUrl(nodeApi.formatUrlForNotListing({ name: nodeName }))}>
{nodeName} {nodeName}
</Link> </Link>
</DrawerItem> </DrawerItem>
@ -230,7 +187,7 @@ class NonInjectedPodDetails extends React.Component<PodDetailsProps & Dependenci
key={container.name} key={container.name}
pod={pod} pod={pod}
container={container} 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, { export const PodDetails = withInjectables<Dependencies, PodDetailsProps>(NonInjectedPodDetails, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
requestPodMetrics: di.inject(requestPodMetricsInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable),
nodeApi: di.inject(nodeApiInjectable), nodeApi: di.inject(nodeApiInjectable),
priorityClassApi: di.inject(priorityClassApiInjectable), priorityClassApi: di.inject(priorityClassApiInjectable),
runtimeClassApi: di.inject(runtimeClassApiInjectable), runtimeClassApi: di.inject(runtimeClassApiInjectable),
serviceAccountApi: di.inject(serviceAccountApiInjectable), 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 "./replicaset-details.scss";
import React from "react"; import React from "react";
import { makeObservable, observable, reaction } from "mobx";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; 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 { disposeOnUnmount, observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { ReplicaSet } from "../../../common/k8s-api/endpoints"; 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 { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger";
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; 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 podStoreInjectable from "../+workloads-pods/store.injectable";
import type { ReplicaSetStore } from "./store"; import type { ReplicaSetStore } from "./store";
import replicaSetStoreInjectable from "./store.injectable"; import replicaSetStoreInjectable from "./store.injectable";
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import loggerInjectable from "../../../common/logger.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";
export interface ReplicaSetDetailsProps extends KubeObjectDetailsProps<ReplicaSet> { export interface ReplicaSetDetailsProps extends KubeObjectDetailsProps<ReplicaSet> {
} }
@ -39,39 +31,20 @@ interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore; podStore: PodStore;
replicaSetStore: ReplicaSetStore; replicaSetStore: ReplicaSetStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestPodMetricsForReplicaSets: RequestPodMetricsForReplicaSets;
} }
@observer @observer
class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProps & Dependencies> { class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProps & Dependencies> {
@observable metrics: ReplicaSetPodMetricData | null = null;
constructor(props: ReplicaSetDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore, this.props.podStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: replicaSet, requestPodMetricsForReplicaSets } = this.props;
this.metrics = await requestPodMetricsForReplicaSets([replicaSet], replicaSet.getNs());
};
render() { render() {
const { object: replicaSet, podStore, replicaSetStore, getActiveClusterEntity } = this.props; const { object: replicaSet, replicaSetStore, logger } = this.props;
if (!replicaSet) { if (!replicaSet) {
return null; return null;
@ -88,21 +61,9 @@ class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProp
const nodeSelector = replicaSet.getNodeSelectors(); const nodeSelector = replicaSet.getNodeSelectors();
const images = replicaSet.getImages(); const images = replicaSet.getImages();
const childPods = replicaSetStore.getChildPods(replicaSet); const childPods = replicaSetStore.getChildPods(replicaSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.ReplicaSet);
return ( return (
<div className="ReplicaSetDetails"> <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 && ( {selectors.length > 0 && (
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{ {
@ -132,7 +93,6 @@ class NonInjectedReplicaSetDetails extends React.Component<ReplicaSetDetailsProp
<DrawerItem name="Pod Status" className="pod-status"> <DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/> <PodDetailsStatuses pods={childPods}/>
</DrawerItem> </DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={replicaSet}/> <PodDetailsList pods={childPods} owner={replicaSet}/>
</div> </div>
); );
@ -145,7 +105,6 @@ export const ReplicaSetDetails = withInjectables<Dependencies, ReplicaSetDetails
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
replicaSetStore: di.inject(replicaSetStoreInjectable), replicaSetStore: di.inject(replicaSetStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), logger: di.inject(loggerInjectable),
requestPodMetricsForReplicaSets: di.inject(requestPodMetricsForReplicaSetsInjectable),
}), }),
}); });

View File

@ -35,64 +35,61 @@ interface Dependencies {
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const NonInjectedReplicaSets = observer((props: Dependencies) => {
class NonInjectedReplicaSets extends React.Component<Dependencies> { const {
render() { eventStore,
const { filterByNamespace,
eventStore, replicaSetStore,
filterByNamespace, } = props;
replicaSetStore,
} = this.props;
return ( return (
<SiblingsInTabLayout> <SiblingsInTabLayout>
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="workload_replicasets" tableId="workload_replicasets"
className="ReplicaSets" className="ReplicaSets"
store={replicaSetStore} store={replicaSetStore}
dependentStores={[eventStore]} // status icon component uses event store dependentStores={[eventStore]} // status icon component uses event store
sortingCallbacks={{ sortingCallbacks={{
[columnId.name]: replicaSet => replicaSet.getName(), [columnId.name]: replicaSet => replicaSet.getName(),
[columnId.namespace]: replicaSet => replicaSet.getNs(), [columnId.namespace]: replicaSet => replicaSet.getNs(),
[columnId.desired]: replicaSet => replicaSet.getDesired(), [columnId.desired]: replicaSet => replicaSet.getDesired(),
[columnId.current]: replicaSet => replicaSet.getCurrent(), [columnId.current]: replicaSet => replicaSet.getCurrent(),
[columnId.ready]: replicaSet => replicaSet.getReady(), [columnId.ready]: replicaSet => replicaSet.getReady(),
[columnId.age]: replicaSet => -replicaSet.getCreationTimestamp(), [columnId.age]: replicaSet => -replicaSet.getCreationTimestamp(),
}} }}
searchFilters={[ searchFilters={[
replicaSet => replicaSet.getSearchFields(), replicaSet => replicaSet.getSearchFields(),
]} ]}
renderHeaderTitle="Replica Sets" renderHeaderTitle="Replica Sets"
renderTableHeader={[ renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ className: "warning", showWithColumn: columnId.name }, { className: "warning", showWithColumn: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "Desired", className: "desired", sortBy: columnId.desired, id: columnId.desired }, { title: "Desired", className: "desired", sortBy: columnId.desired, id: columnId.desired },
{ title: "Current", className: "current", sortBy: columnId.current, id: columnId.current }, { title: "Current", className: "current", sortBy: columnId.current, id: columnId.current },
{ title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]} ]}
renderTableContents={replicaSet => [ renderTableContents={replicaSet => [
replicaSet.getName(), replicaSet.getName(),
<KubeObjectStatusIcon key="icon" object={replicaSet} />, <KubeObjectStatusIcon key="icon" object={replicaSet} />,
<a <a
key="namespace" key="namespace"
className="filterNamespace" className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(replicaSet.getNs()))} onClick={prevDefault(() => filterByNamespace(replicaSet.getNs()))}
> >
{replicaSet.getNs()} {replicaSet.getNs()}
</a>, </a>,
replicaSet.getDesired(), replicaSet.getDesired(),
replicaSet.getCurrent(), replicaSet.getCurrent(),
replicaSet.getReady(), replicaSet.getReady(),
<KubeObjectAge key="age" object={replicaSet} />, <KubeObjectAge key="age" object={replicaSet} />,
]} ]}
/> />
</SiblingsInTabLayout> </SiblingsInTabLayout>
); );
} });
}
export const ReplicaSets = withInjectables<Dependencies>(NonInjectedReplicaSets, { export const ReplicaSets = withInjectables<Dependencies>(NonInjectedReplicaSets, {
getProps: (di, props) => ({ 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 type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
export interface ReplicaSetStoreDependencies { interface Dependencies {
getPodsByOwnerId: GetPodsByOwnerId; getPodsByOwnerId: GetPodsByOwnerId;
} }
export class ReplicaSetStore extends KubeObjectStore<ReplicaSet, ReplicaSetApi> { 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); 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 React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { makeObservable, observable, reaction } from "mobx";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; 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 { StatefulSetStore } from "./store";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { StatefulSet } from "../../../common/k8s-api/endpoints"; 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 { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object-meta"; import type { Logger } from "../../../common/logger";
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import type { PodStore } from "../+workloads-pods/store"; import type { PodStore } from "../+workloads-pods/store";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable";
import statefulSetStoreInjectable from "./store.injectable"; import statefulSetStoreInjectable from "./store.injectable";
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable"; import loggerInjectable from "../../../common/logger.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";
export interface StatefulSetDetailsProps extends KubeObjectDetailsProps<StatefulSet> { export interface StatefulSetDetailsProps extends KubeObjectDetailsProps<StatefulSet> {
} }
@ -40,39 +32,21 @@ interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore; podStore: PodStore;
statefulSetStore: StatefulSetStore; statefulSetStore: StatefulSetStore;
getActiveClusterEntity: GetActiveClusterEntity; logger: Logger;
requestPodMetricsForStatefulSets: RequestPodMetricsForStatefulSets;
} }
@observer @observer
class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsProps & Dependencies> { class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsProps & Dependencies> {
@observable metrics: StatefulSetPodMetricData | null = null;
constructor(props: StatefulSetDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
this.metrics = null;
}),
this.props.subscribeStores([ this.props.subscribeStores([
this.props.podStore, this.props.podStore,
]), ]),
]); ]);
} }
loadMetrics = async () => {
const { object: statefulSet, requestPodMetricsForStatefulSets } = this.props;
this.metrics = await requestPodMetricsForStatefulSets([statefulSet], statefulSet.getNs());
};
render() { render() {
const { object: statefulSet, podStore, statefulSetStore, getActiveClusterEntity } = this.props; const { object: statefulSet, statefulSetStore, logger } = this.props;
if (!statefulSet) { if (!statefulSet) {
return null; return null;
@ -88,21 +62,9 @@ class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsPr
const selectors = statefulSet.getSelectors(); const selectors = statefulSet.getSelectors();
const nodeSelector = statefulSet.getNodeSelectors(); const nodeSelector = statefulSet.getNodeSelectors();
const childPods = statefulSetStore.getChildPods(statefulSet); const childPods = statefulSetStore.getChildPods(statefulSet);
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.StatefulSet);
return ( return (
<div className="StatefulSetDetails"> <div className="StatefulSetDetails">
{!isMetricHidden && podStore.isLoaded && (
<ResourceMetrics
loader={this.loadMetrics}
tabs={podMetricTabs}
object={statefulSet}
metrics={this.metrics}
>
<PodCharts/>
</ResourceMetrics>
)}
<KubeObjectMeta object={statefulSet}/>
{selectors.length && ( {selectors.length && (
<DrawerItem name="Selector" labelsOnly> <DrawerItem name="Selector" labelsOnly>
{ {
@ -131,7 +93,6 @@ class NonInjectedStatefulSetDetails extends React.Component<StatefulSetDetailsPr
<DrawerItem name="Pod Status" className="pod-status"> <DrawerItem name="Pod Status" className="pod-status">
<PodDetailsStatuses pods={childPods}/> <PodDetailsStatuses pods={childPods}/>
</DrawerItem> </DrawerItem>
<ResourceMetricsText metrics={this.metrics}/>
<PodDetailsList pods={childPods} owner={statefulSet}/> <PodDetailsList pods={childPods} owner={statefulSet}/>
</div> </div>
); );
@ -144,8 +105,7 @@ export const StatefulSetDetails = withInjectables<Dependencies, StatefulSetDetai
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
statefulSetStore: di.inject(statefulSetStoreInjectable), statefulSetStore: di.inject(statefulSetStoreInjectable),
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), logger: di.inject(loggerInjectable),
requestPodMetricsForStatefulSets: di.inject(requestPodMetricsForStatefulSetsInjectable),
}), }),
}); });

View File

@ -13,13 +13,11 @@ import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age"; import { KubeObjectAge } from "../kube-object/age";
import type { StatefulSetStore } from "./store"; import type { StatefulSetStore } from "./store";
import type { PodStore } from "../+workloads-pods/store";
import type { EventStore } from "../+events/store"; import type { EventStore } from "../+events/store";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import eventStoreInjectable from "../+events/store.injectable"; import eventStoreInjectable from "../+events/store.injectable";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.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 statefulSetStoreInjectable from "./store.injectable";
import { prevDefault } from "../../utils"; import { prevDefault } from "../../utils";
@ -33,79 +31,73 @@ enum columnId {
interface Dependencies { interface Dependencies {
statefulSetStore: StatefulSetStore; statefulSetStore: StatefulSetStore;
podStore: PodStore;
eventStore: EventStore; eventStore: EventStore;
filterByNamespace: FilterByNamespace; filterByNamespace: FilterByNamespace;
} }
@observer const renderPodCounts = (statefulSet: StatefulSet) => {
class NonInjectedStatefulSets extends React.Component<Dependencies> { const { readyReplicas, currentReplicas } = statefulSet.status ?? {};
renderPods(statefulSet: StatefulSet) {
const { readyReplicas, currentReplicas } = statefulSet.status ?? {};
return `${readyReplicas || 0}/${currentReplicas || 0}`; return `${readyReplicas || 0}/${currentReplicas || 0}`;
} };
render() { const NonInjectedStatefulSets = observer((props: Dependencies) => {
const { const {
eventStore, eventStore,
filterByNamespace, filterByNamespace,
podStore, statefulSetStore,
statefulSetStore, } = props;
} = this.props;
return ( return (
<SiblingsInTabLayout> <SiblingsInTabLayout>
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="workload_statefulsets" tableId="workload_statefulsets"
className="StatefulSets" className="StatefulSets"
store={statefulSetStore} store={statefulSetStore}
dependentStores={[podStore, eventStore]} // status icon component uses event store, details component uses podStore dependentStores={[eventStore]}
sortingCallbacks={{ sortingCallbacks={{
[columnId.name]: statefulSet => statefulSet.getName(), [columnId.name]: statefulSet => statefulSet.getName(),
[columnId.namespace]: statefulSet => statefulSet.getNs(), [columnId.namespace]: statefulSet => statefulSet.getNs(),
[columnId.age]: statefulSet => -statefulSet.getCreationTimestamp(), [columnId.age]: statefulSet => -statefulSet.getCreationTimestamp(),
[columnId.replicas]: statefulSet => statefulSet.getReplicas(), [columnId.replicas]: statefulSet => statefulSet.getReplicas(),
}} }}
searchFilters={[ searchFilters={[
statefulSet => statefulSet.getSearchFields(), statefulSet => statefulSet.getSearchFields(),
]} ]}
renderHeaderTitle="Stateful Sets" renderHeaderTitle="Stateful Sets"
renderTableHeader={[ renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
{ title: "Pods", className: "pods", id: columnId.pods }, { title: "Pods", className: "pods", id: columnId.pods },
{ title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas },
{ className: "warning", showWithColumn: columnId.replicas }, { className: "warning", showWithColumn: columnId.replicas },
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]} ]}
renderTableContents={statefulSet => [ renderTableContents={statefulSet => [
statefulSet.getName(), statefulSet.getName(),
<a <a
key="namespace" key="namespace"
className="filterNamespace" className="filterNamespace"
onClick={prevDefault(() => filterByNamespace(statefulSet.getNs()))} onClick={prevDefault(() => filterByNamespace(statefulSet.getNs()))}
> >
{statefulSet.getNs()} {statefulSet.getNs()}
</a>, </a>,
this.renderPods(statefulSet), renderPodCounts(statefulSet),
statefulSet.getReplicas(), statefulSet.getReplicas(),
<KubeObjectStatusIcon key="icon" object={statefulSet} />, <KubeObjectStatusIcon key="icon" object={statefulSet} />,
<KubeObjectAge key="age" object={statefulSet} />, <KubeObjectAge key="age" object={statefulSet} />,
]} ]}
/> />
</SiblingsInTabLayout> </SiblingsInTabLayout>
); );
} });
}
export const StatefulSets = withInjectables<Dependencies>(NonInjectedStatefulSets, { export const StatefulSets = withInjectables<Dependencies>(NonInjectedStatefulSets, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
eventStore: di.inject(eventStoreInjectable), eventStore: di.inject(eventStoreInjectable),
filterByNamespace: di.inject(filterByNamespaceInjectable), filterByNamespace: di.inject(filterByNamespaceInjectable),
podStore: di.inject(podStoreInjectable),
statefulSetStore: di.inject(statefulSetStoreInjectable), statefulSetStore: di.inject(statefulSetStoreInjectable),
}), }),
}); });

View File

@ -3,10 +3,15 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable"; import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.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({ const currentKubeObjectInDetailsInjectable = getInjectable({
id: "current-kube-object-in-details", id: "current-kube-object-in-details",
@ -14,20 +19,24 @@ const currentKubeObjectInDetailsInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const urlParam = di.inject(kubeDetailsUrlParamInjectable); const urlParam = di.inject(kubeDetailsUrlParamInjectable);
const apiManager = di.inject(apiManagerInjectable); const apiManager = di.inject(apiManagerInjectable);
const logger = di.inject(loggerInjectable);
return computed(() => { return asyncComputed({
const path = urlParam.get(); getValueFromObservedPromise: async (): Promise<CurrentKubeObject> => {
const path = urlParam.get();
const store = apiManager.getStore(path);
try { if (!store) {
return apiManager.getStore(path)?.getByPath(path); return undefined;
} catch (error) { }
logger.error(
`[KUBE-OBJECT-DETAILS]: failed to get store or object ${path}: ${error}`,
);
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 { return {
Component: ClusterRoleBindingDetails, Component: ClusterRoleBindingDetails,
enabled: computed(() => isClusterRoleBinding(kubeObject.get())), enabled: computed(() => isClusterRoleBinding(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,77 +6,15 @@ import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token"; import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token";
import { KubeEventDetails } from "../../../+events/kube-event-details"; 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({ const kubeEventDetailItemInjectable = getInjectable({
id: "kube-event-detail-item", id: "kube-event-detail-item",
instantiate: (di) => { instantiate: () => ({
const currentKubeObjectInDetails = di.inject( Component: KubeEventDetails,
currentKubeObjectInDetailsInjectable, enabled: computed(() => true),
); orderNumber: Infinity,
}),
return {
Component: KubeEventDetails,
enabled: computed(() => {
const kubeObject = currentKubeObjectInDetails.get();
if (!kubeObject) {
return false;
}
const predicates = [
isClusterRole,
isClusterRoleBinding,
isCronJob,
isDaemonSet,
isDeployment,
isEndpoint,
isHorizontalPodAutoscaler,
isIngress,
isJob,
isNetworkPolicy,
isNode,
isPersistentVolume,
isPersistentVolumeClaim,
isPod,
isReplicaSet,
isRole,
isRoleBinding,
isService,
isServiceAccount,
isStatefulSet,
isStorageClass,
];
return predicates.some((predicate) => predicate(kubeObject));
}),
orderNumber: 355,
};
},
injectionToken: kubeObjectDetailItemInjectionToken, injectionToken: kubeObjectDetailItemInjectionToken,
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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