diff --git a/packages/core/src/common/certificate-authorities/request-system-cas.injectable.win32.ts b/packages/core/src/common/certificate-authorities/request-system-cas.injectable.win32.ts index b4bcb51733..4940aa2a7b 100644 --- a/packages/core/src/common/certificate-authorities/request-system-cas.injectable.win32.ts +++ b/packages/core/src/common/certificate-authorities/request-system-cas.injectable.win32.ts @@ -3,7 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import * as path from "path"; import execFileInjectable from "../fs/exec-file.injectable"; import loggerInjectable from "../logger.injectable"; import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; @@ -24,7 +23,7 @@ const pemEncoding = (hexEncodedCert: String) => { const requestSystemCAsInjectable = getInjectable({ id: "request-system-cas", instantiate: (di) => { - const wincaRootsExePath: string = path.resolve(require.resolve("win-ca"), "..", "roots.exe"); + const wincaRootsExePath: string = __non_webpack_require__.resolve("win-ca/lib/roots.exe"); const execFile = di.inject(execFileInjectable); const logger = di.inject(loggerInjectable); diff --git a/packages/core/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts b/packages/core/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts index ee4fcfc63d..ba5483da2c 100644 --- a/packages/core/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts +++ b/packages/core/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; +import type { OptionVarient } from "../../utils"; import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; import { KubeApi } from "../kube-api"; -import type { OptionVarient } from "../../utils"; +import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; +import { KubeObject } from "../kube-object"; export enum HpaMetricType { Resource = "Resource", @@ -17,46 +17,131 @@ export enum HpaMetricType { ContainerResource = "ContainerResource", } +export interface MetricCurrentTarget { + current?: string; + target?: string; +} + export interface HorizontalPodAutoscalerMetricTarget { kind: string; name: string; apiVersion: string; } -export interface ContainerResourceMetricSource { +export interface V2ContainerResourceMetricSource { + container: string; + name: string; + target?: { + averageUtilization?: number; + averageValue?: string; + type?: string; + }; +} + +export interface V2Beta1ContainerResourceMetricSource { container: string; name: string; targetAverageUtilization?: number; targetAverageValue?: string; } -export interface ExternalMetricSource { - metricName: string; +export type ContainerResourceMetricSource = + | V2ContainerResourceMetricSource + | V2Beta1ContainerResourceMetricSource; + +export interface V2ExternalMetricSource { + metricName?: string; + metricSelector?: LabelSelector; + metric?: { + name?: string; + selector?: LabelSelector; + }; + target?: { + type: string; + value?: string; + averageValue?: string; + }; +} + +export interface V2Beta1ExternalMetricSource { + metricName?: string; metricSelector?: LabelSelector; targetAverageValue?: string; targetValue?: string; + metric?: { + selector?: LabelSelector; + }; } -export interface ObjectMetricSource { +export type ExternalMetricSource = + | V2Beta1ExternalMetricSource + | V2ExternalMetricSource; + +export interface V2ObjectMetricSource { + metric?: { + name?: string; + selector?: LabelSelector; + }; + target?: { + type?: string; + value?: string; + averageValue?: string; + }; + describedObject?: CrossVersionObjectReference; +} + +export interface V2Beta1ObjectMetricSource { averageValue?: string; - metricName: string; + metricName?: string; selector?: LabelSelector; - target: CrossVersionObjectReference; - targetValue: string; + targetValue?: string; + describedObject?: CrossVersionObjectReference; } -export interface PodsMetricSource { - metricName: string; - selector?: LabelSelector; - targetAverageValue: string; +export type ObjectMetricSource = + | V2ObjectMetricSource + | V2Beta1ObjectMetricSource; + +export interface V2PodsMetricSource { + metric?: { + name?: string; + selector?: LabelSelector; + }; + target?: { + averageValue?: string; + type?: string; + }; } -export interface ResourceMetricSource { +export interface V2Beta1PodsMetricSource { + metricName?: string; + selector?: LabelSelector; + targetAverageValue?: string; +} + +export type PodsMetricSource = + | V2PodsMetricSource + | V2Beta1PodsMetricSource; + +export interface V2ResourceMetricSource { + name: string; + target?: { + averageUtilization?: number; + averageValue?: string; + type?: string; + }; +} + +export interface V2Beta1ResourceMetricSource { name: string; targetAverageUtilization?: number; targetAverageValue?: string; } +export type ResourceMetricSource = + | V2ResourceMetricSource + | V2Beta1ResourceMetricSource; + export interface BaseHorizontalPodAutoscalerMetricSpec { containerResource: ContainerResourceMetricSource; external: ExternalMetricSource; @@ -93,40 +178,112 @@ interface HPAScalingPolicy { type HPAScalingPolicyType = string; -export interface ContainerResourceMetricStatus { - container: string; +export interface V2ContainerResourceMetricStatus { + container?: string; + name: string; + current?: { + averageUtilization?: number; + averageValue?: string; + }; +} + +export interface V2Beta1ContainerResourceMetricStatus { + container?: string; currentAverageUtilization?: number; - currentAverageValue: string; + currentAverageValue?: string; name: string; } -export interface ExternalMetricStatus { +export type ContainerResourceMetricStatus = + | V2ContainerResourceMetricStatus + | V2Beta1ContainerResourceMetricStatus; + +export interface V2ExternalMetricStatus { + metric?: { + name?: string; + selector?: LabelSelector; + }; + current?: { + averageValue?: string; + value?: string; + }; +} + +export interface V2Beta1ExternalMetricStatus { currentAverageValue?: string; - currentValue: string; - metricName: string; + currentValue?: string; + metricName?: string; metricSelector?: LabelSelector; } -export interface ObjectMetricStatus { +export type ExternalMetricStatus = + | V2Beta1ExternalMetricStatus + | V2ExternalMetricStatus; + +export interface V2ObjectMetricStatus { + metric?: { + name?: string; + selector?: LabelSelector; + }; + current?: { + type?: string; + value?: string; + averageValue?: string; + }; + describedObject?: CrossVersionObjectReference; +} + +export interface V2Beta1ObjectMetricStatus { averageValue?: string; currentValue?: string; - metricName: string; + metricName?: string; selector?: LabelSelector; - target: CrossVersionObjectReference; + describedObject?: CrossVersionObjectReference; } -export interface PodsMetricStatus { - currentAverageValue: string; - metricName: string; +export type ObjectMetricStatus = + | V2Beta1ObjectMetricStatus + | V2ObjectMetricStatus; + +export interface V2PodsMetricStatus { + selector?: LabelSelector; + metric?: { + name?: string; + selector?: LabelSelector; + }; + current?: { + averageValue?: string; + }; +} + +export interface V2Beta1PodsMetricStatus { + currentAverageValue?: string; + metricName?: string; selector?: LabelSelector; } -export interface ResourceMetricStatus { +export type PodsMetricStatus = + | V2Beta1PodsMetricStatus + | V2PodsMetricStatus; + +export interface V2ResourceMetricStatus { + name: string; + current?: { + averageUtilization?: number; + averageValue?: string; + }; +} + +export interface V2Beta1ResourceMetricStatus { currentAverageUtilization?: number; - currentAverageValue: string; + currentAverageValue?: string; name: string; } +export type ResourceMetricStatus = + | V2Beta1ResourceMetricStatus + | V2ResourceMetricStatus; + export interface BaseHorizontalPodAutoscalerMetricStatus { containerResource: ContainerResourceMetricStatus; external: ExternalMetricStatus; @@ -154,6 +311,7 @@ export interface HorizontalPodAutoscalerSpec { maxReplicas: number; metrics?: HorizontalPodAutoscalerMetricSpec[]; behavior?: HorizontalPodAutoscalerBehavior; + targetCPUUtilizationPercentage?: number; // used only in autoscaling/v1 } export interface HorizontalPodAutoscalerStatus { @@ -161,11 +319,7 @@ export interface HorizontalPodAutoscalerStatus { currentReplicas: number; desiredReplicas: number; currentMetrics?: HorizontalPodAutoscalerMetricStatus[]; -} - -interface MetricCurrentTarget { - current?: string; - target?: string; + currentCPUUtilizationPercentage?: number; // used only in autoscaling/v1 } export class HorizontalPodAutoscaler extends KubeObject< @@ -212,15 +366,6 @@ export class HorizontalPodAutoscaler extends KubeObject< getCurrentMetrics() { return this.status?.currentMetrics ?? []; } - - getMetricValues(metric: HorizontalPodAutoscalerMetricSpec): string { - const { - current = "unknown", - target = "unknown", - } = getMetricCurrentTarget(metric, this.getCurrentMetrics()); - - return `${current} / ${target}`; - } } export class HorizontalPodAutoscalerApi extends KubeApi { @@ -229,114 +374,6 @@ export class HorizontalPodAutoscalerApi extends KubeApi ...opts ?? {}, objectConstructor: HorizontalPodAutoscaler, checkPreferredVersion: true, - // Kubernetes < 1.26 - fallbackApiBases: [ - "/apis/autoscaling/v2beta2/horizontalpodautoscalers", - "/apis/autoscaling/v2beta1/horizontalpodautoscalers", - "/apis/autoscaling/v1/horizontalpodautoscalers", - ], }); } } - -function getMetricName(metric: HorizontalPodAutoscalerMetricSpec | HorizontalPodAutoscalerMetricStatus): string | undefined { - switch (metric.type) { - case HpaMetricType.Resource: - return metric.resource.name; - case HpaMetricType.Pods: - return metric.pods.metricName; - case HpaMetricType.Object: - return metric.object.metricName; - case HpaMetricType.External: - return metric.external.metricName; - case HpaMetricType.ContainerResource: - return metric.containerResource.name; - default: - return undefined; - } -} - -function getResourceMetricValue(currentMetric: ResourceMetricStatus | undefined, targetMetric: ResourceMetricSource): MetricCurrentTarget { - return { - current: ( - typeof currentMetric?.currentAverageUtilization === "number" - ? `${currentMetric.currentAverageUtilization}%` - : currentMetric?.currentAverageValue - ), - target: ( - typeof targetMetric?.targetAverageUtilization === "number" - ? `${targetMetric.targetAverageUtilization}%` - : targetMetric?.targetAverageValue - ), - }; -} - -function getPodsMetricValue(currentMetric: PodsMetricStatus | undefined, targetMetric: PodsMetricSource): MetricCurrentTarget { - return { - current: currentMetric?.currentAverageValue, - target: targetMetric?.targetAverageValue, - }; -} - -function getObjectMetricValue(currentMetric: ObjectMetricStatus | undefined, targetMetric: ObjectMetricSource): MetricCurrentTarget { - return { - current: ( - currentMetric?.currentValue - ?? currentMetric?.averageValue - ), - target: ( - targetMetric?.targetValue - ?? targetMetric?.averageValue - ), - }; -} - -function getExternalMetricValue(currentMetric: ExternalMetricStatus | undefined, targetMetric: ExternalMetricSource): MetricCurrentTarget { - return { - current: ( - currentMetric?.currentValue - ?? currentMetric?.currentAverageValue - ), - target: ( - targetMetric?.targetValue - ?? targetMetric?.targetAverageValue - ), - }; -} - -function getContainerResourceMetricValue(currentMetric: ContainerResourceMetricStatus | undefined, targetMetric: ContainerResourceMetricSource): MetricCurrentTarget { - return { - current: ( - typeof currentMetric?.currentAverageUtilization === "number" - ? `${currentMetric.currentAverageUtilization}%` - : currentMetric?.currentAverageValue - ), - target: ( - typeof targetMetric?.targetAverageUtilization === "number" - ? `${targetMetric.targetAverageUtilization}%` - : targetMetric?.targetAverageValue - ), - }; -} - -function getMetricCurrentTarget(spec: HorizontalPodAutoscalerMetricSpec, status: HorizontalPodAutoscalerMetricStatus[]): MetricCurrentTarget { - const currentMetric = status.find(m => ( - m.type === spec.type - && getMetricName(m) === getMetricName(spec) - )); - - switch (spec.type) { - case HpaMetricType.Resource: - return getResourceMetricValue(currentMetric?.resource, spec.resource); - case HpaMetricType.Pods: - return getPodsMetricValue(currentMetric?.pods, spec.pods); - case HpaMetricType.Object: - return getObjectMetricValue(currentMetric?.object, spec.object); - case HpaMetricType.External: - return getExternalMetricValue(currentMetric?.external, spec.external); - case HpaMetricType.ContainerResource: - return getContainerResourceMetricValue(currentMetric?.containerResource, spec.containerResource); - default: - return {}; - } -} diff --git a/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts b/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts similarity index 100% rename from src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts rename to packages/core/src/renderer/components/+config-autoscalers/get-hpa-metric-name.ts diff --git a/src/renderer/components/+config-autoscalers/get-hpa-metrics.injectable.ts b/packages/core/src/renderer/components/+config-autoscalers/get-hpa-metrics.injectable.ts similarity index 100% rename from src/renderer/components/+config-autoscalers/get-hpa-metrics.injectable.ts rename to packages/core/src/renderer/components/+config-autoscalers/get-hpa-metrics.injectable.ts diff --git a/src/renderer/components/+config-autoscalers/horizontal-pod-autoscaler-metrics.test.ts b/packages/core/src/renderer/components/+config-autoscalers/horizontal-pod-autoscaler-metrics.test.ts similarity index 100% rename from src/renderer/components/+config-autoscalers/horizontal-pod-autoscaler-metrics.test.ts rename to packages/core/src/renderer/components/+config-autoscalers/horizontal-pod-autoscaler-metrics.test.ts diff --git a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.scss b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.scss index e10e14220f..571d988b11 100644 --- a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.scss +++ b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.scss @@ -11,9 +11,19 @@ } .metrics .Table { + margin: 0 (-$margin * 3); + .TableCell { word-break: break-word; + &:first-child { + margin-left: $margin * 2; + } + + &:last-child { + margin-right: $margin * 2; + } + &.name { flex-grow: 2; } diff --git a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx index 4bd5f12a66..96c1a7e20b 100644 --- a/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/packages/core/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -22,6 +22,8 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; +import { getMetricName } from "./get-hpa-metric-name"; export interface HpaDetailsProps extends KubeObjectDetailsProps { } @@ -30,6 +32,7 @@ interface Dependencies { apiManager: ApiManager; logger: Logger; getDetailsUrl: GetDetailsUrl; + getMetrics: (hpa: HorizontalPodAutoscaler) => string[]; } @observer @@ -57,47 +60,46 @@ class NonInjectedHpaDetails extends React.Component { + const metricName = getMetricName(metric); + switch (metric.type) { case HpaMetricType.ContainerResource: // fallthrough case HpaMetricType.Resource: { const metricSpec = metric.resource ?? metric.containerResource; - const addition = metricSpec.targetAverageUtilization - ? " (as a percentage of request)" - : ""; - return `Resource ${metricSpec.name} on Pods${addition}`; + return `Resource ${metricSpec.name} on Pods`; } case HpaMetricType.Pods: - return `${metric.pods.metricName} on Pods`; + return `${metricName} on Pods`; case HpaMetricType.Object: { return ( <> - {metric.object.metricName} + {metricName} {" "} - {this.renderTargetLink(metric.object.target)} + {this.renderTargetLink(metric.object?.describedObject)} ); } case HpaMetricType.External: - return `${metric.external.metricName} on ${JSON.stringify(metric.external.metricSelector)}`; + return `${metricName} on ${JSON.stringify(metric.external.metricSelector ?? metric.external.metric?.selector)}`; } }; return ( - + Name Current / Target { - hpa.getMetrics() - .map((metric, index) => ( + this.props.getMetrics(hpa) + .map((metrics, index) => ( - {renderName(metric)} - {hpa.getMetricValues(metric)} + {renderName(hpa.getMetrics()[index])} + {metrics} )) } @@ -175,5 +177,6 @@ export const HpaDetails = withInjectables(NonInje apiManager: di.inject(apiManagerInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), logger: di.inject(loggerInjectable), + getMetrics: di.inject(getHorizontalPodAutoscalerMetrics), }), }); diff --git a/src/renderer/components/+config-autoscalers/hpa-v1-metric-parser.ts b/packages/core/src/renderer/components/+config-autoscalers/hpa-v1-metric-parser.ts similarity index 100% rename from src/renderer/components/+config-autoscalers/hpa-v1-metric-parser.ts rename to packages/core/src/renderer/components/+config-autoscalers/hpa-v1-metric-parser.ts diff --git a/src/renderer/components/+config-autoscalers/hpa-v2-metric-parser.ts b/packages/core/src/renderer/components/+config-autoscalers/hpa-v2-metric-parser.ts similarity index 100% rename from src/renderer/components/+config-autoscalers/hpa-v2-metric-parser.ts rename to packages/core/src/renderer/components/+config-autoscalers/hpa-v2-metric-parser.ts diff --git a/packages/core/src/renderer/components/+config-autoscalers/hpa.tsx b/packages/core/src/renderer/components/+config-autoscalers/hpa.tsx index efdc7fd48d..e7a456b3f1 100644 --- a/packages/core/src/renderer/components/+config-autoscalers/hpa.tsx +++ b/packages/core/src/renderer/components/+config-autoscalers/hpa.tsx @@ -17,6 +17,7 @@ import { KubeObjectAge } from "../kube-object/age"; import type { HorizontalPodAutoscalerStore } from "./store"; import { withInjectables } from "@ogre-tools/injectable-react"; import horizontalPodAutoscalerStoreInjectable from "./store.injectable"; +import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; enum columnId { @@ -32,6 +33,7 @@ enum columnId { interface Dependencies { horizontalPodAutoscalerStore: HorizontalPodAutoscalerStore; + getMetrics: (hpa: HorizontalPodAutoscaler) => string[]; } @observer @@ -39,7 +41,7 @@ class NonInjectedHorizontalPodAutoscalers extends React.Component getTargets(hpa: HorizontalPodAutoscaler) { const metrics = hpa.getMetrics(); - if (metrics.length === 0) { + if (metrics.length === 0 && !hpa.spec?.targetCPUUtilizationPercentage) { return

--

; } @@ -47,7 +49,7 @@ class NonInjectedHorizontalPodAutoscalers extends React.Component return (

- {hpa.getMetricValues(metrics[0])} + {this.props.getMetrics(hpa)[0]} {" "} {metricsRemain}

@@ -120,5 +122,6 @@ export const HorizontalPodAutoscalers = withInjectables(NonInjecte getProps: (di, props) => ({ ...props, horizontalPodAutoscalerStore: di.inject(horizontalPodAutoscalerStoreInjectable), + getMetrics: di.inject(getHorizontalPodAutoscalerMetrics), }), }); diff --git a/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts index 66dbebe7c5..dbf04d52d6 100644 --- a/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts +++ b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts @@ -27,7 +27,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({ export const isHorizontalPodAutoscaler = kubeObjectMatchesToKindAndApiVersion( "HorizontalPodAutoscaler", - ["autoscaling/v2beta1"], + ["autoscaling/v2", "autoscaling/v2beta2", "autoscaling/v2beta1", "autoscaling/v1"], ); export default horizontalPodAutoscalerDetailItemInjectable; diff --git a/src/common/.gitkeep b/src/common/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/common/__tests__/catalog-category-registry.test.ts b/src/common/__tests__/catalog-category-registry.test.ts deleted file mode 100644 index 7da9e1c7c7..0000000000 --- a/src/common/__tests__/catalog-category-registry.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CatalogCategorySpec } from "../catalog"; -import { CatalogCategory, CatalogCategoryRegistry } from "../catalog"; - -class TestCatalogCategoryRegistry extends CatalogCategoryRegistry { } - -class TestCatalogCategory extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - public metadata = { - name: "Test Category", - icon: "", - }; - public spec: CatalogCategorySpec = { - group: "entity.k8slens.dev", - versions: [], - names: { - kind: "Test", - }, - }; -} - -class TestCatalogCategory2 extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - public metadata = { - name: "Test Category 2", - icon: "", - }; - public spec: CatalogCategorySpec = { - group: "entity.k8slens.dev", - versions: [], - names: { - kind: "Test2", - }, - }; -} - -describe("CatalogCategoryRegistry", () => { - it("should remove only the category registered when running the disposer", () => { - const registry = new TestCatalogCategoryRegistry(); - - expect(registry.items.length).toBe(0); - - const d1 = registry.add(new TestCatalogCategory()); - const d2 = registry.add(new TestCatalogCategory2()); - - expect(registry.items.length).toBe(2); - - d1(); - expect(registry.items.length).toBe(1); - - d2(); - expect(registry.items.length).toBe(0); - }); - - it("doesn't return items that are filtered out", () => { - const registry = new TestCatalogCategoryRegistry(); - - registry.add(new TestCatalogCategory()); - registry.add(new TestCatalogCategory2()); - - expect(registry.items.length).toBe(2); - expect(registry.filteredItems.length).toBe(2); - - const disposer = registry.addCatalogCategoryFilter(category => category.metadata.name === "Test Category"); - - expect(registry.items.length).toBe(2); - expect(registry.filteredItems.length).toBe(1); - - const disposer2 = registry.addCatalogCategoryFilter(category => category.metadata.name === "foo"); - - expect(registry.items.length).toBe(2); - expect(registry.filteredItems.length).toBe(0); - - disposer(); - - expect(registry.items.length).toBe(2); - expect(registry.filteredItems.length).toBe(0); - - disposer2(); - - expect(registry.items.length).toBe(2); - expect(registry.filteredItems.length).toBe(2); - }); -}); diff --git a/src/common/__tests__/catalog-entity.test.tsx b/src/common/__tests__/catalog-entity.test.tsx deleted file mode 100644 index d0700b53d3..0000000000 --- a/src/common/__tests__/catalog-entity.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { CatalogCategory } from "../catalog"; -import type { CatalogCategorySpec } from "../catalog"; - -class TestCatalogCategoryWithoutBadge extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - - public metadata = { - name: "Test Category", - icon: "", - }; - - public spec: CatalogCategorySpec = { - group: "entity.k8slens.dev", - versions: [], - names: { - kind: "Test", - }, - }; -} - -class TestCatalogCategoryWithBadge extends TestCatalogCategoryWithoutBadge { - getBadge() { - return (
Test Badge
); - } -} - -describe("CatalogCategory", () => { - it("returns name", () => { - const category = new TestCatalogCategoryWithoutBadge(); - - expect(category.getName()).toEqual("Test Category"); - }); - - it("doesn't return badge by default", () => { - const category = new TestCatalogCategoryWithoutBadge(); - - expect(category.getBadge()).toEqual(null); - }); - - it("returns a badge", () => { - const category = new TestCatalogCategoryWithBadge(); - - expect(category.getBadge()).toBeTruthy(); - }); -}); diff --git a/src/common/__tests__/cluster-store.test.ts b/src/common/__tests__/cluster-store.test.ts deleted file mode 100644 index 9dd1a29a5b..0000000000 --- a/src/common/__tests__/cluster-store.test.ts +++ /dev/null @@ -1,359 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterStore } from "../cluster-store/cluster-store"; -import type { GetCustomKubeConfigFilePath } from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; -import getCustomKubeConfigFilePathInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; -import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; -import type { DiContainer } from "@ogre-tools/injectable"; -import type { CreateCluster } from "../cluster/create-cluster-injection-token"; -import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import assert from "assert"; -import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable"; -import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable"; -import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable"; -import normalizedPlatformInjectable from "../vars/normalized-platform.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import type { WriteJsonSync } from "../fs/write-json-sync.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; -import type { ReadFileSync } from "../fs/read-file-sync.injectable"; -import readFileSyncInjectable from "../fs/read-file-sync.injectable"; -import { readFileSync } from "fs"; -import type { WriteFileSync } from "../fs/write-file-sync.injectable"; -import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; -import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable"; -import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable"; - -// NOTE: this is intended to read the actual file system -const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png"); -const clusterServerUrl = "https://localhost"; -const kubeconfig = ` -apiVersion: v1 -clusters: -- cluster: - server: ${clusterServerUrl} - name: test -contexts: -- context: - cluster: test - user: test - name: foo -- context: - cluster: test - user: test - name: foo2 -current-context: test -kind: Config -preferences: {} -users: -- name: test - user: - token: kubeconfig-user-q4lm4:xxxyyyy -`; - -describe("cluster-store", () => { - let di: DiContainer; - let clusterStore: ClusterStore; - let createCluster: CreateCluster; - let writeJsonSync: WriteJsonSync; - let writeFileSync: WriteFileSync; - let writeBufferSync: WriteBufferSync; - let readFileSync: ReadFileSync; - let getCustomKubeConfigFilePath: GetCustomKubeConfigFilePath; - let writeFileSyncAndReturnPath: (filePath: string, contents: string) => string; - - beforeEach(async () => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - di.override(directoryForTempInjectable, () => "/some-temp-directory"); - di.override(kubectlBinaryNameInjectable, () => "kubectl"); - di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); - di.override(normalizedPlatformInjectable, () => "darwin"); - createCluster = di.inject(createClusterInjectionToken); - getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); - writeJsonSync = di.inject(writeJsonSyncInjectable); - writeFileSync = di.inject(writeFileSyncInjectable); - writeBufferSync = di.inject(writeBufferSyncInjectable); - readFileSync = di.inject(readFileSyncInjectable); - writeFileSyncAndReturnPath = (filePath, contents) => (writeFileSync(filePath, contents), filePath); - }); - - describe("empty config", () => { - beforeEach(async () => { - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {}); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); - }); - - describe("with foo cluster added", () => { - beforeEach(() => { - const cluster = createCluster({ - id: "foo", - contextName: "foo", - preferences: { - terminalCWD: "/some-directory-for-user-data", - icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", - clusterName: "minikube", - }, - kubeConfigPath: writeFileSyncAndReturnPath( - getCustomKubeConfigFilePath("foo"), - kubeconfig, - ), - }, { - clusterServerUrl, - }); - - clusterStore.addCluster(cluster); - }); - - it("adds new cluster to store", async () => { - const storedCluster = clusterStore.getById("foo"); - - assert(storedCluster); - - expect(storedCluster.id).toBe("foo"); - expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data"); - expect(storedCluster.preferences.icon).toBe( - "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", - ); - }); - }); - - describe("with prod and dev clusters added", () => { - beforeEach(() => { - const store = clusterStore; - - store.addCluster({ - id: "prod", - contextName: "foo", - preferences: { - clusterName: "prod", - }, - kubeConfigPath: writeFileSyncAndReturnPath( - getCustomKubeConfigFilePath("prod"), - kubeconfig, - ), - }); - store.addCluster({ - id: "dev", - contextName: "foo2", - preferences: { - clusterName: "dev", - }, - kubeConfigPath: writeFileSyncAndReturnPath( - getCustomKubeConfigFilePath("dev"), - kubeconfig, - ), - }); - }); - - it("check if store can contain multiple clusters", () => { - expect(clusterStore.hasClusters()).toBeTruthy(); - expect(clusterStore.clusters.size).toBe(2); - }); - - it("check if cluster's kubeconfig file saved", () => { - const file = writeFileSyncAndReturnPath(getCustomKubeConfigFilePath("boo"), "kubeconfig"); - - expect(readFileSync(file)).toBe("kubeconfig"); - }); - }); - }); - - describe("config with existing clusters", () => { - beforeEach(() => { - writeFileSync("/temp-kube-config", kubeconfig); - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { - __internal__: { - migrations: { - version: "99.99.99", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfigPath: "/temp-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "default", - }, - { - id: "cluster2", - kubeConfigPath: "/temp-kube-config", - contextName: "foo2", - preferences: { terminalCWD: "/foo2" }, - }, - { - id: "cluster3", - kubeConfigPath: "/temp-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "foo", - ownerRef: "foo", - }, - ], - }); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); - }); - it("allows to retrieve a cluster", () => { - const storedCluster = clusterStore.getById("cluster1"); - - assert(storedCluster); - - expect(storedCluster.id).toBe("cluster1"); - expect(storedCluster.preferences.terminalCWD).toBe("/foo"); - }); - - it("allows getting all of the clusters", async () => { - const storedClusters = clusterStore.clustersList; - - expect(storedClusters.length).toBe(3); - expect(storedClusters[0].id).toBe("cluster1"); - expect(storedClusters[0].preferences.terminalCWD).toBe("/foo"); - expect(storedClusters[1].id).toBe("cluster2"); - expect(storedClusters[1].preferences.terminalCWD).toBe("/foo2"); - expect(storedClusters[2].id).toBe("cluster3"); - }); - }); - - describe("config with invalid cluster kubeconfig", () => { - beforeEach(() => { - writeFileSync("/invalid-kube-config", invalidKubeconfig); - writeFileSync("/valid-kube-config", kubeconfig); - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { - __internal__: { - migrations: { - version: "99.99.99", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfigPath: "/invalid-kube-config", - contextName: "test", - preferences: { terminalCWD: "/foo" }, - workspace: "foo", - }, - { - id: "cluster2", - kubeConfigPath: "/valid-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "default", - }, - ], - }); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); - }); - - it("does not enable clusters with invalid kubeconfig", () => { - const storedClusters = clusterStore.clustersList; - - expect(storedClusters.length).toBe(1); - }); - }); - - describe("pre 3.6.0-beta.1 config with an existing cluster", () => { - beforeEach(() => { - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { - __internal__: { - migrations: { - version: "3.5.0", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfig: minimalValidKubeConfig, - contextName: "cluster", - preferences: { - icon: "store://icon_path", - }, - }, - ], - }); - writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon); - - di.override(storeMigrationVersionInjectable, () => "3.6.0"); - - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); - }); - - it("migrates to modern format with kubeconfig in a file", async () => { - const config = clusterStore.clustersList[0].kubeConfigPath; - - expect(readFileSync(config)).toBe(minimalValidKubeConfig); - }); - - it("migrates to modern format with icon not in file", async () => { - expect(clusterStore.clustersList[0].preferences.icon).toMatch(/data:;base64,/); - }); - }); -}); - -const invalidKubeconfig = JSON.stringify({ - apiVersion: "v1", - clusters: [{ - cluster: { - server: "https://localhost", - }, - name: "test2", - }], - contexts: [{ - context: { - cluster: "test", - user: "test", - }, - name: "test", - }], - "current-context": "test", - kind: "Config", - preferences: {}, - users: [{ - user: { - token: "kubeconfig-user-q4lm4:xxxyyyy", - }, - name: "test", - }], -}); - -const minimalValidKubeConfig = JSON.stringify({ - apiVersion: "v1", - clusters: [ - { - name: "minikube", - cluster: { - server: "https://192.168.64.3:8443", - }, - }, - ], - "current-context": "minikube", - contexts: [ - { - context: { - cluster: "minikube", - user: "minikube", - }, - name: "minikube", - }, - ], - users: [ - { - name: "minikube", - user: { - "client-certificate": "/Users/foo/.minikube/client.crt", - "client-key": "/Users/foo/.minikube/client.key", - }, - }, - ], - kind: "Config", - preferences: {}, -}); diff --git a/src/common/__tests__/event-emitter.test.ts b/src/common/__tests__/event-emitter.test.ts deleted file mode 100644 index cc71922182..0000000000 --- a/src/common/__tests__/event-emitter.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { EventEmitter } from "../event-emitter"; - -describe("EventEmitter", () => { - it("should stop early if a listener returns false", () => { - let called = false; - const e = new EventEmitter<[]>(); - - e.addListener(() => false, {}); - e.addListener(() => { called = true; }, {}); - e.emit(); - - expect(called).toBe(false); - }); - - it("shouldn't stop early if a listener returns 0", () => { - let called = false; - const e = new EventEmitter<[]>(); - - e.addListener(() => 0 as never, {}); - e.addListener(() => { called = true; }, {}); - e.emit(); - - expect(called).toBe(true); - }); - - it("prepended listeners should be called before others", () => { - const callOrder: number[] = []; - const e = new EventEmitter<[]>(); - - e.addListener(() => { callOrder.push(1); }, {}); - e.addListener(() => { callOrder.push(2); }, {}); - e.addListener(() => { callOrder.push(3); }, { prepend: true }); - e.emit(); - - expect(callOrder).toStrictEqual([3, 1, 2]); - }); - - it("once listeners should be called only once", () => { - const callOrder: number[] = []; - const e = new EventEmitter<[]>(); - - e.addListener(() => { callOrder.push(1); }, {}); - e.addListener(() => { callOrder.push(2); }, {}); - e.addListener(() => { callOrder.push(3); }, { once: true }); - e.emit(); - e.emit(); - - expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]); - }); - - it("removeListener should stop the listener from being called", () => { - const callOrder: number[] = []; - const e = new EventEmitter<[]>(); - const r = () => { callOrder.push(3); }; - - e.addListener(() => { callOrder.push(1); }, {}); - e.addListener(() => { callOrder.push(2); }, {}); - e.addListener(r); - - e.emit(); - e.removeListener(r); - e.emit(); - - expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]); - }); - - it("removeAllListeners should stop the all listeners from being called", () => { - const callOrder: number[] = []; - const e = new EventEmitter<[]>(); - - e.addListener(() => { callOrder.push(1); }); - e.addListener(() => { callOrder.push(2); }); - e.addListener(() => { callOrder.push(3); }); - - e.emit(); - e.removeAllListeners(); - e.emit(); - - expect(callOrder).toStrictEqual([1, 2, 3]); - }); -}); diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts deleted file mode 100644 index ac8cecc1d2..0000000000 --- a/src/common/__tests__/hotbar-store.test.ts +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { anyObject } from "jest-mock-extended"; -import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import type { DiContainer } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../hotbars/store.injectable"; -import type { HotbarStore } from "../hotbars/store"; -import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable"; -import { computed } from "mobx"; -import hasCategoryForEntityInjectable from "../catalog/has-category-for-entity.injectable"; -import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import loggerInjectable from "../logger.injectable"; -import type { Logger } from "../logger"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; - -function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { - return { - getName: jest.fn(() => data.metadata?.name), - getId: jest.fn(() => data.metadata?.uid), - getSource: jest.fn(() => data.metadata?.source ?? "unknown"), - isEnabled: jest.fn(() => data.status?.enabled ?? true), - onContextMenuOpen: jest.fn(), - onSettingsOpen: jest.fn(), - metadata: {}, - spec: {}, - status: {}, - ...data, - } as CatalogEntity; -} - -describe("HotbarStore", () => { - let di: DiContainer; - let hotbarStore: HotbarStore; - let testCluster: CatalogEntity; - let minikubeCluster: CatalogEntity; - let awsCluster: CatalogEntity; - let loggerMock: jest.Mocked; - - beforeEach(async () => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - testCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-test-id", - name: "my-test-cluster", - source: "local", - labels: {}, - }, - }); - minikubeCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-minikube-id", - name: "my-minikube-cluster", - source: "local", - labels: {}, - }, - }); - awsCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-aws-id", - name: "my-aws-cluster", - source: "local", - labels: {}, - }, - }); - - di.override(hasCategoryForEntityInjectable, () => () => true); - - loggerMock = { - warn: jest.fn(), - debug: jest.fn(), - error: jest.fn(), - info: jest.fn(), - silly: jest.fn(), - }; - - di.override(loggerInjectable, () => loggerMock); - - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - - const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - catalogEntityRegistry.addComputedSource("some-id", computed(() => [ - testCluster, - minikubeCluster, - awsCluster, - catalogCatalogEntity, - ])); - }); - - describe("given no previous data in store, running all migrations", () => { - beforeEach(() => { - hotbarStore = di.inject(hotbarStoreInjectable); - - hotbarStore.load(); - }); - - describe("load", () => { - it("loads one hotbar by default", () => { - expect(hotbarStore.hotbars.length).toEqual(1); - }); - }); - - describe("add", () => { - it("adds a hotbar", () => { - hotbarStore.add({ name: "hottest" }); - expect(hotbarStore.hotbars.length).toEqual(2); - }); - }); - - describe("hotbar items", () => { - it("initially creates 12 empty cells", () => { - expect(hotbarStore.getActive().items.length).toEqual(12); - }); - - it("initially adds catalog entity as first item", () => { - expect(hotbarStore.getActive().items[0]?.entity.name).toEqual("Catalog"); - }); - - it("adds items", () => { - hotbarStore.addToHotbar(testCluster); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items.length).toEqual(2); - }); - - it("removes items", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.removeFromHotbar("some-test-id"); - hotbarStore.removeFromHotbar("catalog-entity"); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items).toStrictEqual([]); - }); - - it("does nothing if removing with invalid uid", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.removeFromHotbar("invalid uid"); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items.length).toEqual(2); - }); - - it("moves item to empty cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - expect(hotbarStore.getActive().items[6]).toBeNull(); - - hotbarStore.restackItems(1, 5); - - expect(hotbarStore.getActive().items[5]).toBeTruthy(); - expect(hotbarStore.getActive().items[5]?.entity.uid).toEqual("some-test-id"); - }); - - it("moves items down", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - // aws -> catalog - hotbarStore.restackItems(3, 0); - - const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null); - - expect(items.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]); - }); - - it("moves items up", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - // test -> aws - hotbarStore.restackItems(1, 3); - - const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null); - - expect(items.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]); - }); - - it("logs an error if cellIndex is out of bounds", () => { - hotbarStore.add({ name: "hottest", id: "hottest" }); - hotbarStore.setActiveHotbar("hottest"); - - hotbarStore.addToHotbar(testCluster, -1); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - - hotbarStore.addToHotbar(testCluster, 12); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - - hotbarStore.addToHotbar(testCluster, 13); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - }); - - it("throws an error if getId is invalid or returns not a string", () => { - expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(TypeError); - expect(() => hotbarStore.addToHotbar({ getId: () => true } as any)).toThrowError(TypeError); - }); - - it("throws an error if getName is invalid or returns not a string", () => { - expect(() => hotbarStore.addToHotbar({ getId: () => "" } as any)).toThrowError(TypeError); - expect(() => hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError); - }); - - it("does nothing when item moved to same cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.restackItems(1, 1); - - expect(hotbarStore.getActive().items[1]?.entity.uid).toEqual("some-test-id"); - }); - - it("new items takes first empty cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(awsCluster); - hotbarStore.restackItems(0, 3); - hotbarStore.addToHotbar(minikubeCluster); - - expect(hotbarStore.getActive().items[0]?.entity.uid).toEqual("some-minikube-id"); - }); - - it("throws if invalid arguments provided", () => { - hotbarStore.addToHotbar(testCluster); - - expect(() => hotbarStore.restackItems(-5, 0)).toThrow(); - expect(() => hotbarStore.restackItems(2, -1)).toThrow(); - expect(() => hotbarStore.restackItems(14, 1)).toThrow(); - expect(() => hotbarStore.restackItems(11, 112)).toThrow(); - }); - - it("checks if entity already pinned to hotbar", () => { - hotbarStore.addToHotbar(testCluster); - - expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy(); - expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy(); - }); - }); - }); - - describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { - beforeEach(() => { - const writeJsonSync = di.inject(writeJsonSyncInjectable); - - writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", { - __internal__: { - migrations: { - version: "5.0.0-beta.3", - }, - }, - hotbars: [ - { - id: "3caac17f-aec2-4723-9694-ad204465d935", - name: "myhotbar", - items: [ - { - entity: { - uid: "some-aws-id", - }, - }, - { - entity: { - uid: "55b42c3c7ba3b04193416cda405269a5", - }, - }, - { - entity: { - uid: "176fd331968660832f62283219d7eb6e", - }, - }, - { - entity: { - uid: "61c4fb45528840ebad1badc25da41d14", - name: "user1-context", - source: "local", - }, - }, - { - entity: { - uid: "27d6f99fe9e7548a6e306760bfe19969", - name: "foo2", - source: "local", - }, - }, - null, - { - entity: { - uid: "c0b20040646849bb4dcf773e43a0bf27", - name: "multinode-demo", - source: "local", - }, - }, - null, - null, - null, - null, - null, - ], - }, - ], - }); - - di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10"); - - hotbarStore = di.inject(hotbarStoreInjectable); - - hotbarStore.load(); - }); - - it("allows to retrieve a hotbar", () => { - const hotbar = hotbarStore.findById("3caac17f-aec2-4723-9694-ad204465d935"); - - expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935"); - }); - - it("clears cells without entity", () => { - const items = hotbarStore.hotbars[0].items; - - expect(items[2]).toBeNull(); - }); - - it("adds extra data to cells with according entity", () => { - const items = hotbarStore.hotbars[0].items; - - expect(items[0]).toEqual({ - entity: { - name: "my-aws-cluster", - source: "local", - uid: "some-aws-id", - }, - }); - }); - }); -}); diff --git a/src/common/__tests__/kube-helpers.test.ts b/src/common/__tests__/kube-helpers.test.ts deleted file mode 100644 index ec466b5c56..0000000000 --- a/src/common/__tests__/kube-helpers.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeConfig } from "@kubernetes/client-node"; -import { validateKubeConfig, loadConfigFromString } from "../kube-helpers"; - -const kubeconfig = ` -apiVersion: v1 -clusters: -- cluster: - server: https://localhost - name: test -contexts: -- context: - cluster: test - user: test - name: valid -- context: - cluster: test2 - user: test - name: invalidCluster -- context: - cluster: test - user: test2 - name: invalidUser -- context: - cluster: test - user: invalidExec - name: invalidExec -current-context: test -kind: Config -preferences: {} -users: -- name: test - user: - exec: - command: echo -- name: invalidExec - user: - exec: - command: foo -`; - -interface Kubeconfig { - apiVersion: string; - clusters: [{ - name: string; - cluster: { - server: string; - }; - }]; - contexts: [{ - context: { - cluster: string; - user: string; - }; - name: string; - }]; - users: [{ - name: string; - }]; - kind: string; - "current-context": string; - preferences: {}; -} - -let mockKubeConfig: Kubeconfig; - -describe("kube helpers", () => { - describe("validateKubeconfig", () => { - const kc = new KubeConfig(); - - beforeAll(() => { - kc.loadFromString(kubeconfig); - }); - describe("with default validation options", () => { - describe("with valid kubeconfig", () => { - it("does not return an error", () => { - expect(validateKubeConfig(kc, "valid")).toBeDefined(); - }); - }); - describe("with invalid context object", () => { - it("returns an error", () => { - expect(validateKubeConfig(kc, "invalid").error?.toString()).toEqual( - expect.stringContaining("No valid context object provided in kubeconfig for context 'invalid'"), - ); - }); - }); - - describe("with invalid cluster object", () => { - it("returns an error", () => { - expect(validateKubeConfig(kc, "invalidCluster").error?.toString()).toEqual( - expect.stringContaining("No valid cluster object provided in kubeconfig for context 'invalidCluster'"), - ); - }); - }); - - describe("with invalid user object", () => { - it("returns an error", () => { - expect(validateKubeConfig(kc, "invalidUser").error?.toString()).toEqual( - expect.stringContaining("No valid user object provided in kubeconfig for context 'invalidUser'"), - ); - }); - }); - }); - }); - - describe("pre-validate context object in kubeconfig tests", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe("Check logger.error() output", () => { - it("invalid yaml string", () => { - const invalidYAMLString = "fancy foo config"; - - expect(loadConfigFromString(invalidYAMLString).error).toBeInstanceOf(Error); - }); - it("empty contexts", () => { - const emptyContexts = `apiVersion: v1\ncontexts: []`; - - expect(loadConfigFromString(emptyContexts).error).toBeUndefined(); - }); - }); - - describe("Check valid kubeconfigs", () => { - beforeEach(() => { - mockKubeConfig = { - apiVersion: "v1", - clusters: [{ - name: "minikube", - cluster: { - server: "https://192.168.64.3:8443", - }, - }], - contexts: [{ - context: { - cluster: "minikube", - user: "minikube", - }, - name: "minikube", - }], - users: [{ - name: "minikube", - }], - kind: "Config", - "current-context": "minikube", - preferences: {}, - }; - }); - - it("single context is ok", async () => { - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - }); - - it("multiple context is ok", async () => { - mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "cluster-2" }); - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - expect(config.contexts.length).toBe(2); - }); - }); - - describe("Check invalid kubeconfigs", () => { - beforeEach(() => { - mockKubeConfig = { - apiVersion: "v1", - clusters: [{ - name: "minikube", - cluster: { - server: "https://192.168.64.3:8443", - }, - }], - contexts: [{ - context: { - cluster: "minikube", - user: "minikube", - }, - name: "minikube", - }], - users: [{ - name: "minikube", - }], - kind: "Config", - "current-context": "minikube", - preferences: {}, - }; - }); - - it("empty name in context causes it to be removed", async () => { - mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "" }); - expect(mockKubeConfig.contexts.length).toBe(2); - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - expect(config.contexts.length).toBe(1); - }); - - it("empty cluster in context causes it to be removed", async () => { - mockKubeConfig.contexts.push({ context: { cluster: "", user: "cluster-2" }, name: "cluster-2" }); - expect(mockKubeConfig.contexts.length).toBe(2); - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - expect(config.contexts.length).toBe(1); - }); - - it("empty user in context causes it to be removed", async () => { - mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" }); - expect(mockKubeConfig.contexts.length).toBe(2); - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - expect(config.contexts.length).toBe(1); - }); - - it("invalid context in between valid contexts is removed", async () => { - mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" }); - mockKubeConfig.contexts.push({ context: { cluster: "cluster-3", user: "cluster-3" }, name: "cluster-3" }); - expect(mockKubeConfig.contexts.length).toBe(3); - const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig)); - - expect(config.getCurrentContext()).toBe("minikube"); - expect(config.contexts.length).toBe(2); - expect(config.contexts[0].name).toBe("minikube"); - expect(config.contexts[1].name).toBe("cluster-3"); - }); - }); - }); -}); diff --git a/src/common/__tests__/timezones.test.ts b/src/common/__tests__/timezones.test.ts deleted file mode 100644 index e7fa68e87c..0000000000 --- a/src/common/__tests__/timezones.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -describe("Timezones", () => { - it("should always be UTC", () => { - expect(new Date().getTimezoneOffset()).toBe(0); - }); -}); - -export {}; diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts deleted file mode 100644 index 7071fc5c17..0000000000 --- a/src/common/__tests__/user-store.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { UserStore } from "../user-store"; -import userStoreInjectable from "../user-store/user-store.injectable"; -import type { DiContainer } from "@ogre-tools/injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import type { ClusterStoreModel } from "../cluster-store/cluster-store"; -import { defaultThemeId } from "../vars"; -import writeFileInjectable from "../fs/write-file.injectable"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import releaseChannelInjectable from "../vars/release-channel.injectable"; -import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; -import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; - -describe("user store tests", () => { - let userStore: UserStore; - let di: DiContainer; - - beforeEach(async () => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(writeFileInjectable, () => () => Promise.resolve()); - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - - di.override(releaseChannelInjectable, () => ({ - get: () => "latest" as const, - init: async () => {}, - })); - await di.inject(defaultUpdateChannelInjectable).init(); - - userStore = di.inject(userStoreInjectable); - }); - - describe("for an empty config", () => { - beforeEach(() => { - const writeJsonSync = di.inject(writeJsonSyncInjectable); - - writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {}); - writeJsonSync("/some-directory-for-user-data/kube_config", {}); - - userStore.load(); - }); - - it("allows setting and getting preferences", () => { - userStore.httpsProxy = "abcd://defg"; - - expect(userStore.httpsProxy).toBe("abcd://defg"); - expect(userStore.colorTheme).toBe(defaultThemeId); - - userStore.colorTheme = "light"; - expect(userStore.colorTheme).toBe("light"); - }); - - it("correctly resets theme to default value", async () => { - userStore.colorTheme = "some other theme"; - userStore.resetTheme(); - expect(userStore.colorTheme).toBe(defaultThemeId); - }); - }); - - describe("migrations", () => { - beforeEach(() => { - const writeJsonSync = di.inject(writeJsonSyncInjectable); - const writeFileSync = di.inject(writeFileSyncInjectable); - - writeJsonSync("/some-directory-for-user-data/lens-user-store.json", { - preferences: { colorTheme: "light" }, - }); - - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { - clusters: [ - { - id: "foobar", - kubeConfigPath: "/some-directory-for-user-data/extension_data/foo/bar", - }, - { - id: "barfoo", - kubeConfigPath: "/some/other/path", - }, - ], - } as ClusterStoreModel); - - writeJsonSync("/some-directory-for-user-data/extension_data", {}); - - writeFileSync("/some/other/path", "is file"); - - di.override(storeMigrationVersionInjectable, () => "10.0.0"); - - userStore.load(); - }); - - it("skips clusters for adding to kube-sync with files under extension_data/", () => { - expect(userStore.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false); - expect(userStore.syncKubeconfigEntries.has("/some/other/path")).toBe(true); - }); - - it("allows access to the colorTheme preference", () => { - expect(userStore.colorTheme).toBe("light"); - }); - }); -}); diff --git a/src/common/app-event-bus/app-event-bus.injectable.ts b/src/common/app-event-bus/app-event-bus.injectable.ts deleted file mode 100644 index 3dee975f7b..0000000000 --- a/src/common/app-event-bus/app-event-bus.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { EventEmitter } from "../event-emitter"; -import type { AppEvent } from "./event-bus"; - -const appEventBusInjectable = getInjectable({ - id: "app-event-bus", - instantiate: () => new EventEmitter<[AppEvent]>, - decorable: false, -}); - -export default appEventBusInjectable; diff --git a/src/common/app-event-bus/emit-event.injectable.ts b/src/common/app-event-bus/emit-event.injectable.ts deleted file mode 100644 index 9c9194ceb8..0000000000 --- a/src/common/app-event-bus/emit-event.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 appEventBusInjectable from "./app-event-bus.injectable"; -import type { AppEvent } from "./event-bus"; - -export type EmitAppEvent = (event: AppEvent) => void; - -const emitAppEventInjectable = getInjectable({ - id: "emit-app-event", - instantiate: (di): EmitAppEvent => { - const bus = di.inject(appEventBusInjectable); - - return (event) => bus.emit(event); - }, - decorable: false, -}); - -export default emitAppEventInjectable; diff --git a/src/common/app-event-bus/event-bus.ts b/src/common/app-event-bus/event-bus.ts deleted file mode 100644 index d121b842f7..0000000000 --- a/src/common/app-event-bus/event-bus.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Data for telemetry - */ -export interface AppEvent { - name: string; - action: string; - destination?: string; - params?: Record; -} diff --git a/src/common/app-paths/app-path-injection-token.ts b/src/common/app-paths/app-path-injection-token.ts deleted file mode 100644 index 91e8a580d8..0000000000 --- a/src/common/app-paths/app-path-injection-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { PathName } from "./app-path-names"; - -export type AppPaths = Record; diff --git a/src/common/app-paths/app-path-names.ts b/src/common/app-paths/app-path-names.ts deleted file mode 100644 index 8e3d2c440e..0000000000 --- a/src/common/app-paths/app-path-names.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { app as electronApp } from "electron"; - -export type PathName = Parameters[0] | "currentApp"; - -export const pathNames: PathName[] = [ - "currentApp", - "home", - "appData", - "userData", - "cache", - "temp", - "exe", - "module", - "desktop", - "documents", - "downloads", - "music", - "pictures", - "videos", - "logs", - "crashDumps", - "recent", -]; diff --git a/src/common/app-paths/app-paths-channel.ts b/src/common/app-paths/app-paths-channel.ts deleted file mode 100644 index 4502569d3b..0000000000 --- a/src/common/app-paths/app-paths-channel.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AppPaths } from "./app-path-injection-token"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; - -export type AppPathsChannel = RequestChannel; - -export const appPathsChannel: AppPathsChannel = { - id: "app-paths", -}; - diff --git a/src/common/app-paths/app-paths-state.injectable.ts b/src/common/app-paths/app-paths-state.injectable.ts deleted file mode 100644 index 5487d428b2..0000000000 --- a/src/common/app-paths/app-paths-state.injectable.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 { AppPaths } from "./app-path-injection-token"; - -const appPathsStateInjectable = getInjectable({ - id: "app-paths-state", - - instantiate: () => { - let state: AppPaths; - - return { - get: () =>{ - if (!state) { - throw new Error("Tried to get app paths before state is setupped."); - } - - return state; - }, - - set: (newState: AppPaths) => { - if (state) { - throw new Error("Tried to overwrite existing state of app paths."); - } - - state = newState; - }, - }; - }, -}); - -export default appPathsStateInjectable; diff --git a/src/common/app-paths/app-paths.injectable.ts b/src/common/app-paths/app-paths.injectable.ts deleted file mode 100644 index 0e836a8514..0000000000 --- a/src/common/app-paths/app-paths.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsStateInjectable from "./app-paths-state.injectable"; - -const appPathsInjectable = getInjectable({ - id: "app-paths", - instantiate: (di) => di.inject(appPathsStateInjectable).get(), -}); - -export default appPathsInjectable; diff --git a/src/common/app-paths/app-paths.test.ts b/src/common/app-paths/app-paths.test.ts deleted file mode 100644 index 6e645f96fa..0000000000 --- a/src/common/app-paths/app-paths.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AppPaths } from "./app-path-injection-token"; -import getElectronAppPathInjectable from "../../main/app-paths/get-electron-app-path/get-electron-app-path.injectable"; -import type { PathName } from "./app-path-names"; -import setElectronAppPathInjectable from "../../main/app-paths/set-electron-app-path/set-electron-app-path.injectable"; -import directoryForIntegrationTestingInjectable from "../../main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable"; -import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { DiContainer } from "@ogre-tools/injectable"; -import appPathsInjectable from "./app-paths.injectable"; - -describe("app-paths", () => { - let builder: ApplicationBuilder; - - beforeEach(() => { - builder = getApplicationBuilder(); - - const defaultAppPathsStub: AppPaths = { - currentApp: "/some-current-app", - appData: "/some-app-data", - cache: "/some-cache", - crashDumps: "/some-crash-dumps", - desktop: "/some-desktop", - documents: "/some-documents", - downloads: "/some-downloads", - exe: "/some-exe", - home: "/some-home-path", - logs: "/some-logs", - module: "/some-module", - music: "/some-music", - pictures: "/some-pictures", - recent: "/some-recent", - temp: "/some-temp", - videos: "/some-videos", - userData: "/some-irrelevant-user-data", - }; - - builder.beforeApplicationStart((mainDi) => { - mainDi.override( - getElectronAppPathInjectable, - () => - (key: PathName): string | null => - defaultAppPathsStub[key], - ); - - mainDi.override( - setElectronAppPathInjectable, - () => - (key: PathName, path: string): void => { - defaultAppPathsStub[key] = path; - }, - ); - }); - }); - - describe("normally", () => { - let windowDi: DiContainer; - let mainDi: DiContainer; - - beforeEach(async () => { - await builder.render(); - - windowDi = builder.applicationWindow.only.di; - mainDi = builder.mainDi; - }); - - it("given in renderer, when injecting app paths, returns application specific app paths", () => { - const actual = windowDi.inject(appPathsInjectable); - - expect(actual).toEqual({ - currentApp: "/some-current-app", - appData: "/some-app-data", - cache: "/some-cache", - crashDumps: "/some-crash-dumps", - desktop: "/some-desktop", - documents: "/some-documents", - downloads: "/some-downloads", - exe: "/some-exe", - home: "/some-home-path", - logs: "/some-logs", - module: "/some-module", - music: "/some-music", - pictures: "/some-pictures", - recent: "/some-recent", - temp: "/some-temp", - videos: "/some-videos", - userData: "/some-app-data/some-product-name", - }); - }); - - it("given in main, when injecting app paths, returns application specific app paths", () => { - const actual = mainDi.inject(appPathsInjectable); - - expect(actual).toEqual({ - currentApp: "/some-current-app", - appData: "/some-app-data", - cache: "/some-cache", - crashDumps: "/some-crash-dumps", - desktop: "/some-desktop", - documents: "/some-documents", - downloads: "/some-downloads", - exe: "/some-exe", - home: "/some-home-path", - logs: "/some-logs", - module: "/some-module", - music: "/some-music", - pictures: "/some-pictures", - recent: "/some-recent", - temp: "/some-temp", - videos: "/some-videos", - userData: "/some-app-data/some-product-name", - }); - }); - }); - - describe("when running integration tests", () => { - let windowDi: DiContainer; - - beforeEach(async () => { - builder.beforeApplicationStart((mainDi) => { - mainDi.override( - directoryForIntegrationTestingInjectable, - () => "/some-integration-testing-app-data", - ); - }); - - await builder.render(); - - windowDi = builder.applicationWindow.only.di; - }); - - it("given in renderer, when injecting path for app data, has integration specific app data path", () => { - const { appData, userData } = windowDi.inject(appPathsInjectable); - - expect({ appData, userData }).toEqual({ - appData: "/some-integration-testing-app-data", - userData: "/some-integration-testing-app-data/some-product-name", - }); - }); - - it("given in main, when injecting path for app data, has integration specific app data path", () => { - const { appData, userData } = windowDi.inject(appPathsInjectable); - - expect({ appData, userData }).toEqual({ - appData: "/some-integration-testing-app-data", - userData: "/some-integration-testing-app-data/some-product-name", - }); - }); - }); -}); diff --git a/src/common/app-paths/directory-for-binaries/directory-for-binaries.injectable.ts b/src/common/app-paths/directory-for-binaries/directory-for-binaries.injectable.ts deleted file mode 100644 index f8a55b041c..0000000000 --- a/src/common/app-paths/directory-for-binaries/directory-for-binaries.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../directory-for-user-data/directory-for-user-data.injectable"; -import joinPathsInjectable from "../../path/join-paths.injectable"; - -const directoryForBinariesInjectable = getInjectable({ - id: "directory-for-binaries", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const directoryForUserData = di.inject(directoryForUserDataInjectable); - - return joinPaths(directoryForUserData, "binaries"); - }, -}); - -export default directoryForBinariesInjectable; diff --git a/src/common/app-paths/directory-for-downloads/directory-for-downloads.injectable.ts b/src/common/app-paths/directory-for-downloads/directory-for-downloads.injectable.ts deleted file mode 100644 index 01f97dbaec..0000000000 --- a/src/common/app-paths/directory-for-downloads/directory-for-downloads.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsInjectable from "../app-paths.injectable"; - -const directoryForDownloadsInjectable = getInjectable({ - id: "directory-for-downloads", - instantiate: (di) => di.inject(appPathsInjectable).downloads, -}); - -export default directoryForDownloadsInjectable; diff --git a/src/common/app-paths/directory-for-exes/directory-for-exes.injectable.ts b/src/common/app-paths/directory-for-exes/directory-for-exes.injectable.ts deleted file mode 100644 index 690f53d958..0000000000 --- a/src/common/app-paths/directory-for-exes/directory-for-exes.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsInjectable from "../app-paths.injectable"; - -const directoryForExesInjectable = getInjectable({ - id: "directory-for-exes", - instantiate: (di) => di.inject(appPathsInjectable).exe, -}); - -export default directoryForExesInjectable; diff --git a/src/common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable.ts b/src/common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable.ts deleted file mode 100644 index d49029572e..0000000000 --- a/src/common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../directory-for-user-data/directory-for-user-data.injectable"; -import joinPathsInjectable from "../../path/join-paths.injectable"; - -const directoryForKubeConfigsInjectable = getInjectable({ - id: "directory-for-kube-configs", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const directoryForUserData = di.inject(directoryForUserDataInjectable); - - return joinPaths(directoryForUserData, "kubeconfigs"); - }, -}); - -export default directoryForKubeConfigsInjectable; diff --git a/src/common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable.ts b/src/common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable.ts deleted file mode 100644 index c1ef1b23f5..0000000000 --- a/src/common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 directoryForBinariesInjectable from "../directory-for-binaries/directory-for-binaries.injectable"; -import joinPathsInjectable from "../../path/join-paths.injectable"; - -const directoryForKubectlBinariesInjectable = getInjectable({ - id: "directory-for-kubectl-binaries", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const directoryForBinaries = di.inject(directoryForBinariesInjectable); - - return joinPaths(directoryForBinaries, "kubectl"); - }, -}); - -export default directoryForKubectlBinariesInjectable; diff --git a/src/common/app-paths/directory-for-logs.injectable.ts b/src/common/app-paths/directory-for-logs.injectable.ts deleted file mode 100644 index e9abc35c44..0000000000 --- a/src/common/app-paths/directory-for-logs.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsInjectable from "./app-paths.injectable"; - -const directoryForLogsInjectable = getInjectable({ - id: "directory-for-logs", - instantiate: (di) => di.inject(appPathsInjectable).logs, -}); - -export default directoryForLogsInjectable; diff --git a/src/common/app-paths/directory-for-temp/directory-for-temp.injectable.ts b/src/common/app-paths/directory-for-temp/directory-for-temp.injectable.ts deleted file mode 100644 index 460efc073d..0000000000 --- a/src/common/app-paths/directory-for-temp/directory-for-temp.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsInjectable from "../app-paths.injectable"; - -const directoryForTempInjectable = getInjectable({ - id: "directory-for-temp", - instantiate: (di) => di.inject(appPathsInjectable).temp, -}); - -export default directoryForTempInjectable; diff --git a/src/common/app-paths/directory-for-user-data/directory-for-user-data.injectable.ts b/src/common/app-paths/directory-for-user-data/directory-for-user-data.injectable.ts deleted file mode 100644 index 0eb32221c6..0000000000 --- a/src/common/app-paths/directory-for-user-data/directory-for-user-data.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 appPathsInjectable from "../app-paths.injectable"; - -const directoryForUserDataInjectable = getInjectable({ - id: "directory-for-user-data", - instantiate: (di) => di.inject(appPathsInjectable).userData, -}); - -export default directoryForUserDataInjectable; diff --git a/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts b/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts deleted file mode 100644 index 0580d372f1..0000000000 --- a/src/common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 directoryForKubeConfigsInjectable from "../directory-for-kube-configs/directory-for-kube-configs.injectable"; -import joinPathsInjectable from "../../path/join-paths.injectable"; - -export type GetCustomKubeConfigFilePath = (fileName: string) => string; - -const getCustomKubeConfigFilePathInjectable = getInjectable({ - id: "get-custom-kube-config-directory", - - instantiate: (di): GetCustomKubeConfigFilePath => { - const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable); - const joinPaths = di.inject(joinPathsInjectable); - - return (fileName) => joinPaths(directoryForKubeConfigs, fileName); - }, -}); - -export default getCustomKubeConfigFilePathInjectable; diff --git a/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts b/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts deleted file mode 100644 index db4734994d..0000000000 --- a/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import pathToNpmCliInjectable from "./path-to-npm-cli.injectable"; - -export default getGlobalOverride(pathToNpmCliInjectable, () => "/some/npm/cli/path"); diff --git a/src/common/app-paths/path-to-npm-cli.injectable.ts b/src/common/app-paths/path-to-npm-cli.injectable.ts deleted file mode 100644 index 1c32b9ed6a..0000000000 --- a/src/common/app-paths/path-to-npm-cli.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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"; - -const pathToNpmCliInjectable = getInjectable({ - id: "path-to-npm-cli", - instantiate: () => __non_webpack_require__.resolve("npm"), - causesSideEffects: true, -}); - -export default pathToNpmCliInjectable; diff --git a/src/common/base-store/base-store.ts b/src/common/base-store/base-store.ts deleted file mode 100644 index 9a786329e4..0000000000 --- a/src/common/base-store/base-store.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type Config from "conf"; -import type { Migrations, Options as ConfOptions } from "conf/dist/source/types"; -import type { IEqualsComparer } from "mobx"; -import { makeObservable, reaction } from "mobx"; -import { disposer, isPromiseLike, toJS } from "../utils"; -import { broadcastMessage } from "../ipc"; -import isEqual from "lodash/isEqual"; -import { kebabCase } from "lodash"; -import type { GetConfigurationFileModel } from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import type { Logger } from "../logger"; -import type { PersistStateToConfig } from "./save-to-file"; -import type { GetBasenameOfPath } from "../path/get-basename.injectable"; -import type { EnlistMessageChannelListener } from "../utils/channel/enlist-message-channel-listener-injection-token"; - -export interface BaseStoreParams extends Omit, "migrations"> { - syncOptions?: { - fireImmediately?: boolean; - equals?: IEqualsComparer; - }; - configName: string; -} - -export interface IpcChannelPrefixes { - local: string; - remote: string; -} - -export interface BaseStoreDependencies { - readonly logger: Logger; - readonly storeMigrationVersion: string; - readonly directoryForUserData: string; - readonly migrations: Migrations>; - readonly ipcChannelPrefixes: IpcChannelPrefixes; - readonly shouldDisableSyncInListener: boolean; - getConfigurationFileModel: GetConfigurationFileModel; - persistStateToConfig: PersistStateToConfig; - getBasenameOfPath: GetBasenameOfPath; - enlistMessageChannelListener: EnlistMessageChannelListener; -} - -/** - * Note: T should only contain base JSON serializable types. - */ -export abstract class BaseStore { - private readonly syncDisposers = disposer(); - - readonly displayName = kebabCase(this.params.configName).toUpperCase(); - - protected constructor( - protected readonly dependencies: BaseStoreDependencies, - protected readonly params: BaseStoreParams, - ) { - makeObservable(this); - } - - /** - * This must be called after the last child's constructor is finished (or just before it finishes) - */ - load() { - this.dependencies.logger.info(`[${this.displayName}]: LOADING ...`); - - const config = this.dependencies.getConfigurationFileModel({ - projectName: "lens", - projectVersion: this.dependencies.storeMigrationVersion, - cwd: this.cwd(), - ...this.params, - migrations: this.dependencies.migrations as Migrations, - }); - - const res = this.fromStore(config.store); - - if (isPromiseLike(res)) { - this.dependencies.logger.error(`${this.displayName} extends BaseStore's fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`); - } - - this.startSyncing(config); - this.dependencies.logger.info(`[${this.displayName}]: LOADED from ${config.path}`); - } - - protected cwd() { - return this.dependencies.directoryForUserData; - } - - private startSyncing(config: Config) { - const name = this.dependencies.getBasenameOfPath(config.path); - - const disableSync = () => this.syncDisposers(); - const enableSync = () => { - this.syncDisposers.push( - reaction( - () => toJS(this.toJSON()), // unwrap possible observables and react to everything - model => { - this.dependencies.persistStateToConfig(config, model); - broadcastMessage(`${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, model); - }, - this.params.syncOptions, - ), - this.dependencies.enlistMessageChannelListener({ - channel: { - id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`, - }, - handler: (model) => { - this.dependencies.logger.silly(`[${this.displayName}]: syncing ${name}`, { model }); - - if (this.dependencies.shouldDisableSyncInListener) { - disableSync(); - } - - // todo: use "resourceVersion" if merge required (to avoid equality checks => better performance) - if (!isEqual(this.toJSON(), model)) { - this.fromStore(model as T); - } - - if (this.dependencies.shouldDisableSyncInListener) { - enableSync(); - } - }, - }), - ); - }; - - enableSync(); - } - - /** - * fromStore is called internally when a child class syncs with the file - * system. - * - * Note: This function **must** be synchronous. - * - * @param data the parsed information read from the stored JSON file - */ - protected abstract fromStore(data: T): void; - - /** - * toJSON is called when syncing the store to the filesystem. It should - * produce a JSON serializable object representation of the current state. - * - * It is recommended that a round trip is valid. Namely, calling - * `this.fromStore(this.toJSON())` shouldn't change the state. - */ - abstract toJSON(): T; -} diff --git a/src/common/base-store/channel-prefix.ts b/src/common/base-store/channel-prefix.ts deleted file mode 100644 index f2662c65e0..0000000000 --- a/src/common/base-store/channel-prefix.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IpcChannelPrefixes } from "./base-store"; - -export const baseStoreIpcChannelPrefixesInjectionToken = getInjectionToken({ - id: "base-store-ipc-channel-prefix-token", -}); diff --git a/src/common/base-store/disable-sync.ts b/src/common/base-store/disable-sync.ts deleted file mode 100644 index ce7abd16a1..0000000000 --- a/src/common/base-store/disable-sync.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const shouldBaseStoreDisableSyncInIpcListenerInjectionToken = getInjectionToken({ - id: "should-base-store-disable-sync-in-ipc-listener-token", -}); diff --git a/src/common/base-store/migrations.injectable.ts b/src/common/base-store/migrations.injectable.ts deleted file mode 100644 index 27f7489dfa..0000000000 --- a/src/common/base-store/migrations.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { InjectionToken } from "@ogre-tools/injectable"; -import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable"; -import type Conf from "conf/dist/source"; -import type { Migrations } from "conf/dist/source/types"; -import loggerInjectable from "../logger.injectable"; -import { getOrInsert, iter } from "../utils"; - -export interface MigrationDeclaration { - version: string; - run(store: Conf>>): void; -} - -const storeMigrationsInjectable = getInjectable({ - id: "store-migrations", - instantiate: (di, token): Migrations> => { - const logger = di.inject(loggerInjectable); - const declarations = di.injectMany(token); - const migrations = new Map(); - - for (const decl of declarations) { - getOrInsert(migrations, decl.version, []).push(decl.run); - } - - return Object.fromEntries( - iter.map( - migrations, - ([v, fns]) => [v, (store) => { - logger.info(`Running ${v} migration for ${store.path}`); - - for (const fn of fns) { - fn(store); - } - }], - ), - ); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, token: InjectionToken) => token.id, - }), -}); - -export default storeMigrationsInjectable; diff --git a/src/common/base-store/save-to-file.ts b/src/common/base-store/save-to-file.ts deleted file mode 100644 index b4d21ea950..0000000000 --- a/src/common/base-store/save-to-file.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type Config from "conf"; - -export type PersistStateToConfig = (config: Config, state: T) => void; - -export const persistStateToConfigInjectionToken = getInjectionToken({ - id: "persist-state-to-config-token", -}); diff --git a/src/common/catalog-entities/__tests__/kubernetes-cluster.test.ts b/src/common/catalog-entities/__tests__/kubernetes-cluster.test.ts deleted file mode 100644 index b2814f9785..0000000000 --- a/src/common/catalog-entities/__tests__/kubernetes-cluster.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import kubernetesClusterCategoryInjectable from "../../catalog/categories/kubernetes-cluster.injectable"; -import type { KubernetesClusterCategory } from "../kubernetes-cluster"; - - -describe("kubernetesClusterCategory", () => { - let kubernetesClusterCategory: KubernetesClusterCategory; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - kubernetesClusterCategory = di.inject(kubernetesClusterCategoryInjectable); - }); - - describe("filteredItems", () => { - const item1 = { - icon: "Icon", - title: "Title", - onClick: () => {}, - }; - const item2 = { - icon: "Icon 2", - title: "Title 2", - onClick: () => {}, - }; - - it("returns all items if no filter set", () => { - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]); - }); - - it("returns filtered items", () => { - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]); - - const disposer1 = kubernetesClusterCategory.addMenuFilter(item => item.icon === "Icon"); - - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1]); - - const disposer2 = kubernetesClusterCategory.addMenuFilter(item => item.title === "Title 2"); - - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([]); - - disposer1(); - - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item2]); - - disposer2(); - - expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]); - }); - }); -}); diff --git a/src/common/catalog-entities/general-catalog-entities/general-catalog-entity-injection-token.ts b/src/common/catalog-entities/general-catalog-entities/general-catalog-entity-injection-token.ts deleted file mode 100644 index ad743802bf..0000000000 --- a/src/common/catalog-entities/general-catalog-entities/general-catalog-entity-injection-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { GeneralEntity } from "../index"; - -export const generalCatalogEntityInjectionToken = getInjectionToken({ - id: "general-catalog-entity-injection-token", -}); diff --git a/src/common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable.ts b/src/common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable.ts deleted file mode 100644 index 15195a4b74..0000000000 --- a/src/common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 { generalCatalogEntityInjectionToken } from "../general-catalog-entity-injection-token"; -import { GeneralEntity } from "../../index"; -import { buildURL } from "../../../utils/buildUrl"; -import catalogRouteInjectable from "../../../front-end-routing/routes/catalog/catalog-route.injectable"; - -const catalogCatalogEntityInjectable = getInjectable({ - id: "general-catalog-entity-for-catalog", - - instantiate: (di) => { - const route = di.inject(catalogRouteInjectable); - const url = buildURL(route.path); - - return new GeneralEntity({ - metadata: { - uid: "catalog-entity", - name: "Catalog", - source: "app", - labels: {}, - }, - spec: { - path: url, - icon: { - material: "view_list", - background: "#3d90ce", - }, - }, - status: { - phase: "active", - }, - }); - }, - - injectionToken: generalCatalogEntityInjectionToken, -}); - -export default catalogCatalogEntityInjectable; diff --git a/src/common/catalog-entities/general-catalog-entities/implementations/welcome-catalog-entity.injectable.ts b/src/common/catalog-entities/general-catalog-entities/implementations/welcome-catalog-entity.injectable.ts deleted file mode 100644 index 363dd73c5f..0000000000 --- a/src/common/catalog-entities/general-catalog-entities/implementations/welcome-catalog-entity.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 { generalCatalogEntityInjectionToken } from "../general-catalog-entity-injection-token"; -import { GeneralEntity } from "../../index"; -import { buildURL } from "../../../utils/buildUrl"; -import welcomeRouteInjectable from "../../../front-end-routing/routes/welcome/welcome-route.injectable"; - -const welcomeCatalogEntityInjectable = getInjectable({ - id: "general-catalog-entity-for-welcome", - - instantiate: (di) => { - const route = di.inject(welcomeRouteInjectable); - const url = buildURL(route.path); - - return new GeneralEntity({ - metadata: { - uid: "welcome-page-entity", - name: "Welcome Page", - source: "app", - labels: {}, - }, - spec: { - path: url, - icon: { - material: "meeting_room", - background: "#3d90ce", - }, - }, - status: { - phase: "active", - }, - }); - }, - - injectionToken: generalCatalogEntityInjectionToken, -}); - -export default welcomeCatalogEntityInjectable; diff --git a/src/common/catalog-entities/general.ts b/src/common/catalog-entities/general.ts deleted file mode 100644 index e81e59cc16..0000000000 --- a/src/common/catalog-entities/general.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog"; -import type { CatalogEntityActionContext } from "../catalog/catalog-entity"; -import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; - -interface GeneralEntitySpec extends CatalogEntitySpec { - path: string; - icon?: { - material?: string; - background?: string; - }; -} - -export class GeneralEntity extends CatalogEntity { - public readonly apiVersion = "entity.k8slens.dev/v1alpha1"; - public readonly kind = "General"; - - async onRun(context: CatalogEntityActionContext) { - context.navigate(this.spec.path); - } -} - -export class GeneralCategory extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - public metadata = { - name: "General", - icon: "settings", - }; - public spec = { - group: "entity.k8slens.dev", - versions: [ - categoryVersion("v1alpha1", GeneralEntity), - ], - names: { - kind: "General", - }, - }; -} diff --git a/src/common/catalog-entities/icons/kubernetes.svg b/src/common/catalog-entities/icons/kubernetes.svg deleted file mode 100644 index b4a9068420..0000000000 --- a/src/common/catalog-entities/icons/kubernetes.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - diff --git a/src/common/catalog-entities/index.ts b/src/common/catalog-entities/index.ts deleted file mode 100644 index 336a8c0f9a..0000000000 --- a/src/common/catalog-entities/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./general"; -export * from "./kubernetes-cluster"; -export * from "./web-link"; diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts deleted file mode 100644 index 57cac07122..0000000000 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus, CatalogCategorySpec } from "../catalog"; -import { CatalogEntity, CatalogCategory, categoryVersion } from "../catalog/catalog-entity"; -import { broadcastMessage } from "../ipc"; -import { app } from "electron"; -import type { CatalogEntityConstructor, CatalogEntitySpec } from "../catalog/catalog-entity"; -import { IpcRendererNavigationEvents } from "../ipc/navigation-events"; -import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc"; -import KubeClusterCategoryIcon from "./icons/kubernetes.svg"; -import getClusterByIdInjectable from "../cluster-store/get-by-id.injectable"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; - -export interface KubernetesClusterPrometheusMetrics { - address?: { - namespace: string; - service: string; - port: number; - prefix: string; - }; - type?: string; -} - -export interface KubernetesClusterSpec extends CatalogEntitySpec { - kubeconfigPath: string; - kubeconfigContext: string; - metrics?: { - source: string; - prometheus?: KubernetesClusterPrometheusMetrics; - }; - icon?: { - // TODO: move to CatalogEntitySpec once any-entity icons are supported - src?: string; - material?: string; - background?: string; - }; - accessibleNamespaces?: string[]; -} - -export enum LensKubernetesClusterStatus { - DELETING = "deleting", - CONNECTING = "connecting", - CONNECTED = "connected", - DISCONNECTED = "disconnected", -} - -export interface KubernetesClusterMetadata extends CatalogEntityMetadata { - distro?: string; - kubeVersion?: string; -} - -/** - * @deprecated This is no longer used as it is incorrect. Other sources can add more values - */ -export type KubernetesClusterStatusPhase = "connected" | "connecting" | "disconnected" | "deleting"; - -export interface KubernetesClusterStatus extends CatalogEntityStatus { -} - -export function isKubernetesCluster(item: unknown): item is KubernetesCluster { - return item instanceof KubernetesCluster; -} - -export class KubernetesCluster< - Metadata extends KubernetesClusterMetadata = KubernetesClusterMetadata, - Status extends KubernetesClusterStatus = KubernetesClusterStatus, - Spec extends KubernetesClusterSpec = KubernetesClusterSpec, -> extends CatalogEntity { - public static readonly apiVersion: string = "entity.k8slens.dev/v1alpha1"; - public static readonly kind: string = "KubernetesCluster"; - - public readonly apiVersion = KubernetesCluster.apiVersion; - public readonly kind = KubernetesCluster.kind; - - async connect(): Promise { - if (app) { - const di = getLegacyGlobalDiForExtensionApi(); - const getClusterById = di.inject(getClusterByIdInjectable); - - await getClusterById(this.getId())?.activate(); - } else { - await requestClusterActivation(this.getId(), false); - } - } - - async disconnect(): Promise { - if (app) { - const di = getLegacyGlobalDiForExtensionApi(); - const getClusterById = di.inject(getClusterByIdInjectable); - - getClusterById(this.getId())?.disconnect(); - } else { - await requestClusterDisconnection(this.getId(), false); - } - } - - async onRun(context: CatalogEntityActionContext) { - context.navigate(`/cluster/${this.getId()}`); - } - - onDetailsOpen(): void { - // - } - - onSettingsOpen(): void { - // - } - - onContextMenuOpen(context: CatalogEntityContextMenuContext) { - if (!this.metadata.source || this.metadata.source === "local") { - context.menuItems.push({ - title: "Settings", - icon: "settings", - onClick: () => broadcastMessage( - IpcRendererNavigationEvents.NAVIGATE_IN_APP, - `/entity/${this.getId()}/settings`, - ), - }); - } - - switch (this.status.phase) { - case LensKubernetesClusterStatus.CONNECTED: - case LensKubernetesClusterStatus.CONNECTING: - context.menuItems.push({ - title: "Disconnect", - icon: "link_off", - onClick: () => requestClusterDisconnection(this.getId()), - }); - break; - case LensKubernetesClusterStatus.DISCONNECTED: - context.menuItems.push({ - title: "Connect", - icon: "link", - onClick: () => context.navigate(`/cluster/${this.getId()}`), - }); - break; - } - } -} - -export class KubernetesClusterCategory extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - public metadata = { - name: "Clusters", - icon: KubeClusterCategoryIcon, - }; - public spec: CatalogCategorySpec = { - group: "entity.k8slens.dev", - versions: [ - categoryVersion("v1alpha1", KubernetesCluster as CatalogEntityConstructor), - ], - names: { - kind: "KubernetesCluster", - }, - }; -} diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts deleted file mode 100644 index 7c83051c8b..0000000000 --- a/src/common/catalog-entities/web-link.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; -import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; -import productNameInjectable from "../vars/product-name.injectable"; -import weblinkStoreInjectable from "../weblinks-store/weblink-store.injectable"; - -export type WebLinkStatusPhase = "available" | "unavailable"; - -export interface WebLinkStatus extends CatalogEntityStatus { - phase: WebLinkStatusPhase; -} - -export interface WebLinkSpec { - url: string; -} - -export class WebLink extends CatalogEntity { - public static readonly apiVersion = "entity.k8slens.dev/v1alpha1"; - public static readonly kind = "WebLink"; - - public readonly apiVersion = WebLink.apiVersion; - public readonly kind = WebLink.kind; - - async onRun() { - window.open(this.spec.url, "_blank"); - } - - onContextMenuOpen(context: CatalogEntityContextMenuContext) { - // NOTE: this is safe because `onContextMenuOpen` is only supposed to be called in the renderer - const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.renderer); - const productName = di.inject(productNameInjectable); - const weblinkStore = di.inject(weblinkStoreInjectable); - - if (this.metadata.source === "local") { - context.menuItems.push({ - title: "Delete", - icon: "delete", - onClick: async () => weblinkStore.removeById(this.getId()), - confirm: { - message: `Remove Web Link "${this.getName()}" from ${productName}?`, - }, - }); - } - } -} - -export class WebLinkCategory extends CatalogCategory { - public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; - public readonly kind = "CatalogCategory"; - public metadata = { - name: "Web Links", - icon: "public", - }; - public spec = { - group: "entity.k8slens.dev", - versions: [ - categoryVersion("v1alpha1", WebLink), - ], - names: { - kind: "WebLink", - }, - }; -} diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts deleted file mode 100644 index 7effe60f2f..0000000000 --- a/src/common/catalog/catalog-entity.ts +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import EventEmitter from "events"; -import type TypedEmitter from "typed-emitter"; -import { observable, makeObservable } from "mobx"; -import { once } from "lodash"; -import type { Disposer } from "../utils"; -import { iter } from "../utils"; -import type { CategoryColumnRegistration } from "../../renderer/components/+catalog/custom-category-columns"; - -export type CatalogEntityDataFor = Entity extends CatalogEntity - ? CatalogEntityData - : never; - -export type CatalogEntityInstanceFrom = Constructor extends CatalogEntityConstructor - ? Entity - : never; - -export type CatalogEntityConstructor = ( - new (data: CatalogEntityDataFor) => Entity -); - -export interface CatalogCategoryVersion { - /** - * The specific version that the associated constructor is for. This MUST be - * a DNS label and SHOULD be of the form `vN`, `vNalphaY`, or `vNbetaY` where - * `N` and `Y` are both integers greater than 0. - * - * Examples: The following are valid values for this field. - * - `v1` - * - `v1beta1` - * - `v1alpha2` - * - `v3beta2` - */ - readonly name: string; - - /** - * The constructor for the entities. - */ - readonly entityClass: CatalogEntityConstructor; -} - -export interface CatalogCategorySpec { - /** - * The grouping for for the category. This MUST be a DNS label. - */ - readonly group: string; - - /** - * The specific versions of the constructors. - * - * NOTE: the field `.apiVersion` after construction MUST match `{.group}/{.versions.[] | .name}`. - * For example, if `group = "entity.k8slens.dev"` and there is an entry in `.versions` with - * `name = "v1alpha1"` then the resulting `.apiVersion` MUST be `entity.k8slens.dev/v1alpha1` - */ - readonly versions: CatalogCategoryVersion[]; - - /** - * This is the concerning the category - */ - readonly names: { - /** - * The kind of entity that this category is for. This value MUST be a DNS - * label and MUST be equal to the `kind` fields that are produced by the - * `.versions.[] | .entityClass` fields. - */ - readonly kind: string; - }; - - /** - * These are the columns used for displaying entities when in the catalog. - * - * If this is not provided then some default columns will be used, similar in - * scope to the columns in the "Browse" view. - * - * Even if you provide columns, a "Name" column will be provided as well with - * `priority: 0`. - * - * These columns will not be used in the "Browse" view. - */ - readonly displayColumns?: CategoryColumnRegistration[]; -} - -/** - * If the filter return a thruthy value, the menu item is displayed - */ -export type AddMenuFilter = (menu: CatalogEntityAddMenu) => any; - -export interface CatalogCategoryEvents { - /** - * This event will be emitted when the category is loaded in the catalog - * view. - */ - load: () => void; - - /** - * This event will be emitted when the catalog add menu is opened and is the - * way to added entries to that menu. - */ - catalogAddMenu: (context: CatalogEntityAddMenuContext) => void; - - /** - * This event will be emitted when the context menu for an entity is declared - * by this category is opened. - */ - contextMenuOpen: (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void; -} - -export interface CatalogCategoryMetadata { - /** - * The name of your category. The category can be searched for by this - * value. This will also be used for the catalog menu. - */ - readonly name: string; - /** - * Either an `` or the name of an icon from {@link IconProps} - */ - readonly icon: string; -} - -export function categoryVersion< - T extends CatalogEntity, - Metadata extends CatalogEntityMetadata, - Status extends CatalogEntityStatus, - Spec extends CatalogEntitySpec, ->(name: string, entityClass: new (data: CatalogEntityData) => T): CatalogCategoryVersion { - return { - name, - entityClass: entityClass as CatalogEntityConstructor, - }; -} - -export abstract class CatalogCategory extends (EventEmitter as new () => TypedEmitter) { - /** - * The version of category that you are wanting to declare. - * - * Currently supported values: - * - * - `"catalog.k8slens.dev/v1alpha1"` - */ - abstract readonly apiVersion: string; - - /** - * The kind of item you wish to declare. - * - * Currently supported values: - * - * - `"CatalogCategory"` - */ - abstract readonly kind: string; - - /** - * The data about the category itself - */ - abstract readonly metadata: CatalogCategoryMetadata; - - /** - * The most important part of a category, as it is where entity versions are declared. - */ - abstract readonly spec: CatalogCategorySpec; - - /** - * @internal - */ - protected readonly filters = observable.set([], { - deep: false, - }); - - /** - * Parse a category ID into parts. - * @param id The id of a category is parse - * @returns The group and kind parts of the ID - */ - public static parseId(id: string): { group?: string; kind?: string } { - const [group, kind] = id.split("/") ?? []; - - return { group, kind }; - } - - /** - * Get the ID of this category - */ - public getId(): string { - return `${this.spec.group}/${this.spec.names.kind}`; - } - - /** - * Get the name of this category - */ - public getName(): string { - return this.metadata.name; - } - - /** - * Get the badge of this category. - * Defaults to no badge. - * The badge is displayed next to the Category name in the Catalog Category menu - */ - public getBadge(): React.ReactNode { - return null; - } - - /** - * Add a filter for menu items of catalogAddMenu - * @param fn The function that should return a truthy value if that menu item should be displayed - * @returns A function to remove that filter - */ - public addMenuFilter(fn: AddMenuFilter): Disposer { - this.filters.add(fn); - - return once(() => void this.filters.delete(fn)); - } - - /** - * Filter menuItems according to the Category's set filters - * @param menuItems menu items to filter - * @returns filtered menu items - */ - public filteredItems(menuItems: CatalogEntityAddMenu[]) { - return Array.from( - iter.reduce( - this.filters, - iter.filter, - menuItems.values(), - ), - ); - } -} - -export type EntityMetadataObject = { [Key in string]?: EntityMetadataValue }; -export type EntityMetadataValue = string | number | boolean | EntityMetadataObject | undefined; - -export interface CatalogEntityMetadata extends EntityMetadataObject { - uid: string; - name: string; - shortName?: string; - description?: string; - source?: string; - labels: Record; -} - -export interface CatalogEntityStatus { - phase: string; - reason?: string; - - /** - * @default true - */ - enabled?: boolean; - message?: string; - active?: boolean; -} - -export interface CatalogEntityActionContext { - navigate: (url: string) => void; - setCommandPaletteContext: (context?: CatalogEntity) => void; -} - -export interface CatalogEntityContextMenu { - /** - * Menu title - */ - title: string; - /** - * Menu icon - */ - icon?: string; - /** - * OnClick handler - */ - onClick: () => void | Promise; - /** - * Confirm click with a message - */ - confirm?: { - message: string; - }; -} - -export interface CatalogEntityAddMenu extends CatalogEntityContextMenu { - icon: string; - defaultAction?: boolean; -} - -export interface CatalogEntitySettingsMenu { - group?: string; - title: string; - components: { - View: React.ComponentType; - }; -} - -export interface CatalogEntityContextMenuNavigate { - /** - * @param pathname The location to navigate to in the main iframe - */ - (pathname: string, forceMainFrame?: boolean): void; - /** - * @param pathname The location to navigate to in the current iframe. Useful for when called within the cluster frame - */ - (pathname: string, forceMainFrame: false): void; -} - -export interface CatalogEntityContextMenuContext { - /** - * Navigate to the specified pathname - */ - navigate: CatalogEntityContextMenuNavigate; - menuItems: CatalogEntityContextMenu[]; -} - -export interface CatalogEntitySettingsContext { - menuItems: CatalogEntityContextMenu[]; -} - -export interface CatalogEntityAddMenuContext { - navigate: (url: string) => void; - menuItems: CatalogEntityAddMenu[]; -} - -export type CatalogEntitySpec = Record; - - -export interface CatalogEntityData< - Metadata extends CatalogEntityMetadata = CatalogEntityMetadata, - Status extends CatalogEntityStatus = CatalogEntityStatus, - Spec extends CatalogEntitySpec = CatalogEntitySpec, -> { - metadata: Metadata; - status: Status; - spec: Spec; -} - -export interface CatalogEntityKindData { - readonly apiVersion: string; - readonly kind: string; -} - -export abstract class CatalogEntity< - Metadata extends CatalogEntityMetadata = CatalogEntityMetadata, - Status extends CatalogEntityStatus = CatalogEntityStatus, - Spec extends CatalogEntitySpec = CatalogEntitySpec, -> implements CatalogEntityKindData { - /** - * The group and version of this class. - */ - public abstract readonly apiVersion: string; - - /** - * A DNS label name of the entity. - */ - public abstract readonly kind: string; - - @observable metadata: Metadata; - @observable status: Status; - @observable spec: Spec; - - constructor({ metadata, status, spec }: CatalogEntityData) { - makeObservable(this); - - if (!metadata || typeof metadata !== "object") { - throw new TypeError("CatalogEntity's metadata must be a defined object"); - } - - if (!status || typeof status !== "object") { - throw new TypeError("CatalogEntity's status must be a defined object"); - } - - if (!spec || typeof spec !== "object") { - throw new TypeError("CatalogEntity's spec must be a defined object"); - } - - this.metadata = metadata; - this.status = status; - this.spec = spec; - } - - /** - * Get the UID of this entity - */ - public getId(): string { - return this.metadata.uid; - } - - /** - * Get the name of this entity - */ - public getName(): string { - return this.metadata.name; - } - - /** - * Get the specified source of this entity, defaulting to `"unknown"` if not - * provided - */ - public getSource(): string { - return this.metadata.source ?? "unknown"; - } - - /** - * Get if this entity is enabled. - */ - public isEnabled(): boolean { - return this.status.enabled ?? true; - } - - public onRun?(context: CatalogEntityActionContext): void | Promise; - public onContextMenuOpen?(context: CatalogEntityContextMenuContext): void | Promise; - public onSettingsOpen?(context: CatalogEntitySettingsContext): void | Promise; -} diff --git a/src/common/catalog/catalog-run-event.ts b/src/common/catalog/catalog-run-event.ts deleted file mode 100644 index c1b5eac986..0000000000 --- a/src/common/catalog/catalog-run-event.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CatalogEntity } from "../catalog"; - -export class CatalogRunEvent { - #defaultPrevented: boolean; - #target: CatalogEntity; - - get defaultPrevented() { - return this.#defaultPrevented; - } - - get target() { - return this.#target; - } - - constructor({ target }: { target: CatalogEntity }) { - this.#defaultPrevented = false; - this.#target = target; - } - - preventDefault() { - this.#defaultPrevented = true; - } -} diff --git a/src/common/catalog/categories/general.injectable.ts b/src/common/catalog/categories/general.injectable.ts deleted file mode 100644 index d2e3ba7a69..0000000000 --- a/src/common/catalog/categories/general.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { GeneralCategory } from "../../catalog-entities"; -import { builtInCategoryInjectionToken } from "../category-registry.injectable"; - -const generalCategoryInjectable = getInjectable({ - id: "general-category", - instantiate: () => new GeneralCategory(), - injectionToken: builtInCategoryInjectionToken, -}); - -export default generalCategoryInjectable; diff --git a/src/common/catalog/categories/kubernetes-cluster.injectable.ts b/src/common/catalog/categories/kubernetes-cluster.injectable.ts deleted file mode 100644 index 6dc3f9be8d..0000000000 --- a/src/common/catalog/categories/kubernetes-cluster.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { KubernetesClusterCategory } from "../../catalog-entities/kubernetes-cluster"; -import { builtInCategoryInjectionToken } from "../category-registry.injectable"; - -const kubernetesClusterCategoryInjectable = getInjectable({ - id: "kubernetes-cluster-category", - instantiate: () => new KubernetesClusterCategory(), - injectionToken: builtInCategoryInjectionToken, -}); - -export default kubernetesClusterCategoryInjectable; diff --git a/src/common/catalog/categories/weblink.injectable.ts b/src/common/catalog/categories/weblink.injectable.ts deleted file mode 100644 index 339758efbf..0000000000 --- a/src/common/catalog/categories/weblink.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { WebLinkCategory } from "../../catalog-entities"; -import { builtInCategoryInjectionToken } from "../category-registry.injectable"; - -const weblinkCategoryInjectable = getInjectable({ - id: "weblink-category", - instantiate: () => new WebLinkCategory(), - injectionToken: builtInCategoryInjectionToken, -}); - -export default weblinkCategoryInjectable; diff --git a/src/common/catalog/category-registry.injectable.ts b/src/common/catalog/category-registry.injectable.ts deleted file mode 100644 index a73ed973f6..0000000000 --- a/src/common/catalog/category-registry.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import type { CatalogCategory } from "./catalog-entity"; -import { CatalogCategoryRegistry } from "./category-registry"; - -export const builtInCategoryInjectionToken = getInjectionToken({ - id: "built-in-category-token", -}); - -const catalogCategoryRegistryInjectable = getInjectable({ - id: "catalog-category-registry", - instantiate: (di) => { - const registry = new CatalogCategoryRegistry(); - const categories = di.injectMany(builtInCategoryInjectionToken); - - for (const category of categories) { - registry.add(category); - } - - return registry; - }, -}); - -export default catalogCategoryRegistryInjectable; diff --git a/src/common/catalog/category-registry.ts b/src/common/catalog/category-registry.ts deleted file mode 100644 index 75c33d8a10..0000000000 --- a/src/common/catalog/category-registry.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, computed, observable, makeObservable } from "mobx"; -import { once } from "lodash"; -import { iter, getOrInsertMap, strictSet } from "../utils"; -import type { Disposer } from "../utils"; -import type { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity"; - -export type CategoryFilter = (category: CatalogCategory) => any; - -export class CatalogCategoryRegistry { - protected readonly categories = observable.set(); - protected readonly groupKinds = new Map>(); - protected readonly filters = observable.set([], { - deep: false, - }); - - constructor() { - makeObservable(this); - } - - @action add(category: CatalogCategory): Disposer { - const byGroup = getOrInsertMap(this.groupKinds, category.spec.group); - - this.categories.add(category); - strictSet(byGroup, category.spec.names.kind, category); - - return () => { - this.categories.delete(category); - byGroup.delete(category.spec.names.kind); - }; - } - - @computed get items() { - return Array.from(this.categories); - } - - @computed get filteredItems() { - return Array.from( - iter.reduce( - this.filters, - iter.filter, - this.items.values(), - ), - ); - } - - - getForGroupKind(group: string, kind: string): T | undefined { - return this.groupKinds.get(group)?.get(kind) as T; - } - - getEntityForData(data: CatalogEntityData & CatalogEntityKindData) { - const category = this.getCategoryForEntity(data); - - if (!category) { - return null; - } - - const splitApiVersion = data.apiVersion.split("/"); - const version = splitApiVersion[1]; - - const specVersion = category.spec.versions.find((v) => v.name === version); - - if (!specVersion) { - return null; - } - - return new specVersion.entityClass(data); - } - - hasCategoryForEntity({ kind, apiVersion }: CatalogEntityData & CatalogEntityKindData): boolean { - const splitApiVersion = apiVersion.split("/"); - const group = splitApiVersion[0]; - - return this.groupKinds.get(group)?.has(kind) ?? false; - } - - getCategoryForEntity(data: CatalogEntityData & CatalogEntityKindData): T | undefined { - const splitApiVersion = data.apiVersion.split("/"); - const group = splitApiVersion[0]; - - return this.getForGroupKind(group, data.kind); - } - - getByName(name: string) { - return this.items.find(category => category.metadata?.name == name); - } - - /** - * Add a new filter to the set of category filters - * @param fn The function that should return a truthy value if that category should be displayed - * @returns A function to remove that filter - */ - addCatalogCategoryFilter(fn: CategoryFilter): Disposer { - this.filters.add(fn); - - return once(() => void this.filters.delete(fn)); - } -} diff --git a/src/common/catalog/filtered-categories.injectable.ts b/src/common/catalog/filtered-categories.injectable.ts deleted file mode 100644 index c84d527ff2..0000000000 --- a/src/common/catalog/filtered-categories.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 catalogCategoryRegistryInjectable from "./category-registry.injectable"; - -const filteredCategoriesInjectable = getInjectable({ - id: "filtered-categories", - instantiate: (di) => { - const registry = di.inject(catalogCategoryRegistryInjectable); - - return computed(() => [...registry.filteredItems]); - }, -}); - -export default filteredCategoriesInjectable; diff --git a/src/common/catalog/has-category-for-entity.injectable.ts b/src/common/catalog/has-category-for-entity.injectable.ts deleted file mode 100644 index cff7d720c1..0000000000 --- a/src/common/catalog/has-category-for-entity.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { CatalogEntityData, CatalogEntityKindData } from "./catalog-entity"; -import catalogCategoryRegistryInjectable from "./category-registry.injectable"; - -export type HasCategoryForEntity = (data: CatalogEntityData & CatalogEntityKindData) => boolean; - -const hasCategoryForEntityInjectable = getInjectable({ - id: "has-category-for-entity", - - instantiate: (di): HasCategoryForEntity => { - const registry = di.inject(catalogCategoryRegistryInjectable); - - return (data) => registry.hasCategoryForEntity(data); - }, -}); - -export default hasCategoryForEntityInjectable; diff --git a/src/common/catalog/index.ts b/src/common/catalog/index.ts deleted file mode 100644 index 4964dada6a..0000000000 --- a/src/common/catalog/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./category-registry"; -export * from "./catalog-entity"; diff --git a/src/common/catalog/visit-entity-context-menu.injectable.ts b/src/common/catalog/visit-entity-context-menu.injectable.ts deleted file mode 100644 index eb1a2abeba..0000000000 --- a/src/common/catalog/visit-entity-context-menu.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 { CatalogEntity, CatalogEntityContextMenuContext } from "./catalog-entity"; -import catalogCategoryRegistryInjectable from "./category-registry.injectable"; - -export type VisitEntityContextMenu = (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void; - -const visitEntityContextMenuInjectable = getInjectable({ - id: "visit-entity-context-menu", - instantiate: (di): VisitEntityContextMenu => { - const categoryRegistry = di.inject(catalogCategoryRegistryInjectable); - - return (entity, context) => { - entity.onContextMenuOpen?.(context); - categoryRegistry.getCategoryForEntity(entity)?.emit("contextMenuOpen", entity, context); - }; - }, -}); - -export default visitEntityContextMenuInjectable; diff --git a/src/common/certificate-authorities/inject-system-cas.injectable.ts b/src/common/certificate-authorities/inject-system-cas.injectable.ts deleted file mode 100644 index f75dcd6c70..0000000000 --- a/src/common/certificate-authorities/inject-system-cas.injectable.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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 { globalAgent } from "https"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -// DST Root CA X3, which was expired on 9.30.2021 -const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n"; - -function isCertActive(cert: string) { - const isExpired = typeof cert !== "string" || cert.includes(DSTRootCAX3); - - return !isExpired; -} - -const injectSystemCAsInjectable = getInjectable({ - id: "inject-system-cas", - instantiate: (di) => { - const requestSystemCAs = di.inject(requestSystemCAsInjectionToken); - - return async () => { - const certs = await requestSystemCAs(); - - if (certs.length === 0) { - // Leave the global option alone - return; - } - - const cas = (() => { - if (Array.isArray(globalAgent.options.ca)) { - return globalAgent.options.ca; - } - - if (globalAgent.options.ca) { - return [globalAgent.options.ca]; - } - - return []; - })(); - - for (const cert of certs) { - if (!isCertActive(cert)) { - continue; - } - - if (!cas.includes(cert)) { - cas.push(cert); - } - } - - globalAgent.options.ca = cas; - }; - }, -}); - -export default injectSystemCAsInjectable; - diff --git a/src/common/certificate-authorities/request-system-cas-token.ts b/src/common/certificate-authorities/request-system-cas-token.ts deleted file mode 100644 index c69b0bd8b0..0000000000 --- a/src/common/certificate-authorities/request-system-cas-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const requestSystemCAsInjectionToken = getInjectionToken<() => Promise>({ - id: "request-system-cas-token", -}); diff --git a/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts b/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts deleted file mode 100644 index c471c954e4..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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 execFileInjectable from "../fs/exec-file.injectable"; -import loggerInjectable from "../logger.injectable"; -import type { AsyncResult } from "../utils/async-result"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions -const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const execFile = di.inject(execFileInjectable); - const logger = di.inject(loggerInjectable); - - const execSecurity = async (...args: string[]): Promise> => { - const result = await execFile("/usr/bin/security", args); - - if (!result.callWasSuccessful) { - return { - callWasSuccessful: false, - error: result.error.stderr || result.error.message, - }; - } - - return { - callWasSuccessful: true, - response: result.response.split(certSplitPattern), - }; - }; - - return async () => { - const [trustedResult, rootCAResult] = await Promise.all([ - execSecurity("find-certificate", "-a", "-p"), - execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), - ]); - - if (!trustedResult.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving trusted CAs: ${trustedResult.error}`); - } else if (!rootCAResult.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving root CAs: ${rootCAResult.error}`); - } else { - return [...new Set([...trustedResult.response, ...rootCAResult.response])]; - } - - return []; - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.linux.ts b/src/common/certificate-authorities/request-system-cas.injectable.linux.ts deleted file mode 100644 index 1d7bf10350..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.linux.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts b/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts deleted file mode 100644 index 1d7bf10350..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.win32.ts b/src/common/certificate-authorities/request-system-cas.injectable.win32.ts deleted file mode 100644 index 4940aa2a7b..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.win32.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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 execFileInjectable from "../fs/exec-file.injectable"; -import loggerInjectable from "../logger.injectable"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const pemEncoding = (hexEncodedCert: String) => { - const certData = Buffer.from(hexEncodedCert, "hex").toString("base64"); - const lines = ["-----BEGIN CERTIFICATE-----"]; - - for (let i = 0; i < certData.length; i += 64) { - lines.push(certData.substring(i, i + 64)); - } - - lines.push("-----END CERTIFICATE-----", ""); - - return lines.join("\r\n"); -}; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const wincaRootsExePath: string = __non_webpack_require__.resolve("win-ca/lib/roots.exe"); - const execFile = di.inject(execFileInjectable); - const logger = di.inject(loggerInjectable); - - return async () => { - /** - * This needs to be done manually because for some reason calling the api from "win-ca" - * directly fails to load "child_process" correctly on renderer - */ - const result = await execFile(wincaRootsExePath, { - maxBuffer: 128 * 1024 * 1024, // 128 MiB - }); - - if (!result.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving CAs`, result.error); - - return []; - } - - return result - .response - .split("\r\n") - .filter(Boolean) - .map(pemEncoding); - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate/lens-proxy-certificate-channel.ts b/src/common/certificate/lens-proxy-certificate-channel.ts deleted file mode 100644 index 7d9652ce5c..0000000000 --- a/src/common/certificate/lens-proxy-certificate-channel.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { SelfSignedCert } from "selfsigned"; -import { getRequestChannel } from "../utils/channel/get-request-channel"; - -export const lensProxyCertificateChannel = getRequestChannel("request-lens-proxy-certificate"); diff --git a/src/common/certificate/lens-proxy-certificate.global-override-for-injectable.ts b/src/common/certificate/lens-proxy-certificate.global-override-for-injectable.ts deleted file mode 100644 index d547516062..0000000000 --- a/src/common/certificate/lens-proxy-certificate.global-override-for-injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import lensProxyCertificateInjectable from "./lens-proxy-certificate.injectable"; - -export default getGlobalOverride(lensProxyCertificateInjectable, () => { - return { - get: () => ({ - public: "", - private: "", - cert: "", - }), - set: () => {}, - }; -}); - diff --git a/src/common/certificate/lens-proxy-certificate.injectable.ts b/src/common/certificate/lens-proxy-certificate.injectable.ts deleted file mode 100644 index b5e00e0094..0000000000 --- a/src/common/certificate/lens-proxy-certificate.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 { SelfSignedCert } from "selfsigned"; - -const lensProxyCertificateInjectable = getInjectable({ - id: "lens-proxy-certificate", - instantiate: () => { - let certState: SelfSignedCert; - const cert = { - get: () => { - if (!certState) { - throw "certificate has not been set"; - } - - return certState; - }, - set: (certificate: SelfSignedCert) => { - if (certState) { - throw "certificate has already been set"; - } - - certState = certificate; - }, - }; - - return cert; - }, -}); - -export default lensProxyCertificateInjectable; diff --git a/src/common/cluster-frames.injectable.ts b/src/common/cluster-frames.injectable.ts deleted file mode 100644 index 23897012a0..0000000000 --- a/src/common/cluster-frames.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { clusterFrameMap } from "./cluster-frames"; - -const clusterFramesInjectable = getInjectable({ - id: "cluster-frames", - instantiate: () => clusterFrameMap, - causesSideEffects: true, -}); - -export default clusterFramesInjectable; diff --git a/src/common/cluster-frames.ts b/src/common/cluster-frames.ts deleted file mode 100644 index 6a4757e68a..0000000000 --- a/src/common/cluster-frames.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; - -export interface ClusterFrameInfo { - frameId: number; - processId: number; -} - -export const clusterFrameMap = observable.map(); diff --git a/src/common/cluster-store/allowed-resources-injection-token.ts b/src/common/cluster-store/allowed-resources-injection-token.ts deleted file mode 100644 index 5b71038d04..0000000000 --- a/src/common/cluster-store/allowed-resources-injection-token.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import type { KubeApiResourceDescriptor } from "../rbac"; - -export const shouldShowResourceInjectionToken = getInjectionToken, KubeApiResourceDescriptor>({ - id: "should-show-resource", -}); diff --git a/src/common/cluster-store/cluster-store.injectable.ts b/src/common/cluster-store/cluster-store.injectable.ts deleted file mode 100644 index 9712e3fdb0..0000000000 --- a/src/common/cluster-store/cluster-store.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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 { ClusterStore } from "./cluster-store"; -import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token"; -import readClusterConfigSyncInjectable from "./read-cluster-config.injectable"; -import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { clusterStoreMigrationInjectionToken } from "./migration-token"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; - -const clusterStoreInjectable = getInjectable({ - id: "cluster-store", - - instantiate: (di) => new ClusterStore({ - createCluster: di.inject(createClusterInjectionToken), - readClusterConfigSync: di.inject(readClusterConfigSyncInjectable), - emitAppEvent: di.inject(emitAppEventInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, clusterStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default clusterStoreInjectable; diff --git a/src/common/cluster-store/cluster-store.ts b/src/common/cluster-store/cluster-store.ts deleted file mode 100644 index 20929cf77e..0000000000 --- a/src/common/cluster-store/cluster-store.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -import { action, comparer, computed, makeObservable, observable } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { Cluster } from "../cluster/cluster"; -import { toJS } from "../utils"; -import type { ClusterModel, ClusterId } from "../cluster-types"; -import type { CreateCluster } from "../cluster/create-cluster-injection-token"; -import type { ReadClusterConfigSync } from "./read-cluster-config.injectable"; -import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable"; - -export interface ClusterStoreModel { - clusters?: ClusterModel[]; -} - -interface Dependencies extends BaseStoreDependencies { - createCluster: CreateCluster; - readClusterConfigSync: ReadClusterConfigSync; - emitAppEvent: EmitAppEvent; -} - -export class ClusterStore extends BaseStore { - readonly clusters = observable.map(); - - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-cluster-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - - makeObservable(this); - } - - @computed get clustersList(): Cluster[] { - return Array.from(this.clusters.values()); - } - - @computed get connectedClustersList(): Cluster[] { - return this.clustersList.filter((c) => !c.disconnected); - } - - hasClusters() { - return this.clusters.size > 0; - } - - getById(id: ClusterId | undefined): Cluster | undefined { - if (id) { - return this.clusters.get(id); - } - - return undefined; - } - - addCluster(clusterOrModel: ClusterModel | Cluster): Cluster { - this.dependencies.emitAppEvent({ name: "cluster", action: "add" }); - - const cluster = clusterOrModel instanceof Cluster - ? clusterOrModel - : this.dependencies.createCluster( - clusterOrModel, - this.dependencies.readClusterConfigSync(clusterOrModel), - ); - - this.clusters.set(cluster.id, cluster); - - return cluster; - } - - @action - protected fromStore({ clusters = [] }: ClusterStoreModel = {}) { - const currentClusters = new Map(this.clusters); - const newClusters = new Map(); - - // update new clusters - for (const clusterModel of clusters) { - try { - let cluster = currentClusters.get(clusterModel.id); - - if (cluster) { - cluster.updateModel(clusterModel); - } else { - cluster = this.dependencies.createCluster( - clusterModel, - this.dependencies.readClusterConfigSync(clusterModel), - ); - } - newClusters.set(clusterModel.id, cluster); - } catch (error) { - this.dependencies.logger.warn(`[CLUSTER-STORE]: Failed to update/create a cluster: ${error}`); - } - } - - this.clusters.replace(newClusters); - } - - toJSON(): ClusterStoreModel { - return toJS({ - clusters: this.clustersList.map(cluster => cluster.toJSON()), - }); - } -} diff --git a/src/common/cluster-store/get-by-id.injectable.ts b/src/common/cluster-store/get-by-id.injectable.ts deleted file mode 100644 index 534bdb5e76..0000000000 --- a/src/common/cluster-store/get-by-id.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { ClusterId } from "../cluster-types"; -import type { Cluster } from "../cluster/cluster"; -import clusterStoreInjectable from "./cluster-store.injectable"; - -export type GetClusterById = (id: ClusterId) => Cluster | undefined; - -const getClusterByIdInjectable = getInjectable({ - id: "get-cluster-by-id", - instantiate: (di): GetClusterById => { - const store = di.inject(clusterStoreInjectable); - - return (id) => store.getById(id); - }, -}); - -export default getClusterByIdInjectable; diff --git a/src/common/cluster-store/migration-token.ts b/src/common/cluster-store/migration-token.ts deleted file mode 100644 index 86489509a2..0000000000 --- a/src/common/cluster-store/migration-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; - -export const clusterStoreMigrationInjectionToken = getInjectionToken({ - id: "cluster-store-migration", -}); diff --git a/src/common/cluster-store/read-cluster-config.injectable.ts b/src/common/cluster-store/read-cluster-config.injectable.ts deleted file mode 100644 index 9fea013b16..0000000000 --- a/src/common/cluster-store/read-cluster-config.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 { ClusterConfigData, ClusterModel } from "../cluster-types"; -import readFileSyncInjectable from "../fs/read-file-sync.injectable"; -import { loadConfigFromString, validateKubeConfig } from "../kube-helpers"; - -export type ReadClusterConfigSync = (model: ClusterModel) => ClusterConfigData; - -const readClusterConfigSyncInjectable = getInjectable({ - id: "read-cluster-config-sync", - instantiate: (di): ReadClusterConfigSync => { - const readFileSync = di.inject(readFileSyncInjectable); - - return ({ kubeConfigPath, contextName }) => { - const kubeConfigData = readFileSync(kubeConfigPath); - const { config } = loadConfigFromString(kubeConfigData); - const result = validateKubeConfig(config, contextName); - - if (result.error) { - throw result.error; - } - - return { clusterServerUrl: result.cluster.server }; - }; - }, -}); - -export default readClusterConfigSyncInjectable; diff --git a/src/common/cluster-types.ts b/src/common/cluster-types.ts deleted file mode 100644 index 0cd447f0e2..0000000000 --- a/src/common/cluster-types.ts +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import Joi from "joi"; - -/** - * JSON serializable metadata type - */ -export type ClusterMetadata = Record; - -/** - * Metadata for cluster's prometheus settings - */ -export interface ClusterPrometheusMetadata { - success?: boolean; - provider?: string; - autoDetected?: boolean; -} - -/** - * A ClusterId is an opaque string - */ -export type ClusterId = string; - -/** - * The fields that are used for updating a cluster instance - */ -export type UpdateClusterModel = Omit; - -/** - * A type validator for `UpdateClusterModel` so that only expected types are present - */ -export const updateClusterModelChecker = Joi.object({ - kubeConfigPath: Joi.string() - .required() - .min(1), - contextName: Joi.string() - .required() - .min(1), - workspace: Joi.string() - .optional(), - workspaces: Joi.array() - .items(Joi.string()), - preferences: Joi.object(), - metadata: Joi.object(), - accessibleNamespaces: Joi.array() - .items(Joi.string()), - labels: Joi.object().pattern(Joi.string(), Joi.string()), -}); - -/** - * A type validator for just the `id` fields of `ClusterModel`. The rest is - * covered by `updateClusterModelChecker` - */ -export const clusterModelIdChecker = Joi.object>({ - id: Joi.string() - .required() - .min(1), -}); - -/** - * The model for passing cluster data around, including to disk - */ -export interface ClusterModel { - /** Unique id for a cluster */ - id: ClusterId; - - /** Path to cluster kubeconfig */ - kubeConfigPath: string; - - /** - * Workspace id - * - * @deprecated - */ - workspace?: string; - - /** - * @deprecated this is used only for hotbar migrations from 4.2.X - */ - workspaces?: string[]; - - /** User context in kubeconfig */ - contextName: string; - - /** Preferences */ - preferences?: ClusterPreferences; - - /** Metadata */ - metadata?: ClusterMetadata; - - /** List of accessible namespaces */ - accessibleNamespaces?: string[]; - - /** - * Labels for the catalog entity - */ - labels?: Record; -} - -/** - * This data is retreived from the kubeconfig file before calling the cluster constructor. - * - * That is done to remove the external dependency on the construction of Cluster instances. - */ -export interface ClusterConfigData { - clusterServerUrl: string; -} - -/** - * The complete set of cluster settings or preferences - */ -export interface ClusterPreferences extends ClusterPrometheusPreferences { - terminalCWD?: string; - clusterName?: string; - iconOrder?: number; - /** - * The src for the cluster. If set to `null` that means that it was - * cleared by preferences. - */ - icon?: string | null; - httpsProxy?: string; - hiddenMetrics?: string[]; - nodeShellImage?: string; - imagePullSecret?: string; - defaultNamespace?: string; -} - -/** - * A cluster's prometheus settings (a subset of cluster settings) - */ -export interface ClusterPrometheusPreferences { - prometheus?: { - namespace: string; - service: string; - port: number; - prefix: string; - }; - prometheusProvider?: { - type: string; - }; -} - -/** - * The options for the status of connection attempts to a cluster - */ -export enum ClusterStatus { - AccessGranted = 2, - AccessDenied = 1, - Offline = 0, -} - -/** - * The message format for the "cluster::connection-update" channels - */ -export interface KubeAuthUpdate { - message: string; - isError: boolean; -} - -/** - * The OpenLens known static metadata keys - */ -export enum ClusterMetadataKey { - VERSION = "version", - CLUSTER_ID = "id", - DISTRIBUTION = "distribution", - NODES_COUNT = "nodes", - LAST_SEEN = "lastSeen", - PROMETHEUS = "prometheus", -} - -/** - * A shorthand enum for resource types that have metrics attached to them via OpenLens metrics stack - */ -export enum ClusterMetricsResourceType { - Cluster = "Cluster", - Node = "Node", - Pod = "Pod", - Deployment = "Deployment", - StatefulSet = "StatefulSet", - Container = "Container", - Ingress = "Ingress", - VolumeClaim = "VolumeClaim", - ReplicaSet = "ReplicaSet", - DaemonSet = "DaemonSet", - Job = "Job", - Namespace = "Namespace", -} - -/** - * The default node shell image - */ -export const initialNodeShellImage = "docker.io/alpine:3.13"; - -/** - * The data representing a cluster's state, for passing between main and renderer - */ -export interface ClusterState { - apiUrl: string; - online: boolean; - disconnected: boolean; - accessible: boolean; - ready: boolean; - isAdmin: boolean; - allowedNamespaces: string[]; - allowedResources: string[]; - isGlobalWatchEnabled: boolean; -} diff --git a/src/common/cluster/authorization-review.injectable.ts b/src/common/cluster/authorization-review.injectable.ts deleted file mode 100644 index 4c9b83330d..0000000000 --- a/src/common/cluster/authorization-review.injectable.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"; -import { AuthorizationV1Api } from "@kubernetes/client-node"; -import { getInjectable } from "@ogre-tools/injectable"; -import type { Logger } from "../logger"; -import loggerInjectable from "../logger.injectable"; - -/** - * Requests the permissions for actions on the kube cluster - * @param resourceAttributes The descriptor of the action that is desired to be known if it is allowed - * @returns `true` if the actions described are allowed - */ -export type CanI = (resourceAttributes: V1ResourceAttributes) => Promise; - -/** - * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster - */ -export type AuthorizationReview = (proxyConfig: KubeConfig) => CanI; - -interface Dependencies { - logger: Logger; -} - -const authorizationReview = ({ logger }: Dependencies): AuthorizationReview => { - return (proxyConfig) => { - const api = proxyConfig.makeApiClient(AuthorizationV1Api); - - return async (resourceAttributes: V1ResourceAttributes): Promise => { - try { - const { body } = await api.createSelfSubjectAccessReview({ - apiVersion: "authorization.k8s.io/v1", - kind: "SelfSubjectAccessReview", - spec: { resourceAttributes }, - }); - - return body.status?.allowed ?? false; - } catch (error) { - logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes }); - - return false; - } - }; - }; -}; - -const authorizationReviewInjectable = getInjectable({ - id: "authorization-review", - instantiate: (di) => { - const logger = di.inject(loggerInjectable); - - return authorizationReview({ logger }); - }, -}); - -export default authorizationReviewInjectable; diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts deleted file mode 100644 index 3f353dfee4..0000000000 --- a/src/common/cluster/cluster.ts +++ /dev/null @@ -1,709 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, comparer, computed, makeObservable, observable, reaction, runInAction, when } from "mobx"; -import type { ClusterContextHandler } from "../../main/context-handler/context-handler"; -import type { KubeConfig } from "@kubernetes/client-node"; -import { HttpError } from "@kubernetes/client-node"; -import type { Kubectl } from "../../main/kubectl/kubectl"; -import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig-manager"; -import type { KubeApiResource, KubeApiResourceDescriptor } from "../rbac"; -import { formatKubeApiResource } from "../rbac"; -import plimit from "p-limit"; -import type { ClusterState, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate, ClusterConfigData } from "../cluster-types"; -import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus, clusterModelIdChecker, updateClusterModelChecker } from "../cluster-types"; -import { disposer, isDefined, isRequestError, toJS } from "../utils"; -import { clusterListNamespaceForbiddenChannel } from "../ipc/cluster"; -import type { CanI } from "./authorization-review.injectable"; -import type { ListNamespaces } from "./list-namespaces.injectable"; -import assert from "assert"; -import type { Logger } from "../logger"; -import type { BroadcastMessage } from "../ipc/broadcast-message.injectable"; -import type { LoadConfigfromFile } from "../kube-helpers/load-config-from-file.injectable"; -import type { CanListResource, RequestNamespaceListPermissions, RequestNamespaceListPermissionsFor } from "./request-namespace-list-permissions.injectable"; -import type { RequestApiResources } from "../../main/cluster/request-api-resources.injectable"; -import type { DetectClusterMetadata } from "../../main/cluster-detectors/detect-cluster-metadata.injectable"; -import type { FalibleOnlyClusterMetadataDetector } from "../../main/cluster-detectors/token"; - -export interface ClusterDependencies { - readonly directoryForKubeConfigs: string; - readonly logger: Logger; - readonly clusterVersionDetector: FalibleOnlyClusterMetadataDetector; - detectClusterMetadata: DetectClusterMetadata; - createKubeconfigManager: (cluster: Cluster) => KubeconfigManager; - createContextHandler: (cluster: Cluster) => ClusterContextHandler; - createKubectl: (clusterVersion: string) => Kubectl; - createAuthorizationReview: (config: KubeConfig) => CanI; - requestApiResources: RequestApiResources; - requestNamespaceListPermissionsFor: RequestNamespaceListPermissionsFor; - createListNamespaces: (config: KubeConfig) => ListNamespaces; - broadcastMessage: BroadcastMessage; - loadConfigfromFile: LoadConfigfromFile; -} - -/** - * Cluster - * - * @beta - */ -export class Cluster implements ClusterModel { - /** Unique id for a cluster */ - public readonly id: ClusterId; - private kubeCtl: Kubectl | undefined; - /** - * Context handler - * - * @internal - */ - protected readonly _contextHandler: ClusterContextHandler | undefined; - protected readonly _proxyKubeconfigManager: KubeconfigManager | undefined; - protected readonly eventsDisposer = disposer(); - protected activated = false; - - public get contextHandler() { - // TODO: remove these once main/renderer are seperate classes - assert(this._contextHandler, "contextHandler is only defined in the main environment"); - - return this._contextHandler; - } - - protected get proxyKubeconfigManager() { - // TODO: remove these once main/renderer are seperate classes - assert(this._proxyKubeconfigManager, "proxyKubeconfigManager is only defined in the main environment"); - - return this._proxyKubeconfigManager; - } - - get whenReady() { - return when(() => this.ready); - } - - /** - * Kubeconfig context name - * - * @observable - */ - @observable contextName!: string; - /** - * Path to kubeconfig - * - * @observable - */ - @observable kubeConfigPath!: string; - /** - * @deprecated - */ - @observable workspace?: string; - /** - * @deprecated - */ - @observable workspaces?: string[]; - /** - * Kubernetes API server URL - * - * @observable - */ - @observable apiUrl: string; // cluster server url - /** - * Is cluster online - * - * @observable - */ - @observable online = false; // describes if we can detect that cluster is online - /** - * Can user access cluster resources - * - * @observable - */ - @observable accessible = false; // if user is able to access cluster resources - /** - * Is cluster instance in usable state - * - * @observable - */ - @observable ready = false; // cluster is in usable state - /** - * Is cluster currently reconnecting - * - * @observable - */ - @observable reconnecting = false; - /** - * Is cluster disconnected. False if user has selected to connect. - * - * @observable - */ - @observable disconnected = true; - /** - * Does user have admin like access - * - * @observable - */ - @observable isAdmin = false; - - /** - * Global watch-api accessibility , e.g. "/api/v1/services?watch=1" - * - * @observable - */ - @observable isGlobalWatchEnabled = false; - /** - * Preferences - * - * @observable - */ - @observable preferences: ClusterPreferences = {}; - /** - * Metadata - * - * @observable - */ - @observable metadata: ClusterMetadata = {}; - - /** - * List of allowed namespaces verified via K8S::SelfSubjectAccessReview api - */ - readonly allowedNamespaces = observable.array(); - - /** - * List of accessible namespaces provided by user in the Cluster Settings - */ - readonly accessibleNamespaces = observable.array(); - - private readonly knownResources = observable.array(); - - // The formatting of this is `group.name` or `name` (if in core) - private readonly allowedResources = observable.set(); - - /** - * Labels for the catalog entity - */ - @observable labels: Record = {}; - - /** - * Is cluster available - * - * @computed - */ - @computed get available() { - return this.accessible && !this.disconnected; - } - - /** - * Cluster name - * - * @computed - */ - @computed get name() { - return this.preferences.clusterName || this.contextName; - } - - /** - * The detected kubernetes distribution - */ - @computed get distribution(): string { - return this.metadata[ClusterMetadataKey.DISTRIBUTION]?.toString() || "unknown"; - } - - /** - * The detected kubernetes version - */ - @computed get version(): string { - return this.metadata[ClusterMetadataKey.VERSION]?.toString() || "unknown"; - } - - /** - * Prometheus preferences - * - * @computed - * @internal - */ - @computed get prometheusPreferences(): ClusterPrometheusPreferences { - const { prometheus, prometheusProvider } = this.preferences; - - return toJS({ prometheus, prometheusProvider }); - } - - /** - * defaultNamespace preference - * - * @computed - * @internal - */ - @computed get defaultNamespace(): string | undefined { - return this.preferences.defaultNamespace; - } - - constructor(private readonly dependencies: ClusterDependencies, { id, ...model }: ClusterModel, configData: ClusterConfigData) { - makeObservable(this); - - const { error } = clusterModelIdChecker.validate({ id }); - - if (error) { - throw error; - } - - this.id = id; - this.updateModel(model); - this.apiUrl = configData.clusterServerUrl; - - // for the time being, until renderer gets its own cluster type - this._contextHandler = this.dependencies.createContextHandler(this); - this._proxyKubeconfigManager = this.dependencies.createKubeconfigManager(this); - this.dependencies.logger.debug(`[CLUSTER]: Cluster init success`, { - id: this.id, - context: this.contextName, - apiUrl: this.apiUrl, - }); - } - - /** - * Update cluster data model - * - * @param model - */ - @action updateModel(model: UpdateClusterModel) { - // Note: do not assign ID as that should never be updated - - const { error } = updateClusterModelChecker.validate(model, { allowUnknown: true }); - - if (error) { - throw error; - } - - this.kubeConfigPath = model.kubeConfigPath; - this.contextName = model.contextName; - - if (model.workspace) { - this.workspace = model.workspace; - } - - if (model.workspaces) { - this.workspaces = model.workspaces; - } - - if (model.preferences) { - this.preferences = model.preferences; - } - - if (model.metadata) { - this.metadata = model.metadata; - } - - if (model.accessibleNamespaces) { - this.accessibleNamespaces.replace(model.accessibleNamespaces); - } - - if (model.labels) { - this.labels = model.labels; - } - } - - /** - * @internal - */ - protected bindEvents() { - this.dependencies.logger.info(`[CLUSTER]: bind events`, this.getMeta()); - const refreshTimer = setInterval(() => !this.disconnected && this.refresh(), 30000); // every 30s - const refreshMetadataTimer = setInterval(() => this.available && this.refreshAccessibilityAndMetadata(), 900000); // every 15 minutes - - this.eventsDisposer.push( - reaction( - () => this.prometheusPreferences, - prefs => this.contextHandler.setupPrometheus(prefs), - { equals: comparer.structural }, - ), - () => clearInterval(refreshTimer), - () => clearInterval(refreshMetadataTimer), - reaction(() => this.defaultNamespace, () => this.recreateProxyKubeconfig()), - ); - } - - /** - * @internal - */ - protected async recreateProxyKubeconfig() { - this.dependencies.logger.info("[CLUSTER]: Recreating proxy kubeconfig"); - - try { - await this.proxyKubeconfigManager.clear(); - await this.getProxyKubeconfig(); - } catch (error) { - this.dependencies.logger.error(`[CLUSTER]: failed to recreate proxy kubeconfig`, error); - } - } - - /** - * @param force force activation - * @internal - */ - @action - async activate(force = false) { - if (this.activated && !force) { - return; - } - - this.dependencies.logger.info(`[CLUSTER]: activate`, this.getMeta()); - - if (!this.eventsDisposer.length) { - this.bindEvents(); - } - - if (this.disconnected || !this.accessible) { - try { - this.broadcastConnectUpdate("Starting connection ..."); - await this.reconnect(); - } catch (error) { - this.broadcastConnectUpdate(`Failed to start connection: ${error}`, true); - - return; - } - } - - try { - this.broadcastConnectUpdate("Refreshing connection status ..."); - await this.refreshConnectionStatus(); - } catch (error) { - this.broadcastConnectUpdate(`Failed to connection status: ${error}`, true); - - return; - } - - if (this.accessible) { - try { - this.broadcastConnectUpdate("Refreshing cluster accessibility ..."); - await this.refreshAccessibility(); - } catch (error) { - this.broadcastConnectUpdate(`Failed to refresh accessibility: ${error}`, true); - - return; - } - - // download kubectl in background, so it's not blocking dashboard - this.ensureKubectl() - .catch(error => this.dependencies.logger.warn(`[CLUSTER]: failed to download kubectl for clusterId=${this.id}`, error)); - this.broadcastConnectUpdate("Connected, waiting for view to load ..."); - } - - this.activated = true; - } - - /** - * @internal - */ - async ensureKubectl() { - this.kubeCtl ??= this.dependencies.createKubectl(this.version); - - await this.kubeCtl.ensureKubectl(); - - return this.kubeCtl; - } - - /** - * @internal - */ - @action - async reconnect() { - this.dependencies.logger.info(`[CLUSTER]: reconnect`, this.getMeta()); - await this.contextHandler?.restartServer(); - this.disconnected = false; - } - - /** - * @internal - */ - @action disconnect(): void { - if (this.disconnected) { - return void this.dependencies.logger.debug("[CLUSTER]: already disconnected", { id: this.id }); - } - - this.dependencies.logger.info(`[CLUSTER]: disconnecting`, { id: this.id }); - this.eventsDisposer(); - this.contextHandler?.stopServer(); - this.disconnected = true; - this.online = false; - this.accessible = false; - this.ready = false; - this.activated = false; - this.allowedNamespaces.clear(); - this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id }); - } - - /** - * @internal - */ - @action - async refresh() { - this.dependencies.logger.info(`[CLUSTER]: refresh`, this.getMeta()); - await this.refreshConnectionStatus(); - } - - /** - * @internal - */ - @action - async refreshAccessibilityAndMetadata() { - await this.refreshAccessibility(); - await this.refreshMetadata(); - } - - /** - * @internal - */ - async refreshMetadata() { - this.dependencies.logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta()); - - const newMetadata = await this.dependencies.detectClusterMetadata(this); - - runInAction(() => { - this.metadata = { - ...this.metadata, - ...newMetadata, - }; - }); - } - - /** - * @internal - */ - private async refreshAccessibility(): Promise { - this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.getMeta()); - const proxyConfig = await this.getProxyKubeconfig(); - const canI = this.dependencies.createAuthorizationReview(proxyConfig); - const requestNamespaceListPermissions = this.dependencies.requestNamespaceListPermissionsFor(proxyConfig); - - this.isAdmin = await canI({ - namespace: "kube-system", - resource: "*", - verb: "create", - }); - this.isGlobalWatchEnabled = await canI({ - verb: "watch", - resource: "*", - }); - this.allowedNamespaces.replace(await this.requestAllowedNamespaces(proxyConfig)); - this.knownResources.replace(await this.dependencies.requestApiResources(this)); - this.allowedResources.replace(await this.getAllowedResources(requestNamespaceListPermissions)); - this.ready = true; - } - - /** - * @internal - */ - @action - async refreshConnectionStatus() { - const connectionStatus = await this.getConnectionStatus(); - - this.online = connectionStatus > ClusterStatus.Offline; - this.accessible = connectionStatus == ClusterStatus.AccessGranted; - } - - async getKubeconfig(): Promise { - const { config } = await this.dependencies.loadConfigfromFile(this.kubeConfigPath); - - return config; - } - - /** - * @internal - */ - async getProxyKubeconfig(): Promise { - const proxyKCPath = await this.getProxyKubeconfigPath(); - const { config } = await this.dependencies.loadConfigfromFile(proxyKCPath); - - return config; - } - - /** - * @internal - */ - async getProxyKubeconfigPath(): Promise { - return this.proxyKubeconfigManager.getPath(); - } - - protected async getConnectionStatus(): Promise { - try { - const versionData = await this.dependencies.clusterVersionDetector.detect(this); - - this.metadata.version = versionData.value; - - return ClusterStatus.AccessGranted; - } catch (error) { - this.dependencies.logger.error(`[CLUSTER]: Failed to connect to "${this.contextName}": ${error}`); - - if (isRequestError(error)) { - if (error.statusCode) { - if (error.statusCode >= 400 && error.statusCode < 500) { - this.broadcastConnectUpdate("Invalid credentials", true); - - return ClusterStatus.AccessDenied; - } - - const message = String(error.error || error.message) || String(error); - - this.broadcastConnectUpdate(message, true); - - return ClusterStatus.Offline; - } - - if (error.failed === true) { - if (error.timedOut === true) { - this.broadcastConnectUpdate("Connection timed out", true); - - return ClusterStatus.Offline; - } - - this.broadcastConnectUpdate("Failed to fetch credentials", true); - - return ClusterStatus.AccessDenied; - } - - const message = String(error.error || error.message) || String(error); - - this.broadcastConnectUpdate(message, true); - } else if (error instanceof Error || typeof error === "string") { - this.broadcastConnectUpdate(`${error}`, true); - } else { - this.broadcastConnectUpdate("Unknown error has occurred", true); - } - - return ClusterStatus.Offline; - } - } - - toJSON(): ClusterModel { - return toJS({ - id: this.id, - contextName: this.contextName, - kubeConfigPath: this.kubeConfigPath, - workspace: this.workspace, - workspaces: this.workspaces, - preferences: this.preferences, - metadata: this.metadata, - accessibleNamespaces: this.accessibleNamespaces, - labels: this.labels, - }); - } - - /** - * Serializable cluster-state used for sync btw main <-> renderer - */ - getState(): ClusterState { - return toJS({ - apiUrl: this.apiUrl, - online: this.online, - ready: this.ready, - disconnected: this.disconnected, - accessible: this.accessible, - isAdmin: this.isAdmin, - allowedNamespaces: this.allowedNamespaces, - allowedResources: [...this.allowedResources], - isGlobalWatchEnabled: this.isGlobalWatchEnabled, - }); - } - - /** - * @internal - * @param state cluster state - */ - @action setState(state: ClusterState) { - this.accessible = state.accessible; - this.allowedNamespaces.replace(state.allowedNamespaces); - this.allowedResources.replace(state.allowedResources); - this.apiUrl = state.apiUrl; - this.disconnected = state.disconnected; - this.isAdmin = state.isAdmin; - this.isGlobalWatchEnabled = state.isGlobalWatchEnabled; - this.online = state.online; - this.ready = state.ready; - } - - // get cluster system meta, e.g. use in "logger" - getMeta() { - return { - id: this.id, - name: this.contextName, - ready: this.ready, - online: this.online, - accessible: this.accessible, - disconnected: this.disconnected, - }; - } - - /** - * broadcast an authentication update concerning this cluster - * @internal - */ - broadcastConnectUpdate(message: string, isError = false): void { - const update: KubeAuthUpdate = { message, isError }; - - this.dependencies.logger.debug(`[CLUSTER]: broadcasting connection update`, { ...update, meta: this.getMeta() }); - this.dependencies.broadcastMessage(`cluster:${this.id}:connection-update`, update); - } - - protected async requestAllowedNamespaces(proxyConfig: KubeConfig) { - if (this.accessibleNamespaces.length) { - return this.accessibleNamespaces; - } - - try { - const listNamespaces = this.dependencies.createListNamespaces(proxyConfig); - - return await listNamespaces(); - } catch (error) { - const ctx = proxyConfig.getContextObject(this.contextName); - const namespaceList = [ctx?.namespace].filter(isDefined); - - if (namespaceList.length === 0 && error instanceof HttpError && error.statusCode === 403) { - const { response } = error as HttpError & { response: { body: unknown }}; - - this.dependencies.logger.info("[CLUSTER]: listing namespaces is forbidden, broadcasting", { clusterId: this.id, error: response.body }); - this.dependencies.broadcastMessage(clusterListNamespaceForbiddenChannel, this.id); - } - - return namespaceList; - } - } - - protected async getAllowedResources(requestNamespaceListPermissions: RequestNamespaceListPermissions) { - if (!this.allowedNamespaces.length) { - return []; - } - - try { - const apiLimit = plimit(5); // 5 concurrent api requests - const canListResourceCheckers = await Promise.all(( - this.allowedNamespaces.map(namespace => apiLimit(() => requestNamespaceListPermissions(namespace))) - )); - const canListNamespacedResource: CanListResource = (resource) => canListResourceCheckers.some(fn => fn(resource)); - - return this.knownResources - .filter(canListNamespacedResource) - .map(formatKubeApiResource); - } catch (error) { - return []; - } - } - - shouldShowResource(resource: KubeApiResourceDescriptor): boolean { - return this.allowedResources.has(formatKubeApiResource(resource)); - } - - isMetricHidden(resource: ClusterMetricsResourceType): boolean { - return Boolean(this.preferences.hiddenMetrics?.includes(resource)); - } - - get nodeShellImage(): string { - return this.preferences?.nodeShellImage || initialNodeShellImage; - } - - get imagePullSecret(): string | undefined { - return this.preferences?.imagePullSecret; - } - - isInLocalKubeconfig() { - return this.kubeConfigPath.startsWith(this.dependencies.directoryForKubeConfigs); - } -} diff --git a/src/common/cluster/create-cluster-injection-token.ts b/src/common/cluster/create-cluster-injection-token.ts deleted file mode 100644 index a07ce4459f..0000000000 --- a/src/common/cluster/create-cluster-injection-token.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { ClusterConfigData, ClusterModel } from "../cluster-types"; -import type { Cluster } from "./cluster"; - -export type CreateCluster = (model: ClusterModel, configData: ClusterConfigData) => Cluster; - -export const createClusterInjectionToken = getInjectionToken({ - id: "create-cluster-token", -}); diff --git a/src/common/cluster/current-cluster-channel.ts b/src/common/cluster/current-cluster-channel.ts deleted file mode 100644 index 957baa6f9c..0000000000 --- a/src/common/cluster/current-cluster-channel.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterId } from "../cluster-types"; -import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; - -export const currentClusterMessageChannel: MessageChannel = { - id: "current-visible-cluster", -}; diff --git a/src/common/cluster/list-namespaces.injectable.ts b/src/common/cluster/list-namespaces.injectable.ts deleted file mode 100644 index 468ff3ac2e..0000000000 --- a/src/common/cluster/list-namespaces.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { KubeConfig } from "@kubernetes/client-node"; -import { CoreV1Api } from "@kubernetes/client-node"; -import { getInjectable } from "@ogre-tools/injectable"; -import { isDefined } from "../utils"; - -export type ListNamespaces = () => Promise; - -export function listNamespaces(config: KubeConfig): ListNamespaces { - const coreApi = config.makeApiClient(CoreV1Api); - - return async () => { - const { body: { items }} = await coreApi.listNamespace(); - - return items - .map(ns => ns.metadata?.name) - .filter(isDefined); - }; -} - -const listNamespacesInjectable = getInjectable({ - id: "list-namespaces", - instantiate: () => listNamespaces, -}); - -export default listNamespacesInjectable; diff --git a/src/common/cluster/request-namespace-list-permissions.injectable.ts b/src/common/cluster/request-namespace-list-permissions.injectable.ts deleted file mode 100644 index 62d2477e42..0000000000 --- a/src/common/cluster/request-namespace-list-permissions.injectable.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeConfig } from "@kubernetes/client-node"; -import { AuthorizationV1Api } from "@kubernetes/client-node"; -import { getInjectable } from "@ogre-tools/injectable"; -import loggerInjectable from "../logger.injectable"; -import type { KubeApiResource } from "../rbac"; - -export type CanListResource = (resource: KubeApiResource) => boolean; - -/** - * Requests the permissions for actions on the kube cluster - * @param namespace The namespace of the resources - */ -export type RequestNamespaceListPermissions = (namespace: string) => Promise; - -/** - * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster - */ -export type RequestNamespaceListPermissionsFor = (proxyConfig: KubeConfig) => RequestNamespaceListPermissions; - -const requestNamespaceListPermissionsForInjectable = getInjectable({ - id: "request-namespace-list-permissions-for", - instantiate: (di): RequestNamespaceListPermissionsFor => { - const logger = di.inject(loggerInjectable); - - return (proxyConfig) => { - const api = proxyConfig.makeApiClient(AuthorizationV1Api); - - return async (namespace) => { - try { - const { body: { status }} = await api.createSelfSubjectRulesReview({ - apiVersion: "authorization.k8s.io/v1", - kind: "SelfSubjectRulesReview", - spec: { namespace }, - }); - - if (!status || status.incomplete) { - logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`); - - return () => true; - } - - const { resourceRules } = status; - - return (resource) => { - const resourceRule = resourceRules.find(({ - apiGroups = [], - resources = [], - }) => { - const isAboutRelevantApiGroup = apiGroups.includes("*") || apiGroups.includes(resource.group); - const isAboutResource = resources.includes("*") || resources.includes(resource.apiName); - - return isAboutRelevantApiGroup && isAboutResource; - }); - - if (!resourceRule) { - return false; - } - - const { verbs } = resourceRule; - - return verbs.includes("*") || verbs.includes("list"); - }; - } catch (error) { - logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error }); - - return () => true; - } - }; - }; - }, -}); - -export default requestNamespaceListPermissionsForInjectable; diff --git a/src/common/cluster/visibility-channel.ts b/src/common/cluster/visibility-channel.ts deleted file mode 100644 index 8a1a297ff2..0000000000 --- a/src/common/cluster/visibility-channel.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterId } from "../cluster-types"; -import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; - -export const clusterVisibilityChannel: MessageChannel = { - id: "cluster-visibility", -}; diff --git a/src/common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable.ts b/src/common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable.ts deleted file mode 100644 index 11a8f3f7de..0000000000 --- a/src/common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; - -const directoryForLensLocalStorageInjectable = getInjectable({ - id: "directory-for-lens-local-storage", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const directoryForUserData = di.inject(directoryForUserDataInjectable); - - return joinPaths( - directoryForUserData, - "lens-local-storage", - ); - }, -}); - -export default directoryForLensLocalStorageInjectable; diff --git a/src/common/error-reporting/initialize-sentry-reporting.global-override-for-injectable.ts b/src/common/error-reporting/initialize-sentry-reporting.global-override-for-injectable.ts deleted file mode 100644 index 1aa2934ef9..0000000000 --- a/src/common/error-reporting/initialize-sentry-reporting.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import initializeSentryReportingWithInjectable from "./initialize-sentry-reporting.injectable"; - -export default getGlobalOverride(initializeSentryReportingWithInjectable, () => () => {}); diff --git a/src/common/error-reporting/initialize-sentry-reporting.injectable.ts b/src/common/error-reporting/initialize-sentry-reporting.injectable.ts deleted file mode 100644 index 778f959739..0000000000 --- a/src/common/error-reporting/initialize-sentry-reporting.injectable.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 { ElectronMainOptions } from "@sentry/electron/main"; -import type { BrowserOptions } from "@sentry/electron/renderer"; -import isProductionInjectable from "../vars/is-production.injectable"; -import sentryDataSourceNameInjectable from "../vars/sentry-dsn-url.injectable"; -import { Dedupe, Offline } from "@sentry/integrations"; -import { inspect } from "util"; -import userStoreInjectable from "../user-store/user-store.injectable"; - -export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void; - -const mapProcessName = (type: "browser" | "renderer" | "worker") => type === "browser" ? "main" : type; - -const initializeSentryReportingWithInjectable = getInjectable({ - id: "initialize-sentry-reporting-with", - instantiate: (di): InitializeSentryReportingWith => { - const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable); - const isProduction = di.inject(isProductionInjectable); - const userStore = di.inject(userStoreInjectable); - - if (!sentryDataSourceName) { - return () => {}; - } - - return (initSentry) => initSentry({ - beforeSend: (event) => { - if (userStore.allowErrorReporting) { - return event; - } - - /** - * Directly write to stdout so that no other integrations capture this and create an infinite loop - */ - process.stdout.write(`🔒 [SENTRY-BEFORE-SEND-HOOK]: Sentry event is caught but not sent to server.`); - process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === START OF SENTRY EVENT ==="); - process.stdout.write(inspect(event, false, null, true)); - process.stdout.write("🔒 [SENTRY-BEFORE-SEND-HOOK]: === END OF SENTRY EVENT ==="); - - // if return null, the event won't be sent - // ref https://github.com/getsentry/sentry-javascript/issues/2039 - return null; - }, - dsn: sentryDataSourceName, - integrations: [ - new Dedupe(), - new Offline(), - ], - initialScope: { - tags: { - "process": mapProcessName(process.type), - }, - }, - environment: isProduction ? "production" : "development", - }); - }, - causesSideEffects: true, -}); - -export default initializeSentryReportingWithInjectable; diff --git a/src/common/event-emitter.ts b/src/common/event-emitter.ts deleted file mode 100644 index 03f8e2754b..0000000000 --- a/src/common/event-emitter.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Custom event emitter - -interface Options { - once?: boolean; // call once and remove - prepend?: boolean; // put listener to the beginning -} - -type Callback = (...data: D) => void | boolean; - -export class EventEmitter { - protected listeners: [Callback, Options][] = []; - - addListener(callback: Callback, options: Options = {}) { - const fn = options.prepend ? "unshift" : "push"; - - this.listeners[fn]([callback, options]); - } - - removeListener(callback: Callback) { - this.listeners = this.listeners.filter(([cb]) => cb !== callback); - } - - removeAllListeners() { - this.listeners.length = 0; - } - - emit(...data: D) { - for (const [callback, { once }] of this.listeners) { - if (once) { - this.removeListener(callback); - } - - if (callback(...data) === false) { - break; - } - } - } -} diff --git a/src/common/fetch/download-binary.injectable.ts b/src/common/fetch/download-binary.injectable.ts deleted file mode 100644 index 27ef43d59b..0000000000 --- a/src/common/fetch/download-binary.injectable.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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 { RequestInit, Response } from "node-fetch"; -import type { AsyncResult } from "../utils/async-result"; -import fetchInjectable from "./fetch.injectable"; - -export interface DownloadBinaryOptions { - signal?: AbortSignal | null | undefined; -} - -export type DownloadBinary = (url: string, opts?: DownloadBinaryOptions) => Promise>; - -const downloadBinaryInjectable = getInjectable({ - id: "download-binary", - instantiate: (di): DownloadBinary => { - const fetch = di.inject(fetchInjectable); - - return async (url, opts) => { - let result: Response; - - try { - // TODO: upgrade node-fetch once we switch to ESM - result = await fetch(url, opts as RequestInit); - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } - - if (result.status < 200 || 300 <= result.status) { - return { - callWasSuccessful: false, - error: result.statusText, - }; - } - - try { - return { - callWasSuccessful: true, - response: await result.buffer(), - }; - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } - }; - }, -}); - -export default downloadBinaryInjectable; diff --git a/src/common/fetch/download-json/impl.ts b/src/common/fetch/download-json/impl.ts deleted file mode 100644 index 9faf9af124..0000000000 --- a/src/common/fetch/download-json/impl.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AsyncResult } from "../../utils/async-result"; -import type { Fetch } from "../fetch.injectable"; -import type { RequestInit, Response } from "node-fetch"; - -export interface DownloadJsonOptions { - signal?: AbortSignal | null | undefined; -} - -export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise>; - -export const downloadJsonWith = (fetch: Fetch): DownloadJson => async (url, opts) => { - let result: Response; - - try { - result = await fetch(url, opts as RequestInit); - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } - - if (result.status < 200 || 300 <= result.status) { - return { - callWasSuccessful: false, - error: result.statusText, - }; - } - - try { - return { - callWasSuccessful: true, - response: await result.json(), - }; - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } -}; - diff --git a/src/common/fetch/download-json/normal.injectable.ts b/src/common/fetch/download-json/normal.injectable.ts deleted file mode 100644 index adb5e35d82..0000000000 --- a/src/common/fetch/download-json/normal.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 fetchInjectable from "../fetch.injectable"; -import { downloadJsonWith } from "./impl"; - -const downloadJsonInjectable = getInjectable({ - id: "download-json", - instantiate: (di) => downloadJsonWith(di.inject(fetchInjectable)), -}); - -export default downloadJsonInjectable; diff --git a/src/common/fetch/download-json/proxy.injectable.ts b/src/common/fetch/download-json/proxy.injectable.ts deleted file mode 100644 index 46268d4ddb..0000000000 --- a/src/common/fetch/download-json/proxy.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 proxyFetchInjectable from "../proxy-fetch.injectable"; -import { downloadJsonWith } from "./impl"; - -const proxyDownloadJsonInjectable = getInjectable({ - id: "proxy-download-json", - instantiate: (di) => downloadJsonWith(di.inject(proxyFetchInjectable)), -}); - -export default proxyDownloadJsonInjectable; diff --git a/src/common/fetch/fetch-module.injectable.ts b/src/common/fetch/fetch-module.injectable.ts deleted file mode 100644 index 444333f196..0000000000 --- a/src/common/fetch/fetch-module.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 * as FetchModule from "node-fetch"; - -const { NodeFetch } = require("../../../build/webpack/node-fetch.bundle") as { NodeFetch: typeof FetchModule }; - -/** - * NOTE: while using this module can cause side effects, this specific injectable is not marked as - * such since sometimes the request can be wholely within the perview of unit test - */ -const nodeFetchModuleInjectable = getInjectable({ - id: "node-fetch-module", - instantiate: () => NodeFetch, -}); - -export default nodeFetchModuleInjectable; diff --git a/src/common/fetch/fetch.global-override-for-injectable.ts b/src/common/fetch/fetch.global-override-for-injectable.ts deleted file mode 100644 index 1a5f80735c..0000000000 --- a/src/common/fetch/fetch.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; -import fetchInjectable from "./fetch.injectable"; - -export default getGlobalOverrideForFunction(fetchInjectable); diff --git a/src/common/fetch/fetch.injectable.ts b/src/common/fetch/fetch.injectable.ts deleted file mode 100644 index d4f51efe0d..0000000000 --- a/src/common/fetch/fetch.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { RequestInit, Response } from "node-fetch"; -import nodeFetchModuleInjectable from "./fetch-module.injectable"; - -export type Fetch = (url: string, init?: RequestInit) => Promise; - -const fetchInjectable = getInjectable({ - id: "fetch", - instantiate: (di): Fetch => { - const { default: fetch } = di.inject(nodeFetchModuleInjectable); - - return (url, init) => fetch(url, init); - }, - causesSideEffects: true, -}); - -export default fetchInjectable; diff --git a/src/common/fetch/lens-fetch.global-override-for-injectable.ts b/src/common/fetch/lens-fetch.global-override-for-injectable.ts deleted file mode 100644 index 0bc144b1be..0000000000 --- a/src/common/fetch/lens-fetch.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; -import lensFetchInjectable from "./lens-fetch.injectable"; - -export default getGlobalOverrideForFunction(lensFetchInjectable); diff --git a/src/common/fetch/lens-fetch.injectable.ts b/src/common/fetch/lens-fetch.injectable.ts deleted file mode 100644 index a90818e6cb..0000000000 --- a/src/common/fetch/lens-fetch.injectable.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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 { Agent } from "https"; -import type { RequestInit, Response } from "node-fetch"; -import lensProxyPortInjectable from "../../main/lens-proxy/lens-proxy-port.injectable"; -import lensProxyCertificateInjectable from "../certificate/lens-proxy-certificate.injectable"; -import nodeFetchModuleInjectable from "./fetch-module.injectable"; - -export type LensRequestInit = Omit; - -export type LensFetch = (pathnameAndQuery: string, init?: LensRequestInit) => Promise; - -const lensFetchInjectable = getInjectable({ - id: "lens-fetch", - instantiate: (di): LensFetch => { - const { default: fetch } = di.inject(nodeFetchModuleInjectable); - const lensProxyPort = di.inject(lensProxyPortInjectable); - const lensProxyCertificate = di.inject(lensProxyCertificateInjectable); - - return async (pathnameAndQuery, init = {}) => { - const agent = new Agent({ - ca: lensProxyCertificate.get().cert, - }); - - return fetch(`https://127.0.0.1:${lensProxyPort.get()}${pathnameAndQuery}`, { - ...init, - agent, - }); - }; - }, - causesSideEffects: true, -}); - -export default lensFetchInjectable; diff --git a/src/common/fetch/proxy-fetch.injectable.ts b/src/common/fetch/proxy-fetch.injectable.ts deleted file mode 100644 index f13842c410..0000000000 --- a/src/common/fetch/proxy-fetch.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 { HttpsProxyAgent } from "hpagent"; -import userStoreInjectable from "../user-store/user-store.injectable"; -import type { Fetch } from "./fetch.injectable"; -import fetchInjectable from "./fetch.injectable"; - -const proxyFetchInjectable = getInjectable({ - id: "proxy-fetch", - instantiate: (di): Fetch => { - const fetch = di.inject(fetchInjectable); - const { httpsProxy, allowUntrustedCAs } = di.inject(userStoreInjectable); - const agent = httpsProxy - ? new HttpsProxyAgent({ - proxy: httpsProxy, - rejectUnauthorized: !allowUntrustedCAs, - }) - : undefined; - - return (url, init = {}) => fetch(url, { - agent, - ...init, - }); - }, -}); - -export default proxyFetchInjectable; diff --git a/src/common/fetch/timeout-controller.ts b/src/common/fetch/timeout-controller.ts deleted file mode 100644 index 702becdc9d..0000000000 --- a/src/common/fetch/timeout-controller.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Creates an AbortController with an associated timeout - * @param timeout The number of milliseconds before this controller will auto abort - */ -export function withTimeout(timeout: number): AbortController { - const controller = new AbortController(); - const id = setTimeout(() => controller.abort(), timeout); - - controller.signal.addEventListener("abort", () => clearTimeout(id)); - - return controller; -} diff --git a/src/common/front-end-routing/app-navigation-channel.ts b/src/common/front-end-routing/app-navigation-channel.ts deleted file mode 100644 index b3d8f02c66..0000000000 --- a/src/common/front-end-routing/app-navigation-channel.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { IpcRendererNavigationEvents } from "../ipc/navigation-events"; -import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; - -export type AppNavigationChannel = MessageChannel; - -export const appNavigationChannel: AppNavigationChannel = { - id: IpcRendererNavigationEvents.NAVIGATE_IN_APP, -}; diff --git a/src/common/front-end-routing/cluster-frame-navigation-channel.ts b/src/common/front-end-routing/cluster-frame-navigation-channel.ts deleted file mode 100644 index 2439f523e2..0000000000 --- a/src/common/front-end-routing/cluster-frame-navigation-channel.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { IpcRendererNavigationEvents } from "../ipc/navigation-events"; -import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; - -export type ClusterFrameNavigationChannel = MessageChannel; - -export const clusterFrameNavigationChannel: ClusterFrameNavigationChannel = { - id: IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER, -}; diff --git a/src/common/front-end-routing/front-end-route-injection-token.ts b/src/common/front-end-routing/front-end-route-injection-token.ts deleted file mode 100644 index cf1f9ed87e..0000000000 --- a/src/common/front-end-routing/front-end-route-injection-token.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import type { LensRendererExtension } from "../../extensions/lens-renderer-extension"; - -export const frontEndRouteInjectionToken = getInjectionToken>({ - id: "front-end-route-injection-token", -}); - -export interface Route { - path: string; - clusterFrame: boolean; - isEnabled: IComputedValue; - extension?: LensRendererExtension; - - readonly parameterSignature?: TParameter; -} diff --git a/src/common/front-end-routing/navigate-to-front-page.injectable.ts b/src/common/front-end-routing/navigate-to-front-page.injectable.ts deleted file mode 100644 index a48eee6c04..0000000000 --- a/src/common/front-end-routing/navigate-to-front-page.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 navigateToCatalogInjectable from "./routes/catalog/navigate-to-catalog.injectable"; - -const navigateToFrontPageInjectable = getInjectable({ - id: "navigate-to-front-page", - instantiate: (di) => di.inject(navigateToCatalogInjectable), -}); - -export default navigateToFrontPageInjectable; diff --git a/src/common/front-end-routing/navigate-to-route-injection-token.ts b/src/common/front-end-routing/navigate-to-route-injection-token.ts deleted file mode 100644 index 29fb82867c..0000000000 --- a/src/common/front-end-routing/navigate-to-route-injection-token.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { Route } from "./front-end-route-injection-token"; - -type InferParametersFrom = TRoute extends Route - ? TParameters - : never; - -type RequiredKeys = Exclude< - { - [K in keyof T]: T extends Record ? K : never; - }[keyof T], - undefined ->; - -type ObjectContainingNoRequired = T extends void - ? never - : RequiredKeys extends [] - ? any - : never; - -type ObjectContainsNoRequired = T extends ObjectContainingNoRequired - ? true - : false; - -// TODO: Missing types for: -// - Navigating to route without parameters, with parameters -// - Navigating to route with required parameters, without parameters -type Parameters = TParameters extends void - ? {} - : ObjectContainsNoRequired extends true - ? { parameters?: TParameters } - : { parameters: TParameters }; - -export type NavigateToRouteOptions = Parameters< - InferParametersFrom -> & { - query?: Record; - fragment?: string; - withoutAffectingBackButton?: boolean; -}; - -export type NavigateToRoute = >( - route: TRoute, - options?: NavigateToRouteOptions) => void; - -export const navigateToRouteInjectionToken = getInjectionToken( - { id: "navigate-to-route-injection-token" }, -); diff --git a/src/common/front-end-routing/navigate-to-url-injection-token.ts b/src/common/front-end-routing/navigate-to-url-injection-token.ts deleted file mode 100644 index 560d62ce4a..0000000000 --- a/src/common/front-end-routing/navigate-to-url-injection-token.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; - -export interface NavigateToUrlOptions { - withoutAffectingBackButton?: boolean; - forceRootFrame?: boolean; -} - -export type NavigateToUrl = (url: string, options?: NavigateToUrlOptions) => void; - -export const navigateToUrlInjectionToken = getInjectionToken( - { id: "navigate-to-url-injection-token" }, -); diff --git a/src/common/front-end-routing/routes/add-cluster/add-cluster-route.injectable.ts b/src/common/front-end-routing/routes/add-cluster/add-cluster-route.injectable.ts deleted file mode 100644 index 41e165dc59..0000000000 --- a/src/common/front-end-routing/routes/add-cluster/add-cluster-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -const addClusterRouteInjectable = getInjectable({ - id: "add-cluster-route", - - instantiate: () => ({ - path: "/add-cluster", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default addClusterRouteInjectable; diff --git a/src/common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable.ts b/src/common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable.ts deleted file mode 100644 index 15595d51c2..0000000000 --- a/src/common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 addClusterRouteInjectable from "./add-cluster-route.injectable"; -import { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; - -const navigateToAddClusterInjectable = getInjectable({ - id: "navigate-to-add-cluster", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(addClusterRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToAddClusterInjectable; diff --git a/src/common/front-end-routing/routes/catalog/catalog-route.injectable.ts b/src/common/front-end-routing/routes/catalog/catalog-route.injectable.ts deleted file mode 100644 index bfe0904073..0000000000 --- a/src/common/front-end-routing/routes/catalog/catalog-route.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -export interface CatalogPathParameters { - group?: string; - kind?: string; -} - -const catalogRouteInjectable = getInjectable({ - id: "catalog-route", - - instantiate: (): Route => ({ - path: "/catalog/:group?/:kind?", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default catalogRouteInjectable; diff --git a/src/common/front-end-routing/routes/catalog/navigate-to-catalog.injectable.ts b/src/common/front-end-routing/routes/catalog/navigate-to-catalog.injectable.ts deleted file mode 100644 index 24fab385bb..0000000000 --- a/src/common/front-end-routing/routes/catalog/navigate-to-catalog.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { CatalogPathParameters } from "./catalog-route.injectable"; -import catalogRouteInjectable from "./catalog-route.injectable"; -import { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; - -export type NavigateToCatalog = (parameters?: CatalogPathParameters) => void; - -const navigateToCatalogInjectable = getInjectable({ - id: "navigate-to-catalog", - - instantiate: (di): NavigateToCatalog => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const catalogRoute = di.inject(catalogRouteInjectable); - - return (parameters) => - navigateToRoute(catalogRoute, { - parameters, - }); - }, -}); - -export default navigateToCatalogInjectable; diff --git a/src/common/front-end-routing/routes/cluster-view/cluster-view-route.injectable.ts b/src/common/front-end-routing/routes/cluster-view/cluster-view-route.injectable.ts deleted file mode 100644 index e912ff63e0..0000000000 --- a/src/common/front-end-routing/routes/cluster-view/cluster-view-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -const clusterViewRouteInjectable = getInjectable({ - id: "cluster-view-route", - - instantiate: () => ({ - path: "/cluster/:clusterId", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default clusterViewRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable.ts b/src/common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable.ts deleted file mode 100644 index 4edc3d3bb5..0000000000 --- a/src/common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; -import clusterViewRouteInjectable from "./cluster-view-route.injectable"; - -export type NavigateToClusterView = (clusterId: string) => void; - -const navigateToClusterViewInjectable = getInjectable({ - id: "navigate-to-cluster-view", - - instantiate: (di): NavigateToClusterView => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(clusterViewRouteInjectable); - - return (clusterId) => - navigateToRoute(route, { parameters: { clusterId }}); - }, -}); - -export default navigateToClusterViewInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts deleted file mode 100644 index 0ad7ed3d88..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const configMapsRouteInjectable = getInjectable({ - id: "config-maps-route", - instantiate: (di) => ({ - path: "/configmaps", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "configmaps", - group: "", - }), - }), - injectionToken: frontEndRouteInjectionToken, -}); - -export default configMapsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable.ts b/src/common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable.ts deleted file mode 100644 index d4fa31027f..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 configMapsRouteInjectable from "./config-maps-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToConfigMapsInjectable = getInjectable({ - id: "navigate-to-config-maps", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(configMapsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToConfigMapsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts deleted file mode 100644 index 00002620ee..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const horizontalPodAutoscalersRouteInjectable = getInjectable({ - id: "horizontal-pod-autoscalers-route", - - instantiate: (di) => ({ - path: "/hpa", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "horizontalpodautoscalers", - group: "autoscaling", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default horizontalPodAutoscalersRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable.ts b/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable.ts deleted file mode 100644 index ab171f4a08..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 horizontalPodAutoscalersRouteInjectable from "./horizontal-pod-autoscalers-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToHorizontalPodAutoscalersInjectable = getInjectable({ - id: "navigate-to-horizontal-pod-autoscalers", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(horizontalPodAutoscalersRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToHorizontalPodAutoscalersInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts deleted file mode 100644 index ea4eb2ae59..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const leasesRouteInjectable = getInjectable({ - id: "leases", - - instantiate: (di) => ({ - path: "/leases", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "leases", - group: "coordination.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default leasesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/leases/navigate-to-leases.injectable.ts b/src/common/front-end-routing/routes/cluster/config/leases/navigate-to-leases.injectable.ts deleted file mode 100644 index 5bf7d74ff1..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/leases/navigate-to-leases.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 leasesRouteInjectable from "./leases-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToLeasesInjectable = getInjectable({ - id: "navigate-to-leases", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(leasesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToLeasesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts deleted file mode 100644 index 0536e76004..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const limitRangesRouteInjectable = getInjectable({ - id: "limit-ranges-route", - - instantiate: (di) => ({ - path: "/limitranges", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "limitranges", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default limitRangesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable.ts b/src/common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable.ts deleted file mode 100644 index 00e8c89d7c..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 limitRangesRouteInjectable from "./limit-ranges-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToLimitRangesInjectable = getInjectable({ - id: "navigate-to-limit-ranges", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(limitRangesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToLimitRangesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable.ts b/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable.ts deleted file mode 100644 index 5a3f17027d..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 podDisruptionBudgetsRouteInjectable from "./pod-disruption-budgets-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPodDisruptionBudgetsInjectable = getInjectable({ - id: "navigate-to-pod-disruption-budgets", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(podDisruptionBudgetsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPodDisruptionBudgetsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts deleted file mode 100644 index 12ce0a2138..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const podDisruptionBudgetsRouteInjectable = getInjectable({ - id: "pod-disruption-budgets-route", - - instantiate: (di) => ({ - path: "/poddisruptionbudgets", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "poddisruptionbudgets", - group: "policy", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default podDisruptionBudgetsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts deleted file mode 100644 index 45b7a34eeb..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 priorityClassesRouteInjectable from "./priority-classes-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPriorityClassesInjectable = getInjectable({ - id: "navigate-to-priority-classes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(priorityClassesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPriorityClassesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts deleted file mode 100644 index 75194b0541..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const priorityClassesRouteInjectable = getInjectable({ - id: "priority-classes-route", - - instantiate: (di) => ({ - path: "/priorityclasses", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "priorityclasses", - group: "scheduling.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default priorityClassesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/resource-quotas/navigate-to-resource-quotas.injectable.ts b/src/common/front-end-routing/routes/cluster/config/resource-quotas/navigate-to-resource-quotas.injectable.ts deleted file mode 100644 index 3637782115..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/resource-quotas/navigate-to-resource-quotas.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 resourceQuotasRouteInjectable from "./resource-quotas-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToResourceQuotasInjectable = getInjectable({ - id: "navigate-to-resource-quotas", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(resourceQuotasRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToResourceQuotasInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts deleted file mode 100644 index 4905b70890..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const resourceQuotasRouteInjectable = getInjectable({ - id: "resource-quotas-route", - - instantiate: (di) => ({ - path: "/resourcequotas", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "resourcequotas", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default resourceQuotasRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable.ts deleted file mode 100644 index 4d6ca1757d..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 runtimeClassesRouteInjectable from "./runtime-classes-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToRuntimeClassesInjectable = getInjectable({ - id: "navigate-to-runtime-classes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(runtimeClassesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToRuntimeClassesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts deleted file mode 100644 index beab83754f..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const runtimeClassesRouteInjectable = getInjectable({ - id: "runtime-classes-route", - - instantiate: (di) => ({ - path: "/runtimeclasses", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "runtimeclasses", - group: "node.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default runtimeClassesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/secrets/navigate-to-secrets.injectable.ts b/src/common/front-end-routing/routes/cluster/config/secrets/navigate-to-secrets.injectable.ts deleted file mode 100644 index b8afdf74c3..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/secrets/navigate-to-secrets.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 secretsRouteInjectable from "./secrets-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToSecretsInjectable = getInjectable({ - id: "navigate-to-secrets", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(secretsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToSecretsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts deleted file mode 100644 index 7442de14b1..0000000000 --- a/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const secretsRouteInjectable = getInjectable({ - id: "secrets-route", - - instantiate: (di) => ({ - path: "/secrets", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "secrets", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default secretsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts b/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts deleted file mode 100644 index 854b3a8c3a..0000000000 --- a/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const crdListRouteInjectable = getInjectable({ - id: "crd-list-route", - - instantiate: () => ({ - path: "/crd/definitions", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default crdListRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts b/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts deleted file mode 100644 index bd78dd1174..0000000000 --- a/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 crdListRouteInjectable from "./crd-list-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToCrdListInjectable = getInjectable({ - id: "navigate-to-crd-list", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(crdListRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToCrdListInjectable; diff --git a/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts b/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts deleted file mode 100644 index cc7d9b40be..0000000000 --- a/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -export interface CustomResourcesPathParameters { - group?: string; - name?: string; -} - -const customResourcesRouteInjectable = getInjectable({ - id: "custom-resources-route", - - instantiate: (): Route => ({ - path: "/crd/:group?/:name?", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default customResourcesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts b/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts deleted file mode 100644 index 9df8cd6e2c..0000000000 --- a/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { CustomResourcesPathParameters } from "./custom-resources-route.injectable"; -import customResourcesRouteInjectable from "./custom-resources-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToCustomResourcesInjectable = getInjectable({ - id: "navigate-to-custom-resources", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(customResourcesRouteInjectable); - - return (parameters?: CustomResourcesPathParameters) => - navigateToRoute(route, { parameters }); - }, -}); - -export default navigateToCustomResourcesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts b/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts deleted file mode 100644 index 728f80c21d..0000000000 --- a/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; - -const eventsRouteInjectable = getInjectable({ - id: "events-route", - - instantiate: (di) => ({ - path: "/events", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "events", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default eventsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/events/navigate-to-events.injectable.ts b/src/common/front-end-routing/routes/cluster/events/navigate-to-events.injectable.ts deleted file mode 100644 index 767f69b497..0000000000 --- a/src/common/front-end-routing/routes/cluster/events/navigate-to-events.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 eventsRouteInjectable from "./events-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; - -const navigateToEventsInjectable = getInjectable({ - id: "navigate-to-events", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(eventsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToEventsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable.ts b/src/common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable.ts deleted file mode 100644 index 02161b082a..0000000000 --- a/src/common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -export interface HelmChartsPathParameters { - repo?: string; - chartName?: string; -} - -const helmChartsRouteInjectable = getInjectable({ - id: "helm-charts-route", - - instantiate: (): Route => ({ - path: "/helm/charts/:repo?/:chartName?", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default helmChartsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable.ts b/src/common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable.ts deleted file mode 100644 index 37ab96cc2c..0000000000 --- a/src/common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { HelmChartsPathParameters } from "./helm-charts-route.injectable"; -import helmChartsRouteInjectable from "./helm-charts-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -export type NavigateToHelmCharts = (parameters?: HelmChartsPathParameters) => void; - -const navigateToHelmChartsInjectable = getInjectable({ - id: "navigate-to-helm-charts", - - instantiate: (di): NavigateToHelmCharts => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(helmChartsRouteInjectable); - - return (parameters) => - navigateToRoute(route, { - parameters, - }); - }, -}); - -export default navigateToHelmChartsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable.ts b/src/common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable.ts deleted file mode 100644 index fb66416c63..0000000000 --- a/src/common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -export interface HelmReleasesPathParameters { - namespace?: string; - name?: string; -} - -const helmReleasesRouteInjectable = getInjectable({ - id: "helm-releases-route", - - instantiate: (): Route => ({ - path: "/helm/releases/:namespace?/:name?", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default helmReleasesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable.ts b/src/common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable.ts deleted file mode 100644 index 3d9aebddf5..0000000000 --- a/src/common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 { HelmReleasesPathParameters } from "./helm-releases-route.injectable"; -import helmReleasesRouteInjectable from "./helm-releases-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -export type NavigateToHelmReleases = (parameters?: HelmReleasesPathParameters) => void; - -const navigateToHelmReleasesInjectable = getInjectable({ - id: "navigate-to-helm-releases", - - instantiate: (di): NavigateToHelmReleases => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(helmReleasesRouteInjectable); - - return (parameters) => navigateToRoute(route, { parameters }); - }, -}); - -export default navigateToHelmReleasesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts b/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts deleted file mode 100644 index 0b57c8d573..0000000000 --- a/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; - -const namespacesRouteInjectable = getInjectable({ - id: "namespaces-route", - - instantiate: (di) => ({ - path: "/namespaces", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "namespaces", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default namespacesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/namespaces/navigate-to-namespaces.injectable.ts b/src/common/front-end-routing/routes/cluster/namespaces/navigate-to-namespaces.injectable.ts deleted file mode 100644 index 85fa63c695..0000000000 --- a/src/common/front-end-routing/routes/cluster/namespaces/navigate-to-namespaces.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 namespacesRouteInjectable from "./namespaces-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; - -const navigateToNamespacesInjectable = getInjectable({ - id: "navigate-to-namespaces", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(namespacesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToNamespacesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts deleted file mode 100644 index 50b3c72e4c..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const endpointsRouteInjectable = getInjectable({ - id: "endpoints-route", - - instantiate: (di) => ({ - path: "/endpoints", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "endpoints", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default endpointsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/endpoints/navigate-to-endpoints.injectable.ts b/src/common/front-end-routing/routes/cluster/network/endpoints/navigate-to-endpoints.injectable.ts deleted file mode 100644 index 04439b3d3b..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/endpoints/navigate-to-endpoints.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 endpointsRouteInjectable from "./endpoints-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToEndpointsInjectable = getInjectable({ - id: "navigate-to-endpoints", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(endpointsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToEndpointsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts deleted file mode 100644 index e036d4b6a6..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; -import { - shouldShowResourceInjectionToken, -} from "../../../../../cluster-store/allowed-resources-injection-token"; - -const ingressClassesesRouteInjectable = getInjectable({ - id: "ingress-classes-route", - - instantiate: (di) => { - const isEnabled = di.inject(shouldShowResourceInjectionToken, { - apiName: "ingressclasses", - group: "networking.k8s.io", - }); - - return { - path: "/ingress-classes", - clusterFrame: true, - isEnabled, - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default ingressClassesesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts deleted file mode 100644 index 22c666d5ae..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; -import ingressClassesesRouteInjectable from "./ingress-classeses-route.injectable"; - -const navigateToIngressesInjectable = getInjectable({ - id: "navigate-to-ingress-classes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(ingressClassesesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToIngressesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts deleted file mode 100644 index 8e01646b82..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { computedOr } from "../../../../../utils/computed-or"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const ingressesRouteInjectable = getInjectable({ - id: "ingresses-route", - - instantiate: (di) => ({ - path: "/ingresses", - clusterFrame: true, - isEnabled: computedOr( - di.inject(shouldShowResourceInjectionToken, { - apiName: "ingresses", - group: "networking.k8s.io", - }), - di.inject(shouldShowResourceInjectionToken, { - apiName: "ingresses", - group: "extensions", - }), - ), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default ingressesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable.ts deleted file mode 100644 index b47f9d4be3..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 ingressesRouteInjectable from "./ingresses-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToIngressesInjectable = getInjectable({ - id: "navigate-to-ingresses", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(ingressesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToIngressesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/network-policies/navigate-to-network-policies.injectable.ts b/src/common/front-end-routing/routes/cluster/network/network-policies/navigate-to-network-policies.injectable.ts deleted file mode 100644 index 82bc76ee9f..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/network-policies/navigate-to-network-policies.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 networkPoliciesRouteInjectable from "./network-policies-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToNetworkPoliciesInjectable = getInjectable({ - id: "navigate-to-network-policies", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(networkPoliciesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToNetworkPoliciesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts deleted file mode 100644 index 38a1b8a7e2..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const networkPoliciesRouteInjectable = getInjectable({ - id: "network-policies-route", - - instantiate: (di) => ({ - path: "/network-policies", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "networkpolicies", - group: "networking.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default networkPoliciesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable.ts b/src/common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable.ts deleted file mode 100644 index 9a03eeda47..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { PortForwardsPathParameters } from "./port-forwards-route.injectable"; -import portForwardsRouteInjectable from "./port-forwards-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -export type NavigateToPortForwards = (parameters?: PortForwardsPathParameters) => void; - -const navigateToPortForwardsInjectable = getInjectable({ - id: "navigate-to-port-forwards", - - instantiate: (di): NavigateToPortForwards => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(portForwardsRouteInjectable); - - return (parameters) => - navigateToRoute(route, { parameters }); - }, -}); - -export default navigateToPortForwardsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable.ts deleted file mode 100644 index 0c8cd5ef2d..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -export interface PortForwardsPathParameters { - forwardport?: string; -} - -const portForwardsRouteInjectable = getInjectable({ - id: "port-forwards-route", - - instantiate: (): Route => ({ - path: "/port-forwards/:forwardport?", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default portForwardsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/services/navigate-to-services.injectable.ts b/src/common/front-end-routing/routes/cluster/network/services/navigate-to-services.injectable.ts deleted file mode 100644 index 02aa060b89..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/services/navigate-to-services.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 servicesRouteInjectable from "./services-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToServicesInjectable = getInjectable({ - id: "navigate-to-services", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(servicesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToServicesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts deleted file mode 100644 index 223ded1e65..0000000000 --- a/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const servicesRouteInjectable = getInjectable({ - id: "services-route", - - instantiate: (di) => ({ - path: "/services", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "services", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default servicesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/nodes/navigate-to-nodes.injectable.ts b/src/common/front-end-routing/routes/cluster/nodes/navigate-to-nodes.injectable.ts deleted file mode 100644 index eccdd2377d..0000000000 --- a/src/common/front-end-routing/routes/cluster/nodes/navigate-to-nodes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 nodesRouteInjectable from "./nodes-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; - -const navigateToNodesInjectable = getInjectable({ - id: "navigate-to-nodes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(nodesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToNodesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts deleted file mode 100644 index e6ca61346e..0000000000 --- a/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; - -const nodesRouteInjectable = getInjectable({ - id: "nodes-route", - - instantiate: (di) => ({ - path: "/nodes", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "nodes", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default nodesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts b/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts deleted file mode 100644 index e559c35079..0000000000 --- a/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; - -const clusterOverviewRouteInjectable = getInjectable({ - id: "cluster-overview-route", - - instantiate: (di) => ({ - path: "/overview", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "nodes", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default clusterOverviewRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/overview/navigate-to-cluster-overview.injectable.ts b/src/common/front-end-routing/routes/cluster/overview/navigate-to-cluster-overview.injectable.ts deleted file mode 100644 index f15dca518c..0000000000 --- a/src/common/front-end-routing/routes/cluster/overview/navigate-to-cluster-overview.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 clusterOverviewRouteInjectable from "./cluster-overview-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; - -const navigateToClusterOverviewInjectable = getInjectable({ - id: "navigate-to-cluster-overview", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(clusterOverviewRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToClusterOverviewInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/navigate-to-persistent-volume-claims.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/navigate-to-persistent-volume-claims.injectable.ts deleted file mode 100644 index 015ad6e988..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/navigate-to-persistent-volume-claims.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 persistentVolumeClaimsRouteInjectable from "./persistent-volume-claims-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPersistentVolumeClaimsInjectable = getInjectable({ - id: "navigate-to-persistent-volume-claims", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(persistentVolumeClaimsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPersistentVolumeClaimsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts deleted file mode 100644 index dbda527555..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const persistentVolumeClaimsRouteInjectable = getInjectable({ - id: "persistent-volume-claims-route", - - instantiate: (di) => ({ - path: "/persistent-volume-claims", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "persistentvolumeclaims", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default persistentVolumeClaimsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/navigate-to-persistent-volumes.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/navigate-to-persistent-volumes.injectable.ts deleted file mode 100644 index 6702dd6712..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/navigate-to-persistent-volumes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 persistentVolumesRouteInjectable from "./persistent-volumes-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPersistentVolumesInjectable = getInjectable({ - id: "navigate-to-persistent-volumes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(persistentVolumesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPersistentVolumesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts deleted file mode 100644 index 7a06d9df58..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const persistentVolumesRouteInjectable = getInjectable({ - id: "persistent-volumes-route", - - instantiate: (di) => ({ - path: "/persistent-volumes", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "persistentvolumes", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default persistentVolumesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/storage-classes/navigate-to-storage-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/storage-classes/navigate-to-storage-classes.injectable.ts deleted file mode 100644 index adaba3225b..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/storage-classes/navigate-to-storage-classes.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 storageClassesRouteInjectable from "./storage-classes-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToStorageClassesInjectable = getInjectable({ - id: "navigate-to-storage-classes", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(storageClassesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToStorageClassesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts deleted file mode 100644 index 8702ab1602..0000000000 --- a/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const storageClassesRouteInjectable = getInjectable({ - id: "storage-classes-route", - - instantiate: (di) => ({ - path: "/storage-classes", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "storageclasses", - group: "storage.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default storageClassesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts deleted file mode 100644 index 0903d5fced..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const clusterRoleBindingsRouteInjectable = getInjectable({ - id: "cluster-role-bindings-route", - - instantiate: (di) => ({ - path: "/cluster-role-bindings", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "clusterrolebindings", - group: "rbac.authorization.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default clusterRoleBindingsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/navigate-to-cluster-role-bindings.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/navigate-to-cluster-role-bindings.injectable.ts deleted file mode 100644 index f6bd0bfd97..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/navigate-to-cluster-role-bindings.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 clusterRoleBindingsRouteInjectable from "./cluster-role-bindings-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToClusterRoleBindingsInjectable = getInjectable({ - id: "navigate-to-cluster-role-bindings", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(clusterRoleBindingsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToClusterRoleBindingsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts deleted file mode 100644 index 9fce206667..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const clusterRolesRouteInjectable = getInjectable({ - id: "cluster-roles-route", - - instantiate: (di) => ({ - path: "/cluster-roles", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "clusterroles", - group: "rbac.authorization.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default clusterRolesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/navigate-to-cluster-roles.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/navigate-to-cluster-roles.injectable.ts deleted file mode 100644 index ed56679155..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/navigate-to-cluster-roles.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 clusterRolesRouteInjectable from "./cluster-roles-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToClusterRolesInjectable = getInjectable({ - id: "navigate-to-cluster-roles", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(clusterRolesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToClusterRolesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/navigate-to-pod-security-policies.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/navigate-to-pod-security-policies.injectable.ts deleted file mode 100644 index 205354ba3b..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/navigate-to-pod-security-policies.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 podSecurityPoliciesRouteInjectable from "./pod-security-policies-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPodSecurityPoliciesInjectable = getInjectable({ - id: "navigate-to-pod-security-policies", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(podSecurityPoliciesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPodSecurityPoliciesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts deleted file mode 100644 index 2f35986916..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const podSecurityPoliciesRouteInjectable = getInjectable({ - id: "pod-security-policies-route", - - instantiate: (di) => { - return { - path: "/pod-security-policies", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "podsecuritypolicies", - group: "policy", - }), - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default podSecurityPoliciesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/navigate-to-role-bindings.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/role-bindings/navigate-to-role-bindings.injectable.ts deleted file mode 100644 index ce5bb3713b..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/navigate-to-role-bindings.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 roleBindingsRouteInjectable from "./role-bindings-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToRoleBindingsInjectable = getInjectable({ - id: "navigate-to-role-bindings", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(roleBindingsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToRoleBindingsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts deleted file mode 100644 index 759c1b8eda..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const roleBindingsRouteInjectable = getInjectable({ - id: "role-bindings-route", - - instantiate: (di) => { - return { - path: "/role-bindings", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "rolebindings", - group: "rbac.authorization.k8s.io", - }), - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default roleBindingsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/roles/navigate-to-roles.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/roles/navigate-to-roles.injectable.ts deleted file mode 100644 index adebb64ae1..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/roles/navigate-to-roles.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 rolesRouteInjectable from "./roles-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToRolesInjectable = getInjectable({ - id: "navigate-to-roles", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(rolesRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToRolesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts deleted file mode 100644 index efe4cad810..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const rolesRouteInjectable = getInjectable({ - id: "roles-route", - - instantiate: (di) => ({ - path: "/roles", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "roles", - group: "rbac.authorization.k8s.io", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default rolesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/navigate-to-service-accounts.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/service-accounts/navigate-to-service-accounts.injectable.ts deleted file mode 100644 index 5eb1861a92..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/navigate-to-service-accounts.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 serviceAccountsRouteInjectable from "./service-accounts-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToServiceAccountsInjectable = getInjectable({ - id: "navigate-to-service-accounts", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(serviceAccountsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToServiceAccountsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts deleted file mode 100644 index 65d02135c7..0000000000 --- a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const serviceAccountsRouteInjectable = getInjectable({ - id: "service-accounts-route", - - instantiate: (di) => ({ - path: "/service-accounts", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "serviceaccounts", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default serviceAccountsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts deleted file mode 100644 index 33453a2247..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const cronJobsRouteInjectable = getInjectable({ - id: "cron-jobs-route", - - instantiate: (di) => ({ - path: "/cronjobs", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "cronjobs", - group: "batch", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default cronJobsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/navigate-to-cron-jobs.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/navigate-to-cron-jobs.injectable.ts deleted file mode 100644 index 42b104055e..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/navigate-to-cron-jobs.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 cronJobsRouteInjectable from "./cron-jobs-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToCronJobsInjectable = getInjectable({ - id: "navigate-to-cron-jobs", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(cronJobsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToCronJobsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts deleted file mode 100644 index f1ec2008fa..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const daemonsetsRouteInjectable = getInjectable({ - id: "daemonsets-route", - - instantiate: (di) => ({ - path: "/daemonsets", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "daemonsets", - group: "apps", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default daemonsetsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable.ts deleted file mode 100644 index 1cc5ec8d8d..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 daemonsetsRouteInjectable from "./daemonsets-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToDaemonsetsInjectable = getInjectable({ - id: "navigate-to-daemonsets", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(daemonsetsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToDaemonsetsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts deleted file mode 100644 index 84c059780f..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const deploymentsRouteInjectable = getInjectable({ - id: "deployments-route", - - instantiate: (di) => ({ - path: "/deployments", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "deployments", - group: "apps", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default deploymentsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable.ts deleted file mode 100644 index b6d2f07391..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 deploymentsRouteInjectable from "./deployments-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToDeploymentsInjectable = getInjectable({ - id: "navigate-to-deployments", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(deploymentsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToDeploymentsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts deleted file mode 100644 index 39cc89e88f..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const jobsRouteInjectable = getInjectable({ - id: "jobs-route", - - instantiate: (di) => ({ - path: "/jobs", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "jobs", - group: "batch", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default jobsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable.ts deleted file mode 100644 index 920c5c4de7..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 jobsRouteInjectable from "./jobs-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToJobsInjectable = getInjectable({ - id: "navigate-to-jobs", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(jobsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToJobsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable.ts deleted file mode 100644 index 224950206b..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 workloadsOverviewRouteInjectable from "./workloads-overview-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToWorkloadsOverviewInjectable = getInjectable({ - id: "navigate-to-workloads-overview", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(workloadsOverviewRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToWorkloadsOverviewInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable.ts deleted file mode 100644 index 188bd9f1de..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const workloadsOverviewRouteInjectable = getInjectable({ - id: "workloads-overview-route", - - instantiate: () => ({ - path: "/workloads", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default workloadsOverviewRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable.ts deleted file mode 100644 index 4eab211598..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 podsRouteInjectable from "./pods-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToPodsInjectable = getInjectable({ - id: "navigate-to-pods", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(podsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToPodsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts deleted file mode 100644 index d013f872f0..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const podsRouteInjectable = getInjectable({ - id: "pods-route", - - instantiate: (di) => ({ - path: "/pods", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "pods", - group: "", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default podsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/replicasets/navigate-to-replicasets.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/replicasets/navigate-to-replicasets.injectable.ts deleted file mode 100644 index 9549abab09..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/replicasets/navigate-to-replicasets.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 replicasetsRouteInjectable from "./replicasets-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToReplicasetsInjectable = getInjectable({ - id: "navigate-to-replicasets", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(replicasetsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToReplicasetsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts deleted file mode 100644 index b790ce13ec..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const replicasetsRouteInjectable = getInjectable({ - id: "replicasets-route", - - instantiate: (di) => ({ - path: "/replicasets", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "replicasets", - group: "apps", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default replicasetsRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable.ts deleted file mode 100644 index 403c7fa042..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 statefulsetsRouteInjectable from "./statefulsets-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; - -const navigateToStatefulsetsInjectable = getInjectable({ - id: "navigate-to-statefulsets", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(statefulsetsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToStatefulsetsInjectable; diff --git a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts deleted file mode 100644 index 72c81b3bee..0000000000 --- a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const statefulsetsRouteInjectable = getInjectable({ - id: "statefulsets-route", - - instantiate: (di) => ({ - path: "/statefulsets", - clusterFrame: true, - isEnabled: di.inject(shouldShowResourceInjectionToken, { - apiName: "statefulsets", - group: "apps", - }), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default statefulsetsRouteInjectable; diff --git a/src/common/front-end-routing/routes/entity-settings/entity-settings-route.injectable.ts b/src/common/front-end-routing/routes/entity-settings/entity-settings-route.injectable.ts deleted file mode 100644 index e7d3ac5f46..0000000000 --- a/src/common/front-end-routing/routes/entity-settings/entity-settings-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { Route } from "../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -export interface EntitySettingsPathParameters { - entityId: string; -} - -const entitySettingsRouteInjectable = getInjectable({ - id: "entity-settings-route", - - instantiate: (): Route => ({ - path: "/entity/:entityId/settings", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default entitySettingsRouteInjectable; diff --git a/src/common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable.ts b/src/common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable.ts deleted file mode 100644 index bc49bc4041..0000000000 --- a/src/common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 entitySettingsRouteInjectable from "./entity-settings-route.injectable"; -import { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; - -export type NavigateToEntitySettings = (entityId: string, targetTabId?: string) => void; - -const navigateToEntitySettingsInjectable = getInjectable({ - id: "navigate-to-entity-settings", - - instantiate: (di): NavigateToEntitySettings => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(entitySettingsRouteInjectable); - - return (entityId, targetTabId) => - navigateToRoute(route, { parameters: { entityId }, fragment: targetTabId }); - }, -}); - -export default navigateToEntitySettingsInjectable; diff --git a/src/common/front-end-routing/routes/extensions/extensions-route.injectable.ts b/src/common/front-end-routing/routes/extensions/extensions-route.injectable.ts deleted file mode 100644 index 5992355022..0000000000 --- a/src/common/front-end-routing/routes/extensions/extensions-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -const extensionsRouteInjectable = getInjectable({ - id: "extensions-route", - - instantiate: () => ({ - path: "/extensions", - clusterFrame: false, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default extensionsRouteInjectable; diff --git a/src/common/front-end-routing/routes/extensions/navigate-to-extensions.injectable.ts b/src/common/front-end-routing/routes/extensions/navigate-to-extensions.injectable.ts deleted file mode 100644 index 2e4e6d7d4c..0000000000 --- a/src/common/front-end-routing/routes/extensions/navigate-to-extensions.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 extensionsRouteInjectable from "./extensions-route.injectable"; -import { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; - -const navigateToExtensionsInjectable = getInjectable({ - id: "navigate-to-extensions", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(extensionsRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToExtensionsInjectable; diff --git a/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts b/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts deleted file mode 100644 index d8db889033..0000000000 --- a/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 welcomeRouteConfigInjectable from "./welcome-route-config.injectable"; -import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -const defaultWelcomeRouteInjectable = getInjectable({ - id: "default-welcome-route", - - instantiate: (di) => { - const welcomeRoute = di.inject(welcomeRouteConfigInjectable); - - return { - path: "/welcome", - clusterFrame: false, - isEnabled: computed(() => welcomeRoute === "/welcome"), - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default defaultWelcomeRouteInjectable; diff --git a/src/common/front-end-routing/routes/welcome/navigate-to-welcome.injectable.ts b/src/common/front-end-routing/routes/welcome/navigate-to-welcome.injectable.ts deleted file mode 100644 index 46f5b42056..0000000000 --- a/src/common/front-end-routing/routes/welcome/navigate-to-welcome.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 welcomeRouteInjectable from "./welcome-route.injectable"; -import { navigateToRouteInjectionToken } from "../../navigate-to-route-injection-token"; - -const navigateToWelcomeInjectable = getInjectable({ - id: "navigate-to-welcome", - - instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(welcomeRouteInjectable); - - return () => navigateToRoute(route); - }, -}); - -export default navigateToWelcomeInjectable; diff --git a/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts b/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts deleted file mode 100644 index d46b816c50..0000000000 --- a/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 applicationInformationToken from "../../../vars/application-information-token"; - -const welcomeRouteConfigInjectable = getInjectable({ - id: "welcome-route-config", - - instantiate: (di) => di.inject(applicationInformationToken).config.welcomeRoute, -}); - -export default welcomeRouteConfigInjectable; diff --git a/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts b/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts deleted file mode 100644 index 839a7446c1..0000000000 --- a/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 welcomeRouteConfigInjectable from "./welcome-route-config.injectable"; -import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; - -const welcomeRouteInjectable = getInjectable({ - id: "welcome-route", - - instantiate: (di) => { - const welcomeRoute = di.inject(welcomeRouteConfigInjectable); - - return { - path: welcomeRoute, - clusterFrame: false, - isEnabled: computed(() => true), - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default welcomeRouteInjectable; diff --git a/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts b/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts deleted file mode 100644 index acd1d4401d..0000000000 --- a/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; -import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token"; -import { frontEndRouteInjectionToken } from "./front-end-route-injection-token"; -import { filter, map } from "lodash/fp"; -import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; -import type { ClusterStore } from "../cluster-store/cluster-store"; -import { pipeline } from "@ogre-tools/fp"; - -describe("verify-that-all-routes-have-component", () => { - it("verify that routes have route component", () => { - const rendererDi = getDiForUnitTesting({ doGeneralOverrides: true }); - - rendererDi.override(clusterStoreInjectable, () => ({ - getById: () => null, - } as unknown as ClusterStore)); - - const routes = rendererDi.injectMany(frontEndRouteInjectionToken); - const routeComponents = rendererDi.injectMany( - routeSpecificComponentInjectionToken, - ); - - const routesMissingComponent = pipeline( - routes, - - map( - (currentRoute) => ({ - path: currentRoute.path, - routeComponent: routeComponents.find(({ route }) => ( - route.path === currentRoute.path - && route.clusterFrame === currentRoute.clusterFrame)), - }), - ), - - filter({ routeComponent: undefined }), - - map("path"), - ); - - expect(routesMissingComponent).toEqual([]); - }); -}); diff --git a/src/common/fs/access-path.injectable.ts b/src/common/fs/access-path.injectable.ts deleted file mode 100644 index 0504de9d6f..0000000000 --- a/src/common/fs/access-path.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type AccessPath = (path: string, mode?: number) => Promise; - -const accessPathInjectable = getInjectable({ - id: "access-path", - instantiate: (di): AccessPath => { - const { access } = di.inject(fsInjectable); - - return async (path, mode) => { - try { - await access(path, mode); - - return true; - } catch { - return false; - } - }; - }, -}); - -export default accessPathInjectable; diff --git a/src/common/fs/copy.global-override-for-injectable.ts b/src/common/fs/copy.global-override-for-injectable.ts deleted file mode 100644 index b6d899d2c4..0000000000 --- a/src/common/fs/copy.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import copyInjectable from "./copy.injectable"; - -export default getGlobalOverride(copyInjectable, () => async () => { - throw new Error("tried to copy filepaths without override"); -}); diff --git a/src/common/fs/copy.injectable.ts b/src/common/fs/copy.injectable.ts deleted file mode 100644 index 6a64ee3751..0000000000 --- a/src/common/fs/copy.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 { CopyOptions } from "fs-extra"; -import fsInjectable from "./fs.injectable"; - -export type Copy = (src: string, dest: string, options?: CopyOptions | undefined) => Promise; - -const copyInjectable = getInjectable({ - id: "copy", - instantiate: (di): Copy => di.inject(fsInjectable).copy, -}); - -export default copyInjectable; diff --git a/src/common/fs/create-read-file-stream.injectable.ts b/src/common/fs/create-read-file-stream.injectable.ts deleted file mode 100644 index 8714e1cdcd..0000000000 --- a/src/common/fs/create-read-file-stream.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 { ReadStream } from "fs"; -import fsInjectable from "./fs.injectable"; - -export interface CreateReadStreamOptions { - mode?: number; - end?: number | undefined; - flags?: string | undefined; - encoding?: BufferEncoding | undefined; - autoClose?: boolean | undefined; - /** - * @default false - */ - emitClose?: boolean | undefined; - start?: number | undefined; - highWaterMark?: number | undefined; -} - -export type CreateReadFileStream = (filePath: string, options?: CreateReadStreamOptions) => ReadStream; - -const createReadFileStreamInjectable = getInjectable({ - id: "create-read-file-stream", - instantiate: (di): CreateReadFileStream => di.inject(fsInjectable).createReadStream, -}); - -export default createReadFileStreamInjectable; diff --git a/src/common/fs/ensure-dir.injectable.ts b/src/common/fs/ensure-dir.injectable.ts deleted file mode 100644 index 78ec4d91dc..0000000000 --- a/src/common/fs/ensure-dir.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type EnsureDirectory = (dirPath: string) => Promise; - -const ensureDirInjectable = getInjectable({ - id: "ensure-dir", - - // TODO: Remove usages of ensureDir from business logic. - // TODO: Read, Write, Watch etc. operations should do this internally. - instantiate: (di): EnsureDirectory => di.inject(fsInjectable).ensureDir, -}); - -export default ensureDirInjectable; diff --git a/src/common/fs/exec-file.global-override-for-injectable.ts b/src/common/fs/exec-file.global-override-for-injectable.ts deleted file mode 100644 index 162666a130..0000000000 --- a/src/common/fs/exec-file.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; -import execFileInjectable from "./exec-file.injectable"; - -export default getGlobalOverrideForFunction(execFileInjectable); diff --git a/src/common/fs/exec-file.injectable.ts b/src/common/fs/exec-file.injectable.ts deleted file mode 100644 index f026e0db3a..0000000000 --- a/src/common/fs/exec-file.injectable.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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 { ExecFileException, ExecFileOptions } from "child_process"; -import { execFile } from "child_process"; -import type { AsyncResult } from "../utils/async-result"; - -export type ExecFileError = ExecFileException & { stderr: string }; - -export interface ExecFile { - (filePath: string): Promise>; - (filePath: string, argsOrOptions: string[] | ExecFileOptions): Promise>; - (filePath: string, args: string[], options: ExecFileOptions): Promise>; -} - -const execFileInjectable = getInjectable({ - id: "exec-file", - - instantiate: (): ExecFile => { - return (filePath: string, argsOrOptions?: string[] | ExecFileOptions, maybeOptions?: ExecFileOptions) => { - const { args, options } = (() => { - if (Array.isArray(argsOrOptions)) { - return { - args: argsOrOptions, - options: maybeOptions ?? {}, - }; - } else { - return { - args: [], - options: argsOrOptions ?? {}, - }; - } - })(); - - return new Promise((resolve) => { - execFile(filePath, args, options, (error, stdout, stderr) => { - if (error) { - resolve({ - callWasSuccessful: false, - error: Object.assign(error, { stderr }), - }); - } else { - resolve({ - callWasSuccessful: true, - response: stdout, - }); - } - }); - }); - }; - }, - - causesSideEffects: true, -}); - -export default execFileInjectable; diff --git a/src/common/fs/extract-tar.global-override-for-injectable.ts b/src/common/fs/extract-tar.global-override-for-injectable.ts deleted file mode 100644 index 02a46c1d6b..0000000000 --- a/src/common/fs/extract-tar.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import extractTarInjectable from "./extract-tar.injectable"; - -export default getGlobalOverride(extractTarInjectable, () => async () => { - throw new Error("tried to extract a tar file without override"); -}); diff --git a/src/common/fs/extract-tar.injectable.ts b/src/common/fs/extract-tar.injectable.ts deleted file mode 100644 index 410512139e..0000000000 --- a/src/common/fs/extract-tar.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { ExtractOptions } from "tar"; -import { extract } from "tar"; -import getDirnameOfPathInjectable from "../path/get-dirname.injectable"; - -export type ExtractTar = (filePath: string, opts?: ExtractOptions) => Promise; - -const extractTarInjectable = getInjectable({ - id: "extract-tar", - instantiate: (di): ExtractTar => { - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return (filePath, opts = {}) => extract({ - file: filePath, - cwd: getDirnameOfPath(filePath), - ...opts, - }); - }, - causesSideEffects: true, -}); - -export default extractTarInjectable; diff --git a/src/common/fs/fs.injectable.ts b/src/common/fs/fs.injectable.ts deleted file mode 100644 index f80375095c..0000000000 --- a/src/common/fs/fs.injectable.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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 { ReadOptions } from "fs-extra"; -import fse from "fs-extra"; - -/** - * NOTE: Add corrisponding a corrisponding override of this injecable in `src/test-utils/override-fs-with-fakes.ts` - */ -const fsInjectable = getInjectable({ - id: "fs", - instantiate: () => { - const { - promises: { - readFile, - writeFile, - readdir, - lstat, - rm, - access, - stat, - }, - ensureDir, - ensureDirSync, - readFileSync, - readJson, - writeJson, - readJsonSync, - writeFileSync, - writeJsonSync, - pathExistsSync, - pathExists, - copy, - createReadStream, - } = fse; - - return { - readFile, - readJson: readJson as (file: string, options?: ReadOptions | BufferEncoding) => Promise, - writeFile, - writeJson, - pathExists, - readdir, - readFileSync, - readJsonSync, - writeFileSync, - writeJsonSync, - pathExistsSync, - lstat, - rm, - access, - copy: copy as (src: string, dest: string, options?: fse.CopyOptions) => Promise, - ensureDir: ensureDir as (path: string, options?: number | fse.EnsureOptions ) => Promise, - ensureDirSync, - createReadStream, - stat, - }; - }, - causesSideEffects: true, -}); - -export default fsInjectable; diff --git a/src/common/fs/lstat.global-override-for-injectable.ts b/src/common/fs/lstat.global-override-for-injectable.ts deleted file mode 100644 index 9c9f3d4933..0000000000 --- a/src/common/fs/lstat.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import lstatInjectable from "./lstat.injectable"; - -export default getGlobalOverride(lstatInjectable, () => async () => { - throw new Error("tried to lstat a filepath without override"); -}); diff --git a/src/common/fs/lstat.injectable.ts b/src/common/fs/lstat.injectable.ts deleted file mode 100644 index 50c1d4ad12..0000000000 --- a/src/common/fs/lstat.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 { Stats } from "fs"; -import fsInjectable from "./fs.injectable"; - -export type LStat = (path: string) => Promise; - -const lstatInjectable = getInjectable({ - id: "lstat", - instantiate: (di): LStat => di.inject(fsInjectable).lstat, -}); - -export default lstatInjectable; diff --git a/src/common/fs/path-exists-sync.injectable.ts b/src/common/fs/path-exists-sync.injectable.ts deleted file mode 100644 index 21bcb6d7d1..0000000000 --- a/src/common/fs/path-exists-sync.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -const pathExistsSyncInjectable = getInjectable({ - id: "path-exists-sync", - instantiate: (di) => di.inject(fsInjectable).pathExistsSync, -}); - -export default pathExistsSyncInjectable; diff --git a/src/common/fs/path-exists.injectable.ts b/src/common/fs/path-exists.injectable.ts deleted file mode 100644 index aee6cac52b..0000000000 --- a/src/common/fs/path-exists.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type PathExists = (path: string) => Promise; - -const pathExistsInjectable = getInjectable({ - id: "path-exists", - instantiate: (di): PathExists => di.inject(fsInjectable).pathExists, -}); - -export default pathExistsInjectable; diff --git a/src/common/fs/read-directory.global-override-for-injectable.ts b/src/common/fs/read-directory.global-override-for-injectable.ts deleted file mode 100644 index 57c83ceffb..0000000000 --- a/src/common/fs/read-directory.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import readDirectoryInjectable from "./read-directory.injectable"; - -export default getGlobalOverride(readDirectoryInjectable, () => async () => { - throw new Error("tried to read a directory's content without override"); -}); diff --git a/src/common/fs/read-directory.injectable.ts b/src/common/fs/read-directory.injectable.ts deleted file mode 100644 index 8ebeebe75a..0000000000 --- a/src/common/fs/read-directory.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 { Dirent } from "fs"; -import fsInjectable from "./fs.injectable"; - -export interface ReadDirectory { - ( - path: string, - options: "buffer" | { encoding: "buffer"; withFileTypes?: false | undefined } - ): Promise; - ( - path: string, - options?: - | { encoding: BufferEncoding; withFileTypes?: false | undefined } - | BufferEncoding - ): Promise; - ( - path: string, - options?: { encoding?: BufferEncoding; withFileTypes?: false | undefined }, - ): Promise; - ( - path: string, - options: { encoding?: BufferEncoding; withFileTypes: true }, - ): Promise; -} - -const readDirectoryInjectable = getInjectable({ - id: "read-directory", - instantiate: (di): ReadDirectory => di.inject(fsInjectable).readdir, -}); - -export default readDirectoryInjectable; diff --git a/src/common/fs/read-file-buffer-sync.injectable.ts b/src/common/fs/read-file-buffer-sync.injectable.ts deleted file mode 100644 index 98ba8e6d4b..0000000000 --- a/src/common/fs/read-file-buffer-sync.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type ReadFileBufferSync = (filePath: string) => Buffer; - -const readFileBufferSyncInjectable = getInjectable({ - id: "read-file-buffer-sync", - instantiate: (di): ReadFileBufferSync => { - const { readFileSync } = di.inject(fsInjectable); - - return (filePath) => readFileSync(filePath); - }, -}); - -export default readFileBufferSyncInjectable; diff --git a/src/common/fs/read-file-buffer.injectable.ts b/src/common/fs/read-file-buffer.injectable.ts deleted file mode 100644 index f7707dc594..0000000000 --- a/src/common/fs/read-file-buffer.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -const readFileBufferInjectable = getInjectable({ - id: "read-file-buffer", - - instantiate: (di) => (filePath: string) => - di.inject(fsInjectable).readFile(filePath), -}); - -export default readFileBufferInjectable; diff --git a/src/common/fs/read-file-sync.injectable.ts b/src/common/fs/read-file-sync.injectable.ts deleted file mode 100644 index cdb2e4b4d2..0000000000 --- a/src/common/fs/read-file-sync.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type ReadFileSync = (filePath: string) => string; - -const readFileSyncInjectable = getInjectable({ - id: "read-file-sync", - instantiate: (di): ReadFileSync => { - const { readFileSync } = di.inject(fsInjectable); - - return (filePath) => readFileSync(filePath, "utf-8"); - }, -}); - -export default readFileSyncInjectable; diff --git a/src/common/fs/read-file.injectable.ts b/src/common/fs/read-file.injectable.ts deleted file mode 100644 index 0dc539e1b1..0000000000 --- a/src/common/fs/read-file.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type ReadFile = (filePath: string) => Promise; - -const readFileInjectable = getInjectable({ - id: "read-file", - - instantiate: (di): ReadFile => { - const { readFile } = di.inject(fsInjectable); - - return (filePath) => readFile(filePath, "utf-8"); - }, -}); - -export default readFileInjectable; diff --git a/src/common/fs/read-json-file.injectable.ts b/src/common/fs/read-json-file.injectable.ts deleted file mode 100644 index d270368cf9..0000000000 --- a/src/common/fs/read-json-file.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 { JsonValue } from "type-fest"; -import fsInjectable from "./fs.injectable"; - -export type ReadJson = (filePath: string) => Promise; - -const readJsonFileInjectable = getInjectable({ - id: "read-json-file", - instantiate: (di): ReadJson => di.inject(fsInjectable).readJson, -}); - -export default readJsonFileInjectable; diff --git a/src/common/fs/read-json-sync.injectable.ts b/src/common/fs/read-json-sync.injectable.ts deleted file mode 100644 index 81a9ef478f..0000000000 --- a/src/common/fs/read-json-sync.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -const readJsonSyncInjectable = getInjectable({ - id: "read-json-sync", - instantiate: (di) => di.inject(fsInjectable).readJsonSync, -}); - -export default readJsonSyncInjectable; diff --git a/src/common/fs/read-yaml-file.injectable.ts b/src/common/fs/read-yaml-file.injectable.ts deleted file mode 100644 index a34a69d388..0000000000 --- a/src/common/fs/read-yaml-file.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 readFileInjectable from "./read-file.injectable"; -import yaml from "js-yaml"; - -export type ReadYamlFile = (filePath: string) => Promise; - -const readYamlFileInjectable = getInjectable({ - id: "read-yaml-file", - - instantiate: (di): ReadYamlFile => { - const readFile = di.inject(readFileInjectable); - - return async (filePath: string) => { - const contents = await readFile(filePath); - - return yaml.load(contents); - }; - }, -}); - -export default readYamlFileInjectable; diff --git a/src/common/fs/remove.global-override-for-injectable.ts b/src/common/fs/remove.global-override-for-injectable.ts deleted file mode 100644 index 4b92353344..0000000000 --- a/src/common/fs/remove.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import removePathInjectable from "./remove.injectable"; - -export default getGlobalOverride(removePathInjectable, () => async () => { - throw new Error("tried to remove path without override"); -}); diff --git a/src/common/fs/remove.injectable.ts b/src/common/fs/remove.injectable.ts deleted file mode 100644 index cf6f03987b..0000000000 --- a/src/common/fs/remove.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 fsInjectable from "./fs.injectable"; - -export type RemovePath = (filePath: string) => Promise; - -const removePathInjectable = getInjectable({ - id: "remove-path", - instantiate: (di): RemovePath => { - const { rm } = di.inject(fsInjectable); - - return (filePath) => rm(filePath, { force: true, recursive: true }); - }, -}); - -export default removePathInjectable; diff --git a/src/common/fs/stat.injectable.ts b/src/common/fs/stat.injectable.ts deleted file mode 100644 index 07f2b298b1..0000000000 --- a/src/common/fs/stat.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 { Stats } from "fs"; -import fsInjectable from "./fs.injectable"; - -export type Stat = (path: string) => Promise; - -const statInjectable = getInjectable({ - id: "stat", - instantiate: (di): Stat => di.inject(fsInjectable).stat, -}); - -export default statInjectable; diff --git a/src/common/fs/validate-directory.injectable.ts b/src/common/fs/validate-directory.injectable.ts deleted file mode 100644 index efce915238..0000000000 --- a/src/common/fs/validate-directory.injectable.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 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 { AsyncResult } from "../utils/async-result"; -import { isErrnoException } from "../utils"; -import type { Stats } from "fs-extra"; -import { lowerFirst } from "lodash/fp"; -import statInjectable from "./stat.injectable"; - -export type ValidateDirectory = (path: string) => Promise>; - -function getUserReadableFileType(stats: Stats): string { - if (stats.isFile()) { - return "a file"; - } - - if (stats.isFIFO()) { - return "a pipe"; - } - - if (stats.isSocket()) { - return "a socket"; - } - - if (stats.isBlockDevice()) { - return "a block device"; - } - - if (stats.isCharacterDevice()) { - return "a character device"; - } - - return "an unknown file type"; -} - -const validateDirectoryInjectable = getInjectable({ - id: "validate-directory", - - instantiate: (di): ValidateDirectory => { - const stat = di.inject(statInjectable); - - return async (path) => { - try { - const stats = await stat(path); - - if (stats.isDirectory()) { - return { callWasSuccessful: true, response: undefined }; - } - - return { callWasSuccessful: false, error: `the provided path is ${getUserReadableFileType(stats)} and not a directory.` }; - } catch (error) { - if (!isErrnoException(error)) { - return { callWasSuccessful: false, error: "of an unknown error, please try again." }; - } - - const humanReadableErrors: Record = { - ENOENT: "the provided path does not exist.", - EACCES: "search permissions is denied for one of the directories in the prefix of the provided path.", - ELOOP: "the provided path is a sym-link which points to a chain of sym-links that is too long to resolve. Perhaps it is cyclic.", - ENAMETOOLONG: "the pathname is too long to be used.", - ENOTDIR: "a prefix of the provided path is not a directory.", - }; - - const humanReadableError = error.code - ? humanReadableErrors[error.code] - : lowerFirst(String(error)); - - return { callWasSuccessful: false, error: humanReadableError }; - } - }; - }, -}); - -export default validateDirectoryInjectable; diff --git a/src/common/fs/watch/watch.global-override-for-injectable.ts b/src/common/fs/watch/watch.global-override-for-injectable.ts deleted file mode 100644 index 689c7150cf..0000000000 --- a/src/common/fs/watch/watch.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getGlobalOverride } from "../../test-utils/get-global-override"; -import watchInjectable from "./watch.injectable"; - -export default getGlobalOverride(watchInjectable, () => () => { - throw new Error("Tried to call file system watch without explicit override"); -}); diff --git a/src/common/fs/watch/watch.injectable.ts b/src/common/fs/watch/watch.injectable.ts deleted file mode 100644 index 50f96cdf57..0000000000 --- a/src/common/fs/watch/watch.injectable.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * 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 { watch } from "chokidar"; -import type { Stats } from "fs"; -import type TypedEventEmitter from "typed-emitter"; -import type { SingleOrMany } from "../../utils"; - -export interface AlwaysStatWatcherEvents { - add: (path: string, stats: Stats) => void; - addDir: (path: string, stats: Stats) => void; - change: (path: string, stats: Stats) => void; -} - -export interface MaybeStatWatcherEvents { - add: (path: string, stats?: Stats) => void; - addDir: (path: string, stats?: Stats) => void; - change: (path: string, stats?: Stats) => void; -} - -export type WatcherEvents = BaseWatcherEvents - & ( - AlwaysStat extends true - ? AlwaysStatWatcherEvents - : MaybeStatWatcherEvents - ); - -export interface BaseWatcherEvents { - error: (error: Error) => void; - ready: () => void; - unlink: (path: string) => void; - unlinkDir: (path: string) => void; -} - -export interface Watcher extends TypedEventEmitter> { - close: () => Promise; -} - -export type WatcherOptions = { - /** - * Indicates whether the process should continue to run as long as files are being watched. If - * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, - * even if the process continues to run. - */ - persistent?: boolean; - - /** - * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to - * be ignored. The whole relative or absolute path is tested, not just filename. If a function - * with two arguments is provided, it gets called twice per path - once with a single argument - * (the path), second time with two arguments (the path and the - * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). - */ - ignored?: SingleOrMany boolean)>; - - /** - * If set to `false` then `add`/`addDir` events are also emitted for matching paths while - * instantiating the watching as chokidar discovers these file paths (before the `ready` event). - */ - ignoreInitial?: boolean; - - /** - * When `false`, only the symlinks themselves will be watched for changes instead of following - * the link references and bubbling events through the link's path. - */ - followSymlinks?: boolean; - - /** - * The base directory from which watch `paths` are to be derived. Paths emitted with events will - * be relative to this. - */ - cwd?: string; - - /** - * If set to true then the strings passed to .watch() and .add() are treated as literal path - * names, even if they look like globs. Default: false. - */ - disableGlobbing?: boolean; - - /** - * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU - * utilization, consider setting this to `false`. It is typically necessary to **set this to - * `true` to successfully watch files over a network**, and it may be necessary to successfully - * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides - * the `useFsEvents` default. - */ - usePolling?: boolean; - - /** - * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly - * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on - * OS X, `usePolling: true` becomes the default. - */ - useFsEvents?: boolean; - - /** - * If set, limits how many levels of subdirectories will be traversed. - */ - depth?: number; - - /** - * Interval of file system polling. - */ - interval?: number; - - /** - * Interval of file system polling for binary files. ([see list of binary extensions](https://gi - * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) - */ - binaryInterval?: number; - - /** - * Indicates whether to watch files that don't have read permissions if possible. If watching - * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed - * silently. - */ - ignorePermissionErrors?: boolean; - - /** - * `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts - * that occur when using editors that use "atomic writes" instead of writing directly to the - * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` - * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, - * you can override it by setting `atomic` to a custom value, in milliseconds. - */ - atomic?: boolean | number; - - /** - * can be set to an object in order to adjust timing params: - */ - awaitWriteFinish?: AwaitWriteFinishOptions | boolean; -} & (AlwaysStat extends true - ? { - alwaysStat: true; - } - : { - alwaysStat?: false; - } -); - -export interface AwaitWriteFinishOptions { - /** - * Amount of time in milliseconds for a file size to remain constant before emitting its event. - */ - stabilityThreshold?: number; - - /** - * File size polling interval. - */ - pollInterval?: number; -} - -export type Watch = (path: string, options?: WatcherOptions) => Watcher; - -// TODO: Introduce wrapper to allow simpler API -const watchInjectable = getInjectable({ - id: "watch", - instantiate: () => watch as Watch, - causesSideEffects: true, -}); - -export default watchInjectable; diff --git a/src/common/fs/write-buffer-sync.injectable.ts b/src/common/fs/write-buffer-sync.injectable.ts deleted file mode 100644 index d4d253ae66..0000000000 --- a/src/common/fs/write-buffer-sync.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 getDirnameOfPathInjectable from "../path/get-dirname.injectable"; -import fsInjectable from "./fs.injectable"; - -export type WriteBufferSync = (filePath: string, contents: Buffer) => void; - -const writeBufferSyncInjectable = getInjectable({ - id: "write-buffer-sync", - instantiate: (di): WriteBufferSync => { - const { - writeFileSync, - ensureDirSync, - } = di.inject(fsInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return (filePath, contents) => { - ensureDirSync(getDirnameOfPath(filePath), { - mode: 0o755, - }); - writeFileSync(filePath, contents); - }; - }, -}); - -export default writeBufferSyncInjectable; diff --git a/src/common/fs/write-file-sync.injectable.ts b/src/common/fs/write-file-sync.injectable.ts deleted file mode 100644 index 3daccaf610..0000000000 --- a/src/common/fs/write-file-sync.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 getDirnameOfPathInjectable from "../path/get-dirname.injectable"; -import fsInjectable from "./fs.injectable"; - -export type WriteFileSync = (filePath: string, contents: string) => void; - -const writeFileSyncInjectable = getInjectable({ - id: "write-file-sync", - instantiate: (di): WriteFileSync => { - const { - writeFileSync, - ensureDirSync, - } = di.inject(fsInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return (filePath, contents) => { - ensureDirSync(getDirnameOfPath(filePath), { - mode: 0o755, - }); - writeFileSync(filePath, contents); - }; - }, -}); - -export default writeFileSyncInjectable; diff --git a/src/common/fs/write-file.global-override-for-injectable.ts b/src/common/fs/write-file.global-override-for-injectable.ts deleted file mode 100644 index c8b7ef8e45..0000000000 --- a/src/common/fs/write-file.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import writeFileInjectable from "./write-file.injectable"; - -export default getGlobalOverride(writeFileInjectable, () => async () => { - throw new Error("tried to write file without override"); -}); diff --git a/src/common/fs/write-file.injectable.ts b/src/common/fs/write-file.injectable.ts deleted file mode 100644 index 75e07775e3..0000000000 --- a/src/common/fs/write-file.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 { WriteFileOptions } from "fs-extra"; -import getDirnameOfPathInjectable from "../path/get-dirname.injectable"; -import fsInjectable from "./fs.injectable"; - -export type WriteFile = (filePath: string, content: string | Buffer, opts?: WriteFileOptions) => Promise; - -const writeFileInjectable = getInjectable({ - id: "write-file", - - instantiate: (di): WriteFile => { - const { writeFile, ensureDir } = di.inject(fsInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return async (filePath, content, opts = {}) => { - await ensureDir(getDirnameOfPath(filePath), { - mode: 0o755, - ...opts, - }); - - const { encoding = "utf-8", ...options } = opts; - - await writeFile(filePath, content, { - encoding: encoding as BufferEncoding, - ...options, - }); - }; - }, -}); - -export default writeFileInjectable; diff --git a/src/common/fs/write-json-file.injectable.ts b/src/common/fs/write-json-file.injectable.ts deleted file mode 100644 index 5491487849..0000000000 --- a/src/common/fs/write-json-file.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 getDirnameOfPathInjectable from "../path/get-dirname.injectable"; -import fsInjectable from "./fs.injectable"; - -export type WriteJson = (filePath: string, contents: unknown) => Promise; - -const writeJsonFileInjectable = getInjectable({ - id: "write-json-file", - - instantiate: (di): WriteJson => { - const { writeJson, ensureDir } = di.inject(fsInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return async (filePath, content) => { - await ensureDir(getDirnameOfPath(filePath), { mode: 0o755 }); - - await writeJson(filePath, content, { - encoding: "utf-8", - spaces: 2, - }); - }; - }, -}); - -export default writeJsonFileInjectable; diff --git a/src/common/fs/write-json-sync.injectable.ts b/src/common/fs/write-json-sync.injectable.ts deleted file mode 100644 index eb4abc3936..0000000000 --- a/src/common/fs/write-json-sync.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 getDirnameOfPathInjectable from "../path/get-dirname.injectable"; -import fsInjectable from "./fs.injectable"; - -export type WriteJsonSync = (filePath: string, contents: unknown) => void; - -const writeJsonSyncInjectable = getInjectable({ - id: "write-json-sync", - instantiate: (di): WriteJsonSync => { - const { - writeJsonSync, - ensureDirSync, - } = di.inject(fsInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return (filePath, content) => { - ensureDirSync(getDirnameOfPath(filePath), { mode: 0o755 }); - - writeJsonSync(filePath, content, { - encoding: "utf-8", - spaces: 2, - }); - }; - }, -}); - -export default writeJsonSyncInjectable; diff --git a/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts b/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts deleted file mode 100644 index 5f51460a2b..0000000000 --- a/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import assert from "assert"; -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getConfigurationFileModelInjectable from "./get-configuration-file-model.injectable"; -import type Config from "conf"; -import readJsonSyncInjectable from "../fs/read-json-sync.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; -import { get, set } from "lodash"; -import semver from "semver"; - -const MIGRATION_KEY = `__internal__.migrations.version`; - -const _isVersionInRangeFormat = (version: string) => { - return semver.clean(version) === null; -}; - -const _shouldPerformMigration = (candidateVersion: string, previousMigratedVersion: string, versionToMigrate: string) => { - if (_isVersionInRangeFormat(candidateVersion)) { - if (previousMigratedVersion !== "0.0.0" && semver.satisfies(previousMigratedVersion, candidateVersion)) { - return false; - } - - return semver.satisfies(versionToMigrate, candidateVersion); - } - - if (semver.lte(candidateVersion, previousMigratedVersion)) { - return false; - } - - if (semver.gt(candidateVersion, versionToMigrate)) { - return false; - } - - return true; -}; - -export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { - const readJsonSync = di.inject(readJsonSyncInjectable); - const writeJsonSync = di.inject(writeJsonSyncInjectable); - - return (options) => { - assert(options.cwd, "Missing options.cwd"); - assert(options.configName, "Missing options.configName"); - assert(options.projectVersion, "Missing options.projectVersion"); - - const configFilePath = path.posix.join(options.cwd, `${options.configName}.json`); - let store: object = {}; - - try { - store = readJsonSync(configFilePath); - } catch { - // ignore - } - - const config = { - get store() { - return store; - }, - path: configFilePath, - get: (key: string) => get(store, key), - set: (key: string, value: unknown) => { - let currentState: object; - - try { - currentState = readJsonSync(configFilePath); - } catch { - currentState = {}; - } - - writeJsonSync(configFilePath, { - ...currentState, - [key]: value, - }); - store = readJsonSync(configFilePath); - }, - } as Partial as Config; - - // Migrate - { - const migrations = options.migrations ?? []; - const versionToMigrate = options.projectVersion; - let previousMigratedVersion = get(store, MIGRATION_KEY) || "0.0.0"; - const newerVersions = Object.entries(migrations) - .filter(([candidateVersion]) => _shouldPerformMigration(candidateVersion, previousMigratedVersion, versionToMigrate)); - - let storeBackup = { ...store }; - - for (const [version, migration] of newerVersions) { - try { - migration(config); - set(store, MIGRATION_KEY, version); - previousMigratedVersion = version; - storeBackup = { ...store }; - } - catch (error) { - store = storeBackup; - throw new Error(`Something went wrong during the migration! Changes applied to the store until this failed migration will be restored. ${error}`); - } - } - - if (_isVersionInRangeFormat(previousMigratedVersion) || !semver.eq(previousMigratedVersion, versionToMigrate)) { - set(store, MIGRATION_KEY, versionToMigrate); - } - } - - return config; - }; -}); diff --git a/src/common/get-configuration-file-model/get-configuration-file-model.injectable.ts b/src/common/get-configuration-file-model/get-configuration-file-model.injectable.ts deleted file mode 100644 index e167e464ef..0000000000 --- a/src/common/get-configuration-file-model/get-configuration-file-model.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 Config from "conf"; -import type { Options as ConfOptions } from "conf/dist/source/types"; - -export type GetConfigurationFileModel = (content: ConfOptions) => Config; - -const getConfigurationFileModelInjectable = getInjectable({ - id: "get-configuration-file-model", - instantiate: (): GetConfigurationFileModel => (content) => new Config(content), - causesSideEffects: true, -}); - -export default getConfigurationFileModelInjectable; diff --git a/src/common/helm/add-helm-repository-channel.ts b/src/common/helm/add-helm-repository-channel.ts deleted file mode 100644 index bf5aa19367..0000000000 --- a/src/common/helm/add-helm-repository-channel.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { HelmRepo } from "./helm-repo"; -import type { AsyncResult } from "../utils/async-result"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; - -export type AddHelmRepositoryChannel = RequestChannel>; - -export const addHelmRepositoryChannel: AddHelmRepositoryChannel = { - id: "add-helm-repository-channel", -}; diff --git a/src/common/helm/get-active-helm-repositories-channel.ts b/src/common/helm/get-active-helm-repositories-channel.ts deleted file mode 100644 index 26720e9a5f..0000000000 --- a/src/common/helm/get-active-helm-repositories-channel.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { HelmRepo } from "./helm-repo"; -import type { AsyncResult } from "../utils/async-result"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; - -export type GetActiveHelmRepositoriesChannel = RequestChannel>; - -export const getActiveHelmRepositoriesChannel: GetActiveHelmRepositoriesChannel = { - id: "get-helm-active-list-repositories", -}; diff --git a/src/common/helm/helm-repo.ts b/src/common/helm/helm-repo.ts deleted file mode 100644 index cb4af3449b..0000000000 --- a/src/common/helm/helm-repo.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface HelmRepo { - name: string; - url: string; - cacheFilePath: string; - caFile?: string; - certFile?: string; - insecureSkipTlsVerify?: boolean; - keyFile?: string; - username?: string; - password?: string; -} diff --git a/src/common/helm/remove-helm-repository-channel.ts b/src/common/helm/remove-helm-repository-channel.ts deleted file mode 100644 index 4d479d088c..0000000000 --- a/src/common/helm/remove-helm-repository-channel.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AsyncResult } from "../utils/async-result"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; -import type { HelmRepo } from "./helm-repo"; - -export type RemoveHelmRepositoryChannel = RequestChannel>; - -export const removeHelmRepositoryChannel: RemoveHelmRepositoryChannel = { - id: "remove-helm-repository-channel", -}; diff --git a/src/common/hotbars/add-hotbar.injectable.ts b/src/common/hotbars/add-hotbar.injectable.ts deleted file mode 100644 index 25ee0f588a..0000000000 --- a/src/common/hotbars/add-hotbar.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 hotbarStoreInjectable from "./store.injectable"; -import type { CreateHotbarData, CreateHotbarOptions } from "./types"; - -export type AddHotbar = (data: CreateHotbarData, opts?: CreateHotbarOptions) => void; - -const addHotbarInjectable = getInjectable({ - id: "add-hotbar", - instantiate: (di): AddHotbar => { - const store = di.inject(hotbarStoreInjectable); - - return (data, opts) => store.add(data, opts); - }, -}); - -export default addHotbarInjectable; diff --git a/src/common/hotbars/migrations-token.ts b/src/common/hotbars/migrations-token.ts deleted file mode 100644 index 5441844933..0000000000 --- a/src/common/hotbars/migrations-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; - -export const hotbarStoreMigrationInjectionToken = getInjectionToken({ - id: "hotbar-store-migration-token", -}); diff --git a/src/common/hotbars/store.injectable.ts b/src/common/hotbars/store.injectable.ts deleted file mode 100644 index cc15f93bf8..0000000000 --- a/src/common/hotbars/store.injectable.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import { HotbarStore } from "./store"; -import loggerInjectable from "../logger.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { hotbarStoreMigrationInjectionToken } from "./migrations-token"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; - -const hotbarStoreInjectable = getInjectable({ - id: "hotbar-store", - - instantiate: (di) => new HotbarStore({ - catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable), - logger: di.inject(loggerInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, hotbarStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default hotbarStoreInjectable; diff --git a/src/common/hotbars/store.ts b/src/common/hotbars/store.ts deleted file mode 100644 index 709242f20e..0000000000 --- a/src/common/hotbars/store.ts +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, comparer, observable, makeObservable, computed } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { toJS } from "../utils"; -import type { CatalogEntity } from "../catalog"; -import { broadcastMessage } from "../ipc"; -import type { Hotbar, CreateHotbarData, CreateHotbarOptions } from "./types"; -import { defaultHotbarCells, getEmptyHotbar } from "./types"; -import { hotbarTooManyItemsChannel } from "../ipc/hotbar"; -import type { GeneralEntity } from "../catalog-entities"; -import type { Logger } from "../logger"; -import assert from "assert"; - -export interface HotbarStoreModel { - hotbars: Hotbar[]; - activeHotbarId: string; -} - -interface Dependencies extends BaseStoreDependencies { - readonly catalogCatalogEntity: GeneralEntity; - readonly logger: Logger; -} - -export class HotbarStore extends BaseStore { - @observable hotbars: Hotbar[] = []; - @observable private _activeHotbarId!: string; - - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-hotbar-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - makeObservable(this); - } - - @computed get activeHotbarId() { - return this._activeHotbarId; - } - - /** - * If `hotbar` points to a known hotbar, make it active. Otherwise, ignore - * @param hotbar The hotbar instance, or the index, or its ID - */ - setActiveHotbar(hotbar: Hotbar | number | string) { - if (typeof hotbar === "number") { - if (hotbar >= 0 && hotbar < this.hotbars.length) { - this._activeHotbarId = this.hotbars[hotbar].id; - } - } else if (typeof hotbar === "string") { - if (this.findById(hotbar)) { - this._activeHotbarId = hotbar; - } - } else { - if (this.hotbars.indexOf(hotbar) >= 0) { - this._activeHotbarId = hotbar.id; - } - } - } - - private hotbarIndexById(id: string) { - return this.hotbars.findIndex((hotbar) => hotbar.id === id); - } - - private hotbarIndex(hotbar: Hotbar) { - return this.hotbars.indexOf(hotbar); - } - - @computed get activeHotbarIndex() { - return this.hotbarIndexById(this.activeHotbarId); - } - - @action - protected fromStore(data: Partial = {}) { - if (!data.hotbars || !data.hotbars.length) { - const hotbar = getEmptyHotbar("Default"); - const { - metadata: { uid, name, source }, - } = this.dependencies.catalogCatalogEntity; - const initialItem = { entity: { uid, name, source }}; - - hotbar.items[0] = initialItem; - - this.hotbars = [hotbar]; - } else { - this.hotbars = data.hotbars; - } - - this.hotbars.forEach(ensureExactHotbarItemLength); - - if (data.activeHotbarId) { - this._activeHotbarId = data.activeHotbarId; - } - - if (!this._activeHotbarId) { - this._activeHotbarId = this.hotbars[0].id; - } - } - - toJSON(): HotbarStoreModel { - return toJS({ - hotbars: this.hotbars, - activeHotbarId: this.activeHotbarId, - }); - } - - getActive(): Hotbar { - const hotbar = this.findById(this.activeHotbarId); - - assert(hotbar, "There MUST always be an active hotbar"); - - return hotbar; - } - - findByName(name: string) { - return this.hotbars.find((hotbar) => hotbar.name === name); - } - - findById(id: string) { - return this.hotbars.find((hotbar) => hotbar.id === id); - } - - @action - add(data: CreateHotbarData, { setActive = false }: CreateHotbarOptions = {}) { - const hotbar = getEmptyHotbar(data.name, data.id); - - this.hotbars.push(hotbar); - - if (setActive) { - this._activeHotbarId = hotbar.id; - } - } - - @action - setHotbarName(id: string, name: string): void { - const index = this.hotbars.findIndex((hotbar) => hotbar.id === id); - - if (index < 0) { - return this.dependencies.logger.warn( - `[HOTBAR-STORE]: cannot setHotbarName: unknown id`, - { id }, - ); - } - - this.hotbars[index].name = name; - } - - @action - remove(hotbar: Hotbar) { - assert(this.hotbars.length >= 2, "Cannot remove the last hotbar"); - - this.hotbars = this.hotbars.filter((h) => h !== hotbar); - - if (this.activeHotbarId === hotbar.id) { - this.setActiveHotbar(0); - } - } - - @action - addToHotbar(item: CatalogEntity, cellIndex?: number) { - const hotbar = this.getActive(); - const uid = item.getId(); - const name = item.getName(); - - if (typeof uid !== "string") { - throw new TypeError("CatalogEntity's ID must be a string"); - } - - if (typeof name !== "string") { - throw new TypeError("CatalogEntity's NAME must be a string"); - } - - if (this.isAddedToActive(item)) { - return; - } - - const entity = { - uid, - name, - source: item.metadata.source, - }; - const newItem = { entity }; - - if (cellIndex === undefined) { - // Add item to empty cell - const emptyCellIndex = hotbar.items.indexOf(null); - - if (emptyCellIndex != -1) { - hotbar.items[emptyCellIndex] = newItem; - } else { - broadcastMessage(hotbarTooManyItemsChannel); - } - } else if (0 <= cellIndex && cellIndex < hotbar.items.length) { - hotbar.items[cellIndex] = newItem; - } else { - this.dependencies.logger.error( - `[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`, - { entityId: uid, hotbarId: hotbar.id, cellIndex }, - ); - } - } - - @action - removeFromHotbar(uid: string): void { - const hotbar = this.getActive(); - const index = hotbar.items.findIndex((item) => item?.entity.uid === uid); - - if (index < 0) { - return; - } - - hotbar.items[index] = null; - } - - /** - * Remove all hotbar items that reference the `uid`. - * @param uid The `EntityId` that each hotbar item refers to - * @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed. - */ - @action - removeAllHotbarItems(uid: string) { - for (const hotbar of this.hotbars) { - const index = hotbar.items.findIndex((i) => i?.entity.uid === uid); - - if (index >= 0) { - hotbar.items[index] = null; - } - } - } - - findClosestEmptyIndex(from: number, direction = 1) { - let index = from; - const hotbar = this.getActive(); - - while (hotbar.items[index] != null) { - index += direction; - } - - return index; - } - - @action - restackItems(from: number, to: number): void { - const { items } = this.getActive(); - const source = items[from]; - const moveDown = from < to; - - if ( - from < 0 || - to < 0 || - from >= items.length || - to >= items.length || - isNaN(from) || - isNaN(to) - ) { - throw new Error("Invalid 'from' or 'to' arguments"); - } - - if (from == to) { - return; - } - - items.splice(from, 1, null); - - if (items[to] == null) { - items.splice(to, 1, source); - } else { - // Move cells up or down to closes empty cell - items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1); - items.splice(to, 0, source); - } - } - - switchToPrevious() { - let index = this.activeHotbarIndex - 1; - - if (index < 0) { - index = this.hotbars.length - 1; - } - - this.setActiveHotbar(index); - } - - switchToNext() { - let index = this.activeHotbarIndex + 1; - - if (index >= this.hotbars.length) { - index = 0; - } - - this.setActiveHotbar(index); - } - - /** - * Checks if entity already pinned to the active hotbar - */ - isAddedToActive(entity: CatalogEntity | null | undefined): boolean { - if (!entity) { - return false; - } - - const indexInActiveHotbar = this.getActive().items.findIndex(item => item?.entity.uid === entity.getId()); - - return indexInActiveHotbar >= 0; - } - - getDisplayLabel(hotbar: Hotbar): string { - return `${this.getDisplayIndex(hotbar)}: ${hotbar.name}`; - } - - getDisplayIndex(hotbar: Hotbar): string { - const index = this.hotbarIndex(hotbar); - - if (index < 0) { - return "??"; - } - - return `${index + 1}`; - } -} - -/** - * This function ensures that there are always exactly `defaultHotbarCells` - * worth of items in the hotbar. - * @param hotbar The hotbar to modify - */ -function ensureExactHotbarItemLength(hotbar: Hotbar) { - // if there are not enough items - while (hotbar.items.length < defaultHotbarCells) { - hotbar.items.push(null); - } - - // if for some reason the hotbar was overfilled before, remove as many entries - // as needed, but prefer empty slots and items at the end first. - while (hotbar.items.length > defaultHotbarCells) { - const lastNull = hotbar.items.lastIndexOf(null); - - if (lastNull >= 0) { - hotbar.items.splice(lastNull, 1); - } else { - hotbar.items.length = defaultHotbarCells; - } - } -} diff --git a/src/common/hotbars/types.ts b/src/common/hotbars/types.ts deleted file mode 100644 index 6370fe136d..0000000000 --- a/src/common/hotbars/types.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as uuid from "uuid"; -import type { Tuple } from "../utils"; -import { tuple } from "../utils"; - -export interface HotbarItem { - entity: { - uid: string; - name: string; - source?: string; - }; - params?: { - [key: string]: string; - }; -} - -export type Hotbar = Required; - -export interface CreateHotbarData { - id?: string; - name: string; - items?: Tuple; -} - -export interface CreateHotbarOptions { - setActive?: boolean; -} - -export const defaultHotbarCells = 12; // Number is chosen to easy hit any item with keyboard - -export function getEmptyHotbar(name: string, id: string = uuid.v4()): Hotbar { - return { - id, - items: tuple.filled(defaultHotbarCells, null), - name, - }; -} diff --git a/src/common/initializable-state/create.test.ts b/src/common/initializable-state/create.test.ts deleted file mode 100644 index 38980b0a41..0000000000 --- a/src/common/initializable-state/create.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import type { DiContainer, Injectable } from "@ogre-tools/injectable"; -import { runInAction } from "mobx"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import type { InitializableState } from "./create"; -import { createInitializableState } from "./create"; - -describe("InitializableState tests", () => { - let di: DiContainer; - - beforeEach(() => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - }); - - describe("when created", () => { - let stateInjectable: Injectable, unknown, void>; - let initMock: AsyncFnMock<() => number>; - - beforeEach(() => { - initMock = asyncFn(); - stateInjectable = createInitializableState({ - id: "my-state", - init: initMock, - }); - - runInAction(() => { - di.register(stateInjectable); - }); - }); - - describe("when injected", () => { - let state: InitializableState; - - beforeEach(() => { - state = di.inject(stateInjectable); - }); - - it("when get is called, throw", () => { - expect(() => state.get()).toThrowError("InitializableState(my-state) has not been initialized yet"); - }); - - describe("when init is called", () => { - beforeEach(() => { - state.init(); - }); - - it("should call provided initialization function", () => { - expect(initMock).toBeCalled(); - }); - - it("when get is called, throw", () => { - expect(() => state.get()).toThrowError("InitializableState(my-state) has not finished initializing"); - }); - - describe("when initialization resolves", () => { - beforeEach(async () => { - await initMock.resolve(42); - }); - - it("when get is called, returns value", () => { - expect(state.get()).toBe(42); - }); - - it("when init is called again, throws", async () => { - await expect(() => state.init()).rejects.toThrow("Cannot initialize InitializableState(my-state) more than once"); - }); - }); - }); - }); - }); -}); diff --git a/src/common/initializable-state/create.ts b/src/common/initializable-state/create.ts deleted file mode 100644 index 829de57d94..0000000000 --- a/src/common/initializable-state/create.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable"; -import { getInjectable } from "@ogre-tools/injectable"; - -export interface CreateInitializableStateArgs { - id: string; - init: (di: DiContainerForInjection) => Promise | T; - injectionToken?: InjectionToken, void>; -} - -export interface InitializableState { - get: () => T; - init: () => Promise; -} - -type InitializableStateValue = - | { set: false } - | { set: true; value: T } ; - -export function createInitializableState(args: CreateInitializableStateArgs): Injectable, unknown, void> { - const { id, init, injectionToken } = args; - - return getInjectable({ - id, - instantiate: (di) => { - let box: InitializableStateValue = { - set: false, - }; - let initCalled = false; - - return { - init: async () => { - if (initCalled) { - throw new Error(`Cannot initialize InitializableState(${id}) more than once`); - } - - initCalled = true; - box = { - set: true, - value: await init(di), - }; - }, - get: () => { - if (!initCalled) { - throw new Error(`InitializableState(${id}) has not been initialized yet`); - } - - if (box.set === false) { - throw new Error(`InitializableState(${id}) has not finished initializing`); - } - - return box.value; - }, - }; - }, - injectionToken, - }); -} diff --git a/src/common/ipc/broadcast-message.global-override-for-injectable.ts b/src/common/ipc/broadcast-message.global-override-for-injectable.ts deleted file mode 100644 index e455b30cdc..0000000000 --- a/src/common/ipc/broadcast-message.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; -import broadcastMessageInjectable from "./broadcast-message.injectable"; - -export default getGlobalOverrideForFunction(broadcastMessageInjectable); diff --git a/src/common/ipc/broadcast-message.injectable.ts b/src/common/ipc/broadcast-message.injectable.ts deleted file mode 100644 index 41db749d65..0000000000 --- a/src/common/ipc/broadcast-message.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 { broadcastMessage } from "./ipc"; - -export type BroadcastMessage = (channel: string, ...args: any[]) => Promise; - -const broadcastMessageInjectable = getInjectable({ - id: "broadcast-message", - instantiate: (): BroadcastMessage => broadcastMessage, - causesSideEffects: true, -}); - -export default broadcastMessageInjectable; diff --git a/src/common/ipc/catalog.ts b/src/common/ipc/catalog.ts deleted file mode 100644 index 3d316d211c..0000000000 --- a/src/common/ipc/catalog.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * This is used to activate a specific entity in the renderer main frame - */ -export const catalogEntityRunListener = "catalog-entity:run"; - -/** - * This is broadcast on whenever there is an update to any catalog item - */ -export const catalogItemsChannel = "catalog:items"; - -/** - * This can be sent from renderer to main to initialize a broadcast of ITEMS - */ -export const catalogInitChannel = "catalog:init"; diff --git a/src/common/ipc/cluster.ts b/src/common/ipc/cluster.ts deleted file mode 100644 index 53061abf60..0000000000 --- a/src/common/ipc/cluster.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const clusterActivateHandler = "cluster:activate"; -export const clusterSetFrameIdHandler = "cluster:set-frame-id"; -export const clusterVisibilityHandler = "cluster:visibility"; -export const clusterDisconnectHandler = "cluster:disconnect"; -export const clusterStates = "cluster:states"; - -/** - * This channel is broadcast on whenever the cluster fails to list namespaces - * during a refresh and no `accessibleNamespaces` have been set. - */ -export const clusterListNamespaceForbiddenChannel = "cluster:list-namespace-forbidden"; - -export type ListNamespaceForbiddenArgs = [clusterId: string]; - -export function isListNamespaceForbiddenArgs(args: unknown[]): args is ListNamespaceForbiddenArgs { - return args.length === 1 && typeof args[0] === "string"; -} diff --git a/src/common/ipc/extension-handling.ts b/src/common/ipc/extension-handling.ts deleted file mode 100644 index 1474a6d18d..0000000000 --- a/src/common/ipc/extension-handling.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const extensionDiscoveryStateChannel = "extension-discovery:state"; -export const bundledExtensionsLoaded = "extension-loader:bundled-extensions-loaded"; -export const extensionLoaderFromMainChannel = "extension-loader:main:state"; -export const extensionLoaderFromRendererChannel = "extension-loader:renderer:state"; diff --git a/src/common/ipc/hotbar.ts b/src/common/ipc/hotbar.ts deleted file mode 100644 index 3617ebd6c5..0000000000 --- a/src/common/ipc/hotbar.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const hotbarTooManyItemsChannel = "hotbar:too-many-items"; diff --git a/src/common/ipc/index.ts b/src/common/ipc/index.ts deleted file mode 100644 index aa2a538560..0000000000 --- a/src/common/ipc/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./ipc"; -export * from "./invalid-kubeconfig"; diff --git a/src/common/ipc/invalid-kubeconfig/index.ts b/src/common/ipc/invalid-kubeconfig/index.ts deleted file mode 100644 index 04ebcb9a4a..0000000000 --- a/src/common/ipc/invalid-kubeconfig/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const InvalidKubeconfigChannel = "invalid-kubeconfig"; - -export type InvalidKubeConfigArgs = [clusterId: string]; diff --git a/src/common/ipc/ipc-main-injection-token.ts b/src/common/ipc/ipc-main-injection-token.ts deleted file mode 100644 index 932c700c97..0000000000 --- a/src/common/ipc/ipc-main-injection-token.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IpcMain } from "electron"; - -const ipcMainInjectionToken = getInjectionToken({ - id: "ipc-main-injection-token", -}); - -export default ipcMainInjectionToken; diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts deleted file mode 100644 index e84fcfec56..0000000000 --- a/src/common/ipc/ipc.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Inter-process communications (main <-> renderer) -// https://www.electronjs.org/docs/api/ipc-main -// https://www.electronjs.org/docs/api/ipc-renderer - -import { ipcMain, ipcRenderer, webContents } from "electron"; -import { toJS } from "../utils/toJS"; -import type { ClusterFrameInfo } from "../cluster-frames"; -import { clusterFrameMap } from "../cluster-frames"; -import type { Disposer } from "../utils"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable"; -import loggerInjectable from "../logger.injectable"; -import ipcMainInjectionToken from "./ipc-main-injection-token"; - -export const broadcastMainChannel = "ipc:broadcast-main"; - -export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { - const di = getLegacyGlobalDiForExtensionApi(); - - const ipcMain = di.inject(ipcMainInjectionToken); - - ipcMain.handle(channel, async (event, ...args) => { - return sanitizePayload(await listener(event, ...args)); - }); -} - -function getSubFrames(): ClusterFrameInfo[] { - return Array.from(clusterFrameMap.values()); -} - -export async function broadcastMessage(channel: string, ...args: any[]): Promise { - if (ipcRenderer) { - return ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(sanitizePayload)); - } - - if (!webContents) { - return; - } - - const di = getLegacyGlobalDiForExtensionApi(); - const logger = di.inject(loggerInjectable); - - ipcMain.listeners(channel).forEach((func) => func({ - processId: undefined, frameId: undefined, sender: undefined, senderFrame: undefined, - }, ...args)); - - const subFrames = getSubFrames(); - const views = webContents.getAllWebContents(); - - if (!views || !Array.isArray(views) || views.length === 0) return; - - args = args.map(sanitizePayload); - - for (const view of views) { - let viewType = "unknown"; - - // There will be a uncaught exception if the view is destroyed. - try { - viewType = view.getType(); - } catch { - // We can ignore the view destroyed exception as viewType is only used for logging. - } - - // Send message to views. - try { - logger.silly(`[IPC]: broadcasting "${channel}" to ${viewType}=${view.id}`, { args }); - view.send(channel, ...args); - } catch (error) { - logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"`, { error }); - } - - // Send message to subFrames of views. - for (const frameInfo of subFrames) { - logger.silly(`[IPC]: broadcasting "${channel}" to subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { args }); - - try { - view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args); - } catch (error) { - logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"'s subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { error: String(error) }); - } - } - } -} - -export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): Disposer { - const di = getLegacyGlobalDiForExtensionApi(); - - const ipcMain = di.inject(ipcMainInjectionToken); - - ipcMain.on(channel, listener); - - return () => ipcMain.off(channel, listener); -} - -export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { - const di = getLegacyGlobalDiForExtensionApi(); - - const ipcRenderer = di.inject(ipcRendererInjectable); - - ipcRenderer.on(channel, listener); - - return () => ipcRenderer.off(channel, listener); -} - -/** - * Sanitizing data for IPC-messaging before send. - * Removes possible observable values to avoid exceptions like "can't clone object". - */ -function sanitizePayload(data: any): T { - return toJS(data); -} diff --git a/src/common/ipc/navigation-events.ts b/src/common/ipc/navigation-events.ts deleted file mode 100644 index 452d16af9b..0000000000 --- a/src/common/ipc/navigation-events.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// -export const enum IpcRendererNavigationEvents { - NAVIGATE_IN_APP = "renderer:navigate", - NAVIGATE_IN_CLUSTER = "renderer:navigate-in-cluster", - LOADED = "renderer:loaded", -} diff --git a/src/common/ipc/window.ts b/src/common/ipc/window.ts deleted file mode 100644 index 23a83177c6..0000000000 --- a/src/common/ipc/window.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const windowActionHandleChannel = "window:window-action"; -export const windowOpenAppMenuAsContextMenuChannel = "window:open-app-context-menu"; -export const windowLocationChangedChannel = "window:location-changed"; - -/** - * The supported actions on the current window. The argument for `windowActionHandleChannel` - */ -export enum WindowAction { - /** - * Request that the current window goes back one step of browser history - */ - GO_BACK = "back", - - /** - * Request that the current window goes forward one step of browser history - */ - GO_FORWARD = "forward", - - /** - * Request that the current window is minimized - */ - MINIMIZE = "minimize", - - /** - * Request that the current window is maximized if it isn't, or unmaximized - * if it is - */ - TOGGLE_MAXIMIZE = "toggle-maximize", - - /** - * Request that the current window is closed - */ - CLOSE = "close", -} diff --git a/src/common/item.store.ts b/src/common/item.store.ts deleted file mode 100644 index 805137e95c..0000000000 --- a/src/common/item.store.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import orderBy from "lodash/orderBy"; -import { autoBind } from "./utils"; -import { action, computed, observable, when, makeObservable } from "mobx"; - -export interface ItemObject { - getId(): string; - getName(): string; -} - -export abstract class ItemStore { - protected defaultSorting = (item: Item) => item.getName(); - - @observable failedLoading = false; - @observable isLoading = false; - @observable isLoaded = false; - @observable items = observable.array([], { deep: false }); - @observable selectedItemsIds = observable.set(); - - constructor() { - makeObservable(this); - autoBind(this); - } - - @computed get selectedItems(): Item[] { - return this.pickOnlySelected(this.items); - } - - public pickOnlySelected(items: Item[]): Item[] { - return items.filter(item => this.selectedItemsIds.has(item.getId())); - } - - public getItems(): Item[] { - return Array.from(this.items); - } - - public getTotalCount(): number { - return this.items.length; - } - - getByName(name: string): Item | undefined { - return this.items.find(item => item.getName() === name); - } - - getIndexById(id: string): number { - return this.items.findIndex(item => item.getId() === id); - } - - /** - * Return `items` sorted by the given ordering functions. If two elements of - * `items` are sorted to the same "index" then the next sorting function is used - * to determine where to place them relative to each other. Once the `sorting` - * functions have been all exhausted then the order is unchanged (ie a stable sort). - * @param items the items to be sorted (default: the current items in this store) - * @param sorting list of functions to determine sort order (default: sorting by name) - * @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`) - */ - @action - protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] { - return orderBy(items, sorting, order); - } - - protected async createItem(...args: any[]): Promise; - @action - protected async createItem(request: () => Promise) { - const newItem = await request(); - const item = this.items.find(item => item.getId() === newItem.getId()); - - if (item) { - return item; - } else { - const items = this.sortItems([...this.items, newItem]); - - this.items.replace(items); - - return newItem; - } - } - - protected async loadItems(...args: any[]): Promise; - /** - * Load items to this.items - * @param request Function to return the items to be loaded. - * @param sortItems If true, items will be sorted. - * @param concurrency If true, concurrent loadItems() calls will all be executed. If false, only the first. - * @returns - */ - @action - protected async loadItems(request: () => Promise, sortItems = true, concurrency = false) { - if (this.isLoading) { - await when(() => !this.isLoading); - - // If concurrency for loading is disabled, return instead of loading - if (!concurrency) { - return; - } - } - this.isLoading = true; - - try { - let items = await request(); - - if (sortItems) items = this.sortItems(items); - this.items.replace(items); - this.isLoaded = true; - } finally { - this.isLoading = false; - } - } - - @action - protected async loadItem(request: () => Promise, sortItems = true) { - const item = await Promise.resolve(request()).catch(() => null); - - if (item) { - const existingItem = this.items.find(el => el.getId() === item.getId()); - - if (existingItem) { - const index = this.items.findIndex(item => item === existingItem); - - this.items.splice(index, 1, item); - } else { - let items = [...this.items, item]; - - if (sortItems) items = this.sortItems(items); - this.items.replace(items); - } - } - - return item; - } - - @action - protected async updateItem(item: Item, request: () => Promise) { - const updatedItem = await request(); - const index = this.items.findIndex(i => i.getId() === item.getId()); - - this.items.splice(index, 1, updatedItem); - - return updatedItem; - } - - @action - protected async removeItem(item: Item, request: () => Promise) { - await request(); - this.items.remove(item); - this.selectedItemsIds.delete(item.getId()); - } - - isSelected(item: Item) { - return this.selectedItemsIds.has(item.getId()); - } - - @action - select(item: Item) { - this.selectedItemsIds.add(item.getId()); - } - - @action - unselect(item: Item) { - this.selectedItemsIds.delete(item.getId()); - } - - @action - toggleSelection(item: Item) { - if (this.isSelected(item)) { - this.unselect(item); - } else { - this.select(item); - } - } - - @action - toggleSelectionAll(visibleItems: Item[] = this.items) { - const allSelected = visibleItems.every(this.isSelected); - - if (allSelected) { - visibleItems.forEach(this.unselect); - } else { - visibleItems.forEach(this.select); - } - } - - isSelectedAll(visibleItems: Item[] = this.items) { - if (!visibleItems.length) return false; - - return visibleItems.every(this.isSelected); - } - - @action - resetSelection() { - this.selectedItemsIds.clear(); - } - - @action - reset() { - this.resetSelection(); - this.items.clear(); - this.selectedItemsIds.clear(); - this.isLoaded = false; - this.isLoading = false; - } - - async removeSelectedItems?(): Promise; - - async removeItems?(items: Item[]): Promise; - - * [Symbol.iterator]() { - yield* this.items; - } -} diff --git a/src/common/k8s-api/__tests__/api-manager.test.ts b/src/common/k8s-api/__tests__/api-manager.test.ts deleted file mode 100644 index b83f52c3f0..0000000000 --- a/src/common/k8s-api/__tests__/api-manager.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DiContainer } from "@ogre-tools/injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable"; -import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; -import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import loggerInjectable from "../../logger.injectable"; -import type { ApiManager } from "../api-manager"; -import apiManagerInjectable from "../api-manager/manager.injectable"; -import { KubeApi } from "../kube-api"; -import { KubeObject } from "../kube-object"; -import { KubeObjectStore } from "../kube-object.store"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -class TestApi extends KubeApi { - protected async checkPreferredVersion() { - return; - } -} - -class TestStore extends KubeObjectStore { - -} - -describe("ApiManager", () => { - let apiManager: ApiManager; - let di: DiContainer; - - beforeEach(() => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - apiManager = di.inject(apiManagerInjectable); - }); - - describe("registerApi", () => { - it("re-register store if apiBase changed", async () => { - const apiBase = "apis/v1/foo"; - const fallbackApiBase = "/apis/extensions/v1beta1/foo"; - const kubeApi = new TestApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }, { - objectConstructor: KubeObject, - apiBase, - kind: "foo", - fallbackApiBases: [fallbackApiBase], - checkPreferredVersion: true, - }); - const kubeStore = new TestStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, kubeApi); - - apiManager.registerApi(apiBase, kubeApi); - - // Define to use test api for ingress store - Object.defineProperty(kubeStore, "api", { value: kubeApi }); - apiManager.registerStore(kubeStore, [kubeApi]); - - // Test that store is returned with original apiBase - expect(apiManager.getStore(kubeApi)).toBe(kubeStore); - - // Change apiBase similar as checkPreferredVersion does - Object.defineProperty(kubeApi, "apiBase", { value: fallbackApiBase }); - apiManager.registerApi(fallbackApiBase, kubeApi); - - // Test that store is returned with new apiBase - expect(apiManager.getStore(kubeApi)).toBe(kubeStore); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/crd.test.ts b/src/common/k8s-api/__tests__/crd.test.ts deleted file mode 100644 index e1538dfbe6..0000000000 --- a/src/common/k8s-api/__tests__/crd.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CustomResourceDefinitionSpec } from "../endpoints"; -import { CustomResourceDefinition } from "../endpoints"; - -describe("Crds", () => { - describe("getVersion()", () => { - it("should throw if none of the versions are served", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "apiextensions.k8s.io/v1", - kind: "CustomResourceDefinition", - metadata: { - name: "foo", - resourceVersion: "12345", - uid: "12345", - selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo", - }, - spec: { - group: "foo.bar", - names: { - kind: "Foo", - plural: "foos", - }, - scope: "Namespaced", - versions: [ - { - name: "123", - served: false, - storage: false, - }, - { - name: "1234", - served: false, - storage: false, - }, - ], - }, - }); - - expect(() => crd.getVersion()).toThrowError("Failed to find a version for CustomResourceDefinition foo"); - }); - - it("should get the version that is both served and stored", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "apiextensions.k8s.io/v1", - kind: "CustomResourceDefinition", - metadata: { - name: "foo", - resourceVersion: "12345", - uid: "12345", - selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo", - }, - spec: { - group: "foo.bar", - names: { - kind: "Foo", - plural: "foos", - }, - scope: "Namespaced", - versions: [ - { - name: "123", - served: true, - storage: true, - }, - { - name: "1234", - served: false, - storage: false, - }, - ], - }, - }); - - expect(crd.getVersion()).toBe("123"); - }); - - it("should get the version that is only stored", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "apiextensions.k8s.io/v1", - kind: "CustomResourceDefinition", - metadata: { - name: "foo", - resourceVersion: "12345", - uid: "12345", - selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo", - }, - spec: { - group: "foo.bar", - names: { - kind: "Foo", - plural: "foos", - }, - scope: "Namespaced", - versions: [ - { - name: "123", - served: false, - storage: true, - }, - { - name: "1234", - served: false, - storage: false, - }, - ], - }, - }); - - expect(crd.getVersion()).toBe("123"); - }); - - it("should get the version that is both served and stored even with version field", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "apiextensions.k8s.io/v1", - kind: "CustomResourceDefinition", - metadata: { - name: "foo", - resourceVersion: "12345", - uid: "12345", - selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo", - }, - spec: { - group: "foo.bar", - names: { - kind: "Foo", - plural: "foos", - }, - scope: "Namespaced", - version: "abc", - versions: [ - { - name: "123", - served: true, - storage: true, - }, - { - name: "1234", - served: false, - storage: false, - }, - ], - }, - }); - - expect(crd.getVersion()).toBe("123"); - }); - - it("should get the version name from the version field, ignoring versions on v1beta", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "apiextensions.k8s.io/v1beta1", - kind: "CustomResourceDefinition", - metadata: { - name: "foo", - resourceVersion: "12345", - uid: "12345", - selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo", - }, - spec: { - version: "abc", - versions: [ - { - name: "foobar", - served: true, - storage: true, - }, - ], - } as CustomResourceDefinitionSpec, - }); - - expect(crd.getVersion()).toBe("abc"); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/deployment.api.test.ts b/src/common/k8s-api/__tests__/deployment.api.test.ts deleted file mode 100644 index 60ada4e4d1..0000000000 --- a/src/common/k8s-api/__tests__/deployment.api.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; -import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; -import type { DeploymentApi } from "../endpoints/deployment.api"; -import deploymentApiInjectable from "../endpoints/deployment.api.injectable"; -import type { KubeJsonApi } from "../kube-json-api"; - -describe("DeploymentApi", () => { - let deploymentApi: DeploymentApi; - let kubeJsonApi: jest.Mocked; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - kubeJsonApi = { - getResponse: jest.fn(), - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - patch: jest.fn(), - del: jest.fn(), - } as never; - di.override(apiKubeInjectable, () => kubeJsonApi); - - deploymentApi = di.inject(deploymentApiInjectable); - }); - - describe("scale", () => { - it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => { - deploymentApi.scale({ namespace: "default", name: "deployment-1" }, 5); - - expect(kubeJsonApi.patch).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/deployments/deployment-1/scale", { - data: { - spec: { - replicas: 5, - }, - }, - }, - { - headers: { - "content-type": "application/merge-patch+json", - }, - }); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/endpoint.api.test.ts b/src/common/k8s-api/__tests__/endpoint.api.test.ts deleted file mode 100644 index 666c5df63a..0000000000 --- a/src/common/k8s-api/__tests__/endpoint.api.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { formatEndpointSubset } from "../endpoints"; - -describe("endpoint tests", () => { - describe("EndpointSubset", () => { - it("formatEndpointSubset should be addresses X ports", () => { - const formatted = formatEndpointSubset({ - addresses: [{ - ip: "1.1.1.1", - }, { - ip: "1.1.1.2", - }], - notReadyAddresses: [], - ports: [{ - port: 81, - }, { - port: 82, - }], - }); - - expect(formatted).toBe("1.1.1.1:81, 1.1.1.1:82, 1.1.1.2:81, 1.1.1.2:82"); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/helm-charts.api.test.ts b/src/common/k8s-api/__tests__/helm-charts.api.test.ts deleted file mode 100644 index e7a4cb9439..0000000000 --- a/src/common/k8s-api/__tests__/helm-charts.api.test.ts +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { anyObject } from "jest-mock-extended"; -import { HelmChart } from "../endpoints/helm-charts.api"; - -describe("HelmChart tests", () => { - describe("HelmChart.create() tests", () => { - it("should throw on non-object input", () => { - expect(() => HelmChart.create("" as never)).toThrowError('"value" must be of type object'); - expect(() => HelmChart.create(1 as never)).toThrowError('"value" must be of type object'); - expect(() => HelmChart.create(false as never)).toThrowError('"value" must be of type object'); - expect(() => HelmChart.create([] as never)).toThrowError('"value" must be of type object'); - expect(() => HelmChart.create(Symbol() as never)).toThrowError('"value" must be of type object'); - }); - - it("should throw on missing fields", () => { - expect(() => HelmChart.create({} as never)).toThrowError('"apiVersion" is required'); - expect(() => HelmChart.create({ - apiVersion: "!", - } as never)).toThrowError('"name" is required'); - expect(() => HelmChart.create({ - apiVersion: "!", - name: "!", - } as never)).toThrowError('"version" is required'); - expect(() => HelmChart.create({ - apiVersion: "!", - name: "!", - version: "!", - } as never)).toThrowError('"repo" is required'); - expect(() => HelmChart.create({ - apiVersion: "!", - name: "!", - version: "!", - repo: "!", - } as never)).toThrowError('"created" is required'); - }); - - it("should throw on fields being wrong type", () => { - expect(() => HelmChart.create({ - apiVersion: 1, - name: "!", - version: "!", - repo: "!", - created: "!", - digest: "!", - } as never)).toThrowError('"apiVersion" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: 1, - version: "!", - repo: "!", - created: "!", - digest: "!", - } as never)).toThrowError('"name" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "!", - name: "!", - version: "!", - repo: "!", - created: "!", - digest: 1, - } as never)).toThrowError('"digest" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "", - version: 1, - repo: "!", - created: "!", - digest: "!", - } as never)).toThrowError('"version" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: 1, - created: "!", - digest: "!", - } as never)).toThrowError('"repo" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - created: 1, - digest: "a", - } as never)).toThrowError('"created" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - created: "!", - digest: 1, - } as never)).toThrowError('"digest" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - kubeVersion: 1, - } as never)).toThrowError('"kubeVersion" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - description: 1, - } as never)).toThrowError('"description" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - home: 1, - } as never)).toThrowError('"home" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - engine: 1, - } as never)).toThrowError('"engine" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - icon: 1, - } as never)).toThrowError('"icon" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - appVersion: 1, - } as never)).toThrowError('"appVersion" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - tillerVersion: 1, - } as never)).toThrowError('"tillerVersion" must be a string'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - deprecated: 1, - } as never)).toThrowError('"deprecated" must be a boolean'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - keywords: 1, - } as never)).toThrowError('"keywords" must be an array'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - sources: 1, - } as never)).toThrowError('"sources" must be an array'); - expect(() => HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - maintainers: 1, - } as never)).toThrowError('"maintainers" must be an array'); - }); - - it("should filter non-string keywords", () => { - const chart = HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - keywords: [1, "a", false, {}, "b"] as never, - }); - - expect(chart?.keywords).toStrictEqual(["a", "b"]); - }); - - it("should filter non-string sources", () => { - const chart = HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - sources: [1, "a", false, {}, "b"] as never, - }); - - expect(chart?.sources).toStrictEqual(["a", "b"]); - }); - - it("should filter invalid maintainers", () => { - const chart = HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - maintainers: [{ - name: "a", - email: "b", - url: "c", - }] as never, - }); - - expect(chart?.maintainers).toStrictEqual([{ - name: "a", - email: "b", - url: "c", - }]); - }); - - it("should warn on unknown fields", () => { - const { warn } = console; - const warnFn = console.warn = jest.fn(); - - HelmChart.create({ - apiVersion: "1", - name: "1", - version: "1", - repo: "1", - digest: "1", - created: "!", - maintainers: [{ - name: "a", - email: "b", - url: "c", - }] as never, - "asdjhajksdhadjks": 1, - } as never); - - expect(warnFn).toHaveBeenCalledWith("HelmChart data has unexpected fields", { - original: anyObject(), - unknownFields: ["asdjhajksdhadjks"], - }); - console.warn = warn; - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/ingress.api.test.ts b/src/common/k8s-api/__tests__/ingress.api.test.ts deleted file mode 100644 index acb2a59a68..0000000000 --- a/src/common/k8s-api/__tests__/ingress.api.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { computeRuleDeclarations, Ingress } from "../endpoints"; - -describe("Ingress", () => { - it("given no loadbalancer ingresses in status property, loadbalancers should be an empty array", () => { - const ingress = new Ingress({ - apiVersion: "networking.k8s.io/v1", - kind: "Ingress", - metadata: { - name: "foo", - resourceVersion: "1", - uid: "bar", - namespace: "default", - selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo", - }, - status: { - loadBalancer: {}, - }, - }); - - expect(ingress.getLoadBalancers()).toEqual([]); - }); - - it("given loadbalancer ingresses in status property, loadbalancers should be flat array of ip adresses and hostnames", () => { - const ingress = new Ingress({ - apiVersion: "networking.k8s.io/v1", - kind: "Ingress", - metadata: { - name: "foo", - resourceVersion: "1", - uid: "bar", - namespace: "default", - selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo", - }, - status: { - loadBalancer: { - ingress: [{ - ip: "10.0.0.27", - }, - { - hostname: "localhost", - }], - }, - }, - }); - - expect(ingress.getLoadBalancers()).toEqual(["10.0.0.27", "localhost"]); - }); -}); - -describe("computeRuleDeclarations", () => { - it("given no tls field, should format links as http://", () => { - const ingress = new Ingress({ - apiVersion: "networking.k8s.io/v1", - kind: "Ingress", - metadata: { - name: "foo", - resourceVersion: "1", - uid: "bar", - namespace: "default", - selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo", - }, - }); - - const result = computeRuleDeclarations(ingress, { - host: "foo.bar", - http: { - paths: [{ - pathType: "Exact", - backend: { - service: { - name: "my-service", - port: { - number: 8080, - }, - }, - }, - }], - }, - }); - - expect(result[0].url).toBe("http://foo.bar/"); - }); - - it("given no tls entries, should format links as http://", () => { - const ingress = new Ingress({ - apiVersion: "networking.k8s.io/v1", - kind: "Ingress", - metadata: { - name: "foo", - resourceVersion: "1", - uid: "bar", - namespace: "default", - selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo", - }, - }); - - ingress.spec = { - tls: [], - }; - - const result = computeRuleDeclarations(ingress, { - host: "foo.bar", - http: { - paths: [{ - pathType: "Exact", - backend: { - service: { - name: "my-service", - port: { - number: 8080, - }, - }, - }, - }], - }, - }); - - expect(result[0].url).toBe("http://foo.bar/"); - }); - - it("given some tls entries, should format links as https://", () => { - const ingress = new Ingress({ - apiVersion: "networking.k8s.io/v1", - kind: "Ingress", - metadata: { - name: "foo", - resourceVersion: "1", - uid: "bar", - namespace: "default", - selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo", - }, - }); - - ingress.spec = { - tls: [{ - secretName: "my-secret", - }], - }; - - const result = computeRuleDeclarations(ingress, { - host: "foo.bar", - http: { - paths: [{ - pathType: "Exact", - backend: { - service: { - name: "my-service", - port: { - number: 8080, - }, - }, - }, - }], - }, - }); - - expect(result[0].url).toBe("https://foo.bar/"); - }); -}); diff --git a/src/common/k8s-api/__tests__/kube-api-parse.test.ts b/src/common/k8s-api/__tests__/kube-api-parse.test.ts deleted file mode 100644 index 547f78adef..0000000000 --- a/src/common/k8s-api/__tests__/kube-api-parse.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -jest.mock("../kube-object"); -jest.mock("../kube-api"); -jest.mock("../api-manager", () => ({ - apiManager() { - return { - registerStore: jest.fn(), - }; - }, -})); - -import type { IKubeApiParsed } from "../kube-api-parse"; -import { parseKubeApi } from "../kube-api-parse"; - -/** - * [, ] - */ -type KubeApiParseTestData = [string, IKubeApiParsed]; - -const tests: KubeApiParseTestData[] = [ - ["/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/prometheuses.monitoring.coreos.com", { - apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions", - apiPrefix: "/apis", - apiGroup: "apiextensions.k8s.io", - apiVersion: "v1beta1", - apiVersionWithGroup: "apiextensions.k8s.io/v1beta1", - namespace: undefined, - resource: "customresourcedefinitions", - name: "prometheuses.monitoring.coreos.com", - }], - ["/api/v1/namespaces/kube-system/pods/coredns-6955765f44-v8p27", { - apiBase: "/api/v1/pods", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "v1", - apiVersionWithGroup: "v1", - namespace: "kube-system", - resource: "pods", - name: "coredns-6955765f44-v8p27", - }], - ["/apis/stable.example.com/foo1/crontabs", { - apiBase: "/apis/stable.example.com/foo1/crontabs", - apiPrefix: "/apis", - apiGroup: "stable.example.com", - apiVersion: "foo1", - apiVersionWithGroup: "stable.example.com/foo1", - resource: "crontabs", - name: undefined, - namespace: undefined, - }], - ["/apis/cluster.k8s.io/v1alpha1/clusters", { - apiBase: "/apis/cluster.k8s.io/v1alpha1/clusters", - apiPrefix: "/apis", - apiGroup: "cluster.k8s.io", - apiVersion: "v1alpha1", - apiVersionWithGroup: "cluster.k8s.io/v1alpha1", - resource: "clusters", - name: undefined, - namespace: undefined, - }], - ["/api/v1/namespaces", { - apiBase: "/api/v1/namespaces", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "v1", - apiVersionWithGroup: "v1", - resource: "namespaces", - name: undefined, - namespace: undefined, - }], - ["/api/v1/secrets", { - apiBase: "/api/v1/secrets", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "v1", - apiVersionWithGroup: "v1", - resource: "secrets", - name: undefined, - namespace: undefined, - }], - ["/api/v1/nodes/minikube", { - apiBase: "/api/v1/nodes", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "v1", - apiVersionWithGroup: "v1", - resource: "nodes", - name: "minikube", - namespace: undefined, - }], - ["/api/foo-bar/nodes/minikube", { - apiBase: "/api/foo-bar/nodes", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "foo-bar", - apiVersionWithGroup: "foo-bar", - resource: "nodes", - name: "minikube", - namespace: undefined, - }], - ["/api/v1/namespaces/kube-public", { - apiBase: "/api/v1/namespaces", - apiPrefix: "/api", - apiGroup: "", - apiVersion: "v1", - apiVersionWithGroup: "v1", - resource: "namespaces", - name: "kube-public", - namespace: undefined, - }], -]; - -const throwtests = [ - undefined, - "", - "ajklsmh", -]; - -describe("parseApi unit tests", () => { - it.each(tests)("testing %j", (url, expected) => { - expect(parseKubeApi(url)).toStrictEqual(expected); - }); - - it.each(throwtests)("testing %j should throw", (url) => { - expect(() => parseKubeApi(url as never)).toThrowError("invalid apiPath"); - }); -}); diff --git a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts b/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts deleted file mode 100644 index cd08ef4d5c..0000000000 --- a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts +++ /dev/null @@ -1,708 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { ApiManager } from "../api-manager"; -import type { IngressApi } from "../endpoints"; -import { Ingress } from "../endpoints"; -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import type { Fetch } from "../../fetch/fetch.injectable"; -import fetchInjectable from "../../fetch/fetch.injectable"; -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { flushPromises } from "../../test-utils/flush-promises"; -import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable"; -import { createMockResponseFromString } from "../../../test-utils/mock-responses"; -import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; -import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; -import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; -import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import apiManagerInjectable from "../api-manager/manager.injectable"; -import type { DiContainer } from "@ogre-tools/injectable"; -import ingressApiInjectable from "../endpoints/ingress.api.injectable"; - -describe("KubeApi", () => { - let fetchMock: AsyncFnMock; - let apiManager: ApiManager; - let di: DiContainer; - - beforeEach(async () => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - fetchMock = asyncFn(); - di.override(fetchInjectable, () => fetchMock); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - apiManager = di.inject(apiManagerInjectable); - - const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable); - - setupAutoRegistration.run(); - }); - - describe("on first call to IngressApi.get()", () => { - let ingressApi: IngressApi; - let getCall: Promise; - - beforeEach(async () => { - ingressApi = di.inject(ingressApiInjectable); - getCall = ingressApi.get({ - name: "foo", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("requests version list from the api group from the initial apiBase", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the version list from the api group resolves", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io", JSON.stringify({ - apiVersion: "v1", - kind: "APIGroup", - name: "networking.k8s.io", - versions: [ - { - groupVersion: "networking.k8s.io/v1", - version: "v1", - }, - { - groupVersion: "networking.k8s.io/v1beta1", - version: "v1beta1", - }, - ], - preferredVersion: { - groupVersion: "networking.k8s.io/v1", - version: "v1", - }, - })), - ); - }); - - it("requests resources from the versioned api group from the initial apiBase", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when resource request fufills with a resource", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1", JSON.stringify({ - resources: [{ - name: "ingresses", - }], - })), - ); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - it("sets fields in the api instance", () => { - expect(ingressApi).toEqual(expect.objectContaining({ - apiVersionPreferred: "v1", - apiPrefix: "/apis", - apiGroup: "networking.k8s.io", - })); - }); - - it("api is retrievable with the new apiBase", () => { - expect(apiManager.getApi("/apis/networking.k8s.io/v1/ingresses")).toBeDefined(); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - - describe("when the request resolves with data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({ - apiVersion: "v1", - kind: "Ingress", - metadata: { - name: "foo", - namespace: "default", - resourceVersion: "1", - uid: "12345", - }, - })), - ); - result = await getCall; - }); - - it("results in the get call resolving to an instance", () => { - expect(result).toBeInstanceOf(Ingress); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - }); - - describe("when resource request fufills with no resource", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1", JSON.stringify({ - resources: [], - })), - ); - }); - - it("requests resources from the second versioned api group from the initial apiBase", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - - - describe("when resource request fufills with a resource", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1", JSON.stringify({ - resources: [{ - name: "ingresses", - }], - })), - ); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - it("sets fields in the api instance", () => { - expect(ingressApi).toEqual(expect.objectContaining({ - apiVersionPreferred: "v1beta1", - apiPrefix: "/apis", - apiGroup: "networking.k8s.io", - })); - }); - - it("api is retrievable with the new apiBase", () => { - expect(apiManager.getApi("/apis/networking.k8s.io/v1beta1/ingresses")).toBeDefined(); - }); - - it("api is retrievable with the old apiBase", () => { - expect(apiManager.getApi("/apis/networking.k8s.io/v1/ingresses")).toBeDefined(); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - - describe("when the request resolves with data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({ - apiVersion: "v1", - kind: "Ingress", - metadata: { - name: "foo", - namespace: "default", - resourceVersion: "1", - uid: "12345", - }, - })), - ); - result = await getCall; - }); - - it("results in the get call resolving to an instance", () => { - expect(result).toBeInstanceOf(Ingress); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - }); - }); - }); - - describe("when the version list from the api group resolves with no versions", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io", JSON.stringify({ - "metadata": {}, - "status": "Failure", - "message": "the server could not find the requested resource", - "reason": "NotFound", - "details": { - "causes": [ - { - "reason": "UnexpectedServerResponse", - "message": "404 page not found", - }, - ], - }, - "code": 404, - }), 404), - ); - }); - - it("requests the resources from the base api url from the fallback api", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/extensions", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when resource request fufills with a resource", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions", JSON.stringify({ - apiVersion: "v1", - kind: "APIGroup", - name: "extensions", - versions: [ - { - groupVersion: "extensions/v1beta1", - version: "v1beta1", - }, - ], - preferredVersion: { - groupVersion: "extensions/v1beta1", - version: "v1beta1", - }, - })), - ); - }); - - it("requests resource versions from the versioned api group from the fallback apiBase", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the preferred version request resolves to v1beta1", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions", JSON.stringify({ - resources: [{ - name: "ingresses", - }], - })), - ); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - it("sets fields in the api instance", () => { - expect(ingressApi).toEqual(expect.objectContaining({ - apiVersionPreferred: "v1beta1", - apiPrefix: "/apis", - apiGroup: "extensions", - })); - }); - - it("api is retrievable with the new apiBase", () => { - expect(apiManager.getApi("/apis/extensions/v1beta1/ingresses")).toBeDefined(); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - - describe("when the request resolves with data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({ - apiVersion: "v1beta1", - kind: "Ingress", - metadata: { - name: "foo", - namespace: "default", - resourceVersion: "1", - uid: "12345", - }, - })), - ); - result = await getCall; - }); - - it("results in the get call resolving to an instance", () => { - expect(result).toBeInstanceOf(Ingress); - }); - - describe("on the second call to IngressApi.get()", () => { - let getCall: Promise; - - beforeEach(async () => { - getCall = ingressApi.get({ - name: "foo1", - namespace: "default", - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("makes the request to get the resource", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with no data", () => { - let result: Ingress | null; - - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"], - createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})), - ); - result = await getCall; - }); - - it("results in the get call resolving to null", () => { - expect(result).toBeNull(); - }); - }); - }); - }); - }); - }); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts deleted file mode 100644 index 7bce8a3b7c..0000000000 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ /dev/null @@ -1,1227 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { KubeApiWatchCallback } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import { PassThrough } from "stream"; -import type { DeploymentApi, NamespaceApi } from "../endpoints"; -import { Deployment, Pod, PodApi } from "../endpoints"; -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import type { Fetch } from "../../fetch/fetch.injectable"; -import fetchInjectable from "../../fetch/fetch.injectable"; -import type { CreateKubeApiForRemoteCluster } from "../create-kube-api-for-remote-cluster.injectable"; -import createKubeApiForRemoteClusterInjectable from "../create-kube-api-for-remote-cluster.injectable"; -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { flushPromises } from "../../test-utils/flush-promises"; -import createKubeJsonApiInjectable from "../create-kube-json-api.injectable"; -import type { IKubeWatchEvent } from "../kube-watch-event"; -import type { KubeJsonApiDataFor } from "../kube-object"; -import AbortController from "abort-controller"; -import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable"; -import { createMockResponseFromStream, createMockResponseFromString } from "../../../test-utils/mock-responses"; -import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; -import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable"; -import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable"; -import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; -import type { DiContainer } from "@ogre-tools/injectable"; -import deploymentApiInjectable from "../endpoints/deployment.api.injectable"; -import podApiInjectable from "../endpoints/pod.api.injectable"; -import namespaceApiInjectable from "../endpoints/namespace.api.injectable"; - -// NOTE: this is fine because we are testing something that only exported -// eslint-disable-next-line no-restricted-imports -import { PodsApi } from "../../../extensions/common-api/k8s-api"; - -describe("createKubeApiForRemoteCluster", () => { - let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster; - let fetchMock: AsyncFnMock; - - beforeEach(async () => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - fetchMock = asyncFn(); - di.override(fetchInjectable, () => fetchMock); - - createKubeApiForRemoteCluster = di.inject(createKubeApiForRemoteClusterInjectable); - }); - - it("builds api client for KubeObject", async () => { - const api = createKubeApiForRemoteCluster({ - cluster: { - server: "https://127.0.0.1:6443", - }, - user: { - token: "daa", - }, - }, Pod); - - expect(api).toBeInstanceOf(KubeApi); - }); - - describe("when building for remote cluster with specific constructor", () => { - let api: PodApi; - - beforeEach(() => { - api = createKubeApiForRemoteCluster({ - cluster: { - server: "https://127.0.0.1:6443", - }, - user: { - token: "daa", - }, - }, Pod, PodsApi); - }); - - it("uses the constructor", () => { - expect(api).toBeInstanceOf(PodApi); - }); - - describe("when calling list without namespace", () => { - let listRequest: Promise; - - beforeEach(async () => { - listRequest = api.list(); - - // This is required because of how JS promises work - await flushPromises(); - }); - - it("should request pods from default namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "https://127.0.0.1:6443/api/v1/pods", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["https://127.0.0.1:6443/api/v1/pods"], - createMockResponseFromString("https://127.0.0.1:6443/api/v1/pods", JSON.stringify({ - kind: "PodList", - apiVersion: "v1", - metadata:{ - resourceVersion: "452899", - }, - items: [], - })), - ); - }); - - it("resolves the list call", async () => { - expect(await listRequest).toEqual([]); - }); - }); - }); - }); -}); - -describe("KubeApi", () => { - let fetchMock: AsyncFnMock; - let di: DiContainer; - - beforeEach(async () => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - fetchMock = asyncFn(); - di.override(fetchInjectable, () => fetchMock); - - const createCluster = di.inject(createClusterInjectable); - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - di.override(apiKubeInjectable, () => createKubeJsonApi({ - serverAddress: `http://127.0.0.1:9999`, - apiBase: "/api-kube", - })); - - const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable); - - setupAutoRegistration.run(); - }); - - describe("patching deployments", () => { - let api: DeploymentApi; - - beforeEach(() => { - api = di.inject(deploymentApiInjectable); - }); - - describe("when patching a resource without providing a strategy", () => { - let patchRequest: Promise; - - beforeEach(async () => { - patchRequest = api.patch({ name: "test", namespace: "default" }, { - spec: { replicas: 2 }, - }); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("requests a patch using strategic merge", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - method: "patch", - body: JSON.stringify({ spec: { replicas: 2 }}), - }, - ]); - }); - - describe("when the patch request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({ - apiVersion: "v1", - kind: "Deployment", - metadata: { - name: "test", - namespace: "default", - resourceVersion: "1", - uid: "12345", - }, - spec: { - replicas: 2, - }, - })), - ); - }); - - it("resolves the patch call", async () => { - expect(await patchRequest).toBeInstanceOf(Deployment); - }); - }); - }); - - describe("when patching a resource using json patch", () => { - let patchRequest: Promise; - - beforeEach(async () => { - patchRequest = api.patch({ name: "test", namespace: "default" }, [ - { op: "replace", path: "/spec/replicas", value: 2 }, - ], "json"); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("requests a patch using json merge", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", - { - headers: { - "content-type": "application/json-patch+json", - }, - method: "patch", - body: JSON.stringify([ - { op: "replace", path: "/spec/replicas", value: 2 }, - ]), - }, - ]); - }); - - describe("when the patch request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({ - apiVersion: "v1", - kind: "Deployment", - metadata: { - name: "test", - namespace: "default", - resourceVersion: "1", - uid: "12345", - }, - spec: { - replicas: 2, - }, - })), - ); - }); - - it("resolves the patch call", async () => { - expect(await patchRequest).toBeInstanceOf(Deployment); - }); - }); - }); - - describe("when patching a resource using merge patch", () => { - let patchRequest: Promise; - - beforeEach(async () => { - patchRequest = api.patch( - { name: "test", namespace: "default" }, - { metadata: { annotations: { provisioned: "True" }}}, - "merge", - ); - - // This is needed because of how JS promises work - await flushPromises(); - }); - - it("requests a patch using json merge", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", - { - headers: { - "content-type": "application/merge-patch+json", - }, - method: "patch", - body: JSON.stringify({ metadata: { annotations: { provisioned: "True" }}}), - }, - ]); - }); - - describe("when the patch request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({ - apiVersion: "v1", - kind: "Deployment", - metadata: { - name: "test", - namespace: "default", - resourceVersion: "1", - uid: "12345", - annotations: { - provisioned: "True", - }, - }, - })), - ); - }); - - it("resolves the patch call", async () => { - expect(await patchRequest).toBeInstanceOf(Deployment); - }); - }); - }); - }); - - describe("deleting pods (namespace scoped resource)", () => { - let api: PodApi; - - beforeEach(() => { - api = di.inject(podApiInjectable); - }); - - describe("when deleting by just name", () => { - let deleteRequest: Promise; - - beforeEach(async () => { - deleteRequest = api.delete({ name: "foo" }); - - // This is required for how JS promises work - await flushPromises(); - }); - - it("requests deleting pod in default namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", - { - headers: { - "content-type": "application/json", - }, - method: "delete", - }, - ]); - }); - - describe("when request resolves", () => { - beforeEach(async () => { - fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", "{}"), - ); - }); - - it("resolves the call", async () => { - expect(await deleteRequest).toBeDefined(); - }); - }); - }); - - describe("when deleting by name and empty namespace", () => { - let deleteRequest: Promise; - - beforeEach(async () => { - deleteRequest = api.delete({ name: "foo", namespace: "" }); - - // This is required for how JS promises work - await flushPromises(); - }); - - it("requests deleting pod in default namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", - { - headers: { - "content-type": "application/json", - }, - method: "delete", - }, - ]); - }); - - describe("when request resolves", () => { - beforeEach(async () => { - fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", "{}"), - ); - }); - - it("resolves the call", async () => { - expect(await deleteRequest).toBeDefined(); - }); - }); - }); - - describe("when deleting by name and namespace", () => { - let deleteRequest: Promise; - - beforeEach(async () => { - deleteRequest = api.delete({ name: "foo", namespace: "test" }); - - // This is required for how JS promises work - await flushPromises(); - }); - - it("requests deleting pod in given namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/test/pods/foo?propagationPolicy=Background", - { - headers: { - "content-type": "application/json", - }, - method: "delete", - }, - ]); - }); - - describe("when request resolves", () => { - beforeEach(async () => { - fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/test/pods/foo?propagationPolicy=Background"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/test/pods/foo?propagationPolicy=Background", "{}"), - ); - }); - - it("resolves the call", async () => { - expect(await deleteRequest).toBeDefined(); - }); - }); - }); - }); - - describe("deleting namespaces (cluser scoped resource)", () => { - let api: NamespaceApi; - - beforeEach(() => { - api = di.inject(namespaceApiInjectable); - }); - - describe("when deleting by just name", () => { - let deleteRequest: Promise; - - beforeEach(async () => { - deleteRequest = api.delete({ name: "foo" }); - - // This is required for how JS promises work - await flushPromises(); - }); - - it("requests deleting Namespace without namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", - { - headers: { - "content-type": "application/json", - }, - method: "delete", - }, - ]); - }); - - describe("when request resolves", () => { - beforeEach(async () => { - fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", "{}"), - ); - }); - - it("resolves the call", async () => { - expect(await deleteRequest).toBeDefined(); - }); - }); - }); - - describe("when deleting by name and empty namespace", () => { - let deleteRequest: Promise; - - beforeEach(async () => { - deleteRequest = api.delete({ name: "foo", namespace: "" }); - - // This is required for how JS promises work - await flushPromises(); - }); - - it("requests deleting Namespace without namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", - { - headers: { - "content-type": "application/json", - }, - method: "delete", - }, - ]); - }); - - describe("when request resolves", () => { - beforeEach(async () => { - fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", "{}"), - ); - }); - - it("resolves the call", async () => { - expect(await deleteRequest).toBeDefined(); - }); - }); - }); - - describe("when deleting by name and namespace", () => { - it("rejects request", () => { - expect(api.delete({ name: "foo", namespace: "test" })).rejects.toBeDefined(); - }); - }); - }); - - describe("watching pods", () => { - let api: PodApi; - let stream: PassThrough; - - beforeEach(() => { - api = di.inject(podApiInjectable); - stream = new PassThrough(); - }); - - afterEach(() => { - stream.end(); - stream.destroy(); - }); - - describe("when watching in a namespace", () => { - let stopWatch: () => void; - let callback: jest.MockedFunction; - - beforeEach(async () => { - callback = jest.fn(); - stopWatch = api.watch({ - namespace: "kube-system", - callback, - }); - - await flushPromises(); - }); - - it("requests the watch", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with a stream", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ([url, init]) => { - const isMatch = url === "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600"; - - if (isMatch) { - init?.signal?.addEventListener("abort", () => { - stream.destroy(); - }); - } - - return isMatch; - }, - createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600", stream), - ); - }); - - describe("when some data comes back on the stream", () => { - beforeEach(() => { - stream.emit("data", `${JSON.stringify({ - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - uid: "123456", - }, - }, - } as IKubeWatchEvent>)}\n`); - }); - - it("calls the callback with the data", () => { - expect(callback).toBeCalledWith( - { - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - selfLink: "/api/v1/namespaces/kube-system/pods/foobar", - uid: "123456", - }, - }, - }, - null, - ); - }); - - describe("when stopping the watch", () => { - beforeEach(() => { - stopWatch(); - }); - - it("closes the stream", () => { - expect(stream.destroyed).toBe(true); - }); - }); - }); - }); - }); - - describe("when watching in a namespace with an abort controller provided", () => { - let callback: jest.MockedFunction; - let abortController: AbortController; - - beforeEach(async () => { - callback = jest.fn(); - abortController = new AbortController(); - api.watch({ - namespace: "kube-system", - callback, - abortController, - }); - - await flushPromises(); - }); - - it("requests the watch", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with a stream", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ([url, init]) => { - const isMatch = url === "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600"; - - if (isMatch) { - init?.signal?.addEventListener("abort", () => { - stream.destroy(); - }); - } - - return isMatch; - }, - createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=600", stream), - ); - }); - - describe("when some data comes back on the stream", () => { - beforeEach(() => { - stream.emit("data", `${JSON.stringify({ - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - uid: "123456", - }, - }, - } as IKubeWatchEvent>)}\n`); - }); - - it("calls the callback with the data", () => { - expect(callback).toBeCalledWith( - { - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - selfLink: "/api/v1/namespaces/kube-system/pods/foobar", - uid: "123456", - }, - }, - }, - null, - ); - }); - - describe("when stopping the watch via the controller", () => { - beforeEach(() => { - abortController.abort(); - }); - - it("closes the stream", () => { - expect(stream.destroyed).toBe(true); - }); - }); - }); - }); - }); - - describe("when watching in a namespace with a timeout", () => { - let stopWatch: () => void; - let callback: jest.MockedFunction; - - beforeEach(async () => { - callback = jest.fn(); - stopWatch = api.watch({ - namespace: "kube-system", - callback, - timeout: 60, - }); - - await flushPromises(); - }); - - it("requests the watch", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=60", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with a stream", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ([url, init]) => { - const isMatch = url === "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=60"; - - if (isMatch) { - init?.signal?.addEventListener("abort", () => { - stream.destroy(); - }); - } - - return isMatch; - }, - createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=60", stream), - ); - }); - - describe("when some data comes back on the stream", () => { - beforeEach(() => { - stream.emit("data", `${JSON.stringify({ - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - uid: "123456", - }, - }, - } as IKubeWatchEvent>)}\n`); - }); - - it("calls the callback with the data", () => { - expect(callback).toBeCalledWith( - { - type: "ADDED", - object: { - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "foobar", - namespace: "kube-system", - resourceVersion: "1", - selfLink: "/api/v1/namespaces/kube-system/pods/foobar", - uid: "123456", - }, - }, - }, - null, - ); - }); - - describe("when stopping the watch", () => { - beforeEach(() => { - stopWatch(); - }); - - it("closes the stream", () => { - expect(stream.destroyed).toBe(true); - }); - }); - - describe("when the watch ends", () => { - beforeEach(() => { - stream.end(); - }); - - it("requests a new watch", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=60", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when stopping the watch", () => { - beforeEach(() => { - stopWatch(); - }); - - it("closes the stream", () => { - expect(stream.destroyed).toBe(true); - }); - }); - }); - }); - }); - }); - }); - - describe("creating pods", () => { - let api: PodApi; - - beforeEach(() => { - api = di.inject(podApiInjectable); - }); - - describe("when creating a pod", () => { - let createRequest: Promise; - - beforeEach(async () => { - createRequest = api.create({ - name: "foobar", - namespace: "default", - }, { - metadata: { - labels: { - foo: "bar", - }, - }, - spec: { - containers: [ - { - name: "web", - image: "nginx", - ports: [ - { - name: "web", - containerPort: 80, - protocol: "TCP", - }, - ], - }, - ], - }, - }); - - // This is required because of how JS promises work - await flushPromises(); - }); - - it("should request to create a pod with full descriptor", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", - { - headers: { - "content-type": "application/json", - }, - method: "post", - body: JSON.stringify({ - metadata: { - labels: { - foo: "bar", - }, - name: "foobar", - namespace: "default", - }, - spec: { - containers: [{ - name: "web", - image: "nginx", - ports: [{ - name: "web", - containerPort: 80, - protocol: "TCP", - }], - }], - }, - kind: "Pod", - apiVersion: "v1", - }), - }, - ]); - }); - - describe("when request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", JSON.stringify({ - kind: "Pod", - apiVersion: "v1", - metadata: { - name: "foobar", - namespace: "default", - labels: { - foo: "bar", - }, - resourceVersion: "1", - uid: "123456798", - }, - spec: { - containers: [{ - name: "web", - image: "nginx", - ports: [{ - name: "web", - containerPort: 80, - protocol: "TCP", - }], - }], - }, - })), - ); - }); - - it("call should resolve in a Pod instance", async () => { - expect(await createRequest).toBeInstanceOf(Pod); - }); - }); - }); - }); - - describe("updating pods", () => { - let api: PodApi; - - beforeEach(() => { - api = di.inject(podApiInjectable); - }); - - describe("when updating a pod", () => { - let updateRequest: Promise; - - beforeEach(async () => { - updateRequest = api.update({ - name: "foobar", - namespace: "default", - }, { - kind: "Pod", - apiVersion: "v1", - metadata: { - labels: { - foo: "bar", - }, - }, - spec: { - containers: [{ - name: "web", - image: "nginx", - ports: [{ - name: "web", - containerPort: 80, - protocol: "TCP", - }], - }], - }, - }); - - await flushPromises(); - }); - - it("should request that the pod is updated", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foobar", - { - headers: { - "content-type": "application/json", - }, - method: "put", - body: JSON.stringify({ - kind: "Pod", - apiVersion: "v1", - metadata: { - labels: { - foo: "bar", - }, - name: "foobar", - namespace: "default", - }, - spec: { - containers: [{ - name: "web", - image: "nginx", - ports: [{ - name: "web", - containerPort: 80, - protocol: "TCP", - }], - }], - }, - }), - }, - ]); - }); - - describe("when the request resolves with data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foobar"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foobar", JSON.stringify({ - kind: "Pod", - apiVersion: "v1", - metadata: { - name: "foobar", - namespace: "default", - labels: { - foo: "bar", - }, - resourceVersion: "1", - uid: "123456798", - }, - spec: { - containers: [{ - name: "web", - image: "nginx", - ports: [{ - name: "web", - containerPort: 80, - protocol: "TCP", - }], - }], - }, - })), - ); - }); - - it("the call should resolve to a Pod", async () => { - expect(await updateRequest).toBeInstanceOf(Pod); - }); - }); - }); - }); - - describe("listing pods", () => { - let api: PodApi; - - beforeEach(() => { - api = di.inject(podApiInjectable); - }); - - describe("when listing pods with no descriptor", () => { - let listRequest: Promise; - - beforeEach(async () => { - listRequest = api.list(); - - await flushPromises(); - }); - - it("should request that the pods from all namespaces", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/pods", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with empty data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/pods"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/pods", JSON.stringify({ - kind: "PodList", - apiVersion: "v1", - metadata: {}, - items: [], - })), - ); - }); - - it("the call should resolve to an empty list", async () => { - expect(await listRequest).toEqual([]); - }); - }); - }); - - describe("when listing pods with descriptor with namespace=''", () => { - let listRequest: Promise; - - beforeEach(async () => { - listRequest = api.list({ - namespace: "", - }); - - await flushPromises(); - }); - - it("should request that the pods from all namespaces", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/pods", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with empty data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/pods"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/pods", JSON.stringify({ - kind: "PodList", - apiVersion: "v1", - metadata: {}, - items: [], - })), - ); - }); - - it("the call should resolve to an empty list", async () => { - expect(await listRequest).toEqual([]); - }); - }); - }); - - describe("when listing pods with descriptor with namespace='default'", () => { - let listRequest: Promise; - - beforeEach(async () => { - listRequest = api.list({ - namespace: "default", - }); - - await flushPromises(); - }); - - it("should request that the pods from just the default namespace", () => { - expect(fetchMock.mock.lastCall).toMatchObject([ - "http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", - { - headers: { - "content-type": "application/json", - }, - method: "get", - }, - ]); - }); - - describe("when the request resolves with empty data", () => { - beforeEach(async () => { - await fetchMock.resolveSpecific( - ["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods"], - createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", JSON.stringify({ - kind: "PodList", - apiVersion: "v1", - metadata: {}, - items: [], - })), - ); - }); - - it("the call should resolve to an empty list", async () => { - expect(await listRequest).toEqual([]); - }); - }); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/kube-object.store.test.ts b/src/common/k8s-api/__tests__/kube-object.store.test.ts deleted file mode 100644 index 424cffba23..0000000000 --- a/src/common/k8s-api/__tests__/kube-object.store.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { noop } from "../../utils"; -import type { KubeApi } from "../kube-api"; -import { KubeObject } from "../kube-object"; -import type { KubeObjectStoreLoadingParams } from "../kube-object.store"; -import { KubeObjectStore } from "../kube-object.store"; - -class FakeKubeObjectStore extends KubeObjectStore { - constructor(private readonly _loadItems: (params: KubeObjectStoreLoadingParams) => KubeObject[], api: Partial>) { - super({ - context: { - allNamespaces: [], - contextNamespaces: [], - hasSelectedAll: false, - isGlobalWatchEnabled: () => true, - isLoadingAll: () => true, - }, - logger: { - debug: noop, - error: noop, - info: noop, - silly: noop, - warn: noop, - }, - }, api as KubeApi); - } - - async loadItems(params: KubeObjectStoreLoadingParams) { - return this._loadItems(params); - } -} - -describe("KubeObjectStore", () => { - it("should remove an object from the list of items after it is not returned from listing the same namespace again", async () => { - const loadItems = jest.fn(); - const obj = new KubeObject({ - apiVersion: "v1", - kind: "Foo", - metadata: { - name: "some-obj-name", - resourceVersion: "1", - uid: "some-uid", - namespace: "default", - selfLink: "/some/self/link", - }, - }); - const store = new FakeKubeObjectStore(loadItems, { - isNamespaced: true, - }); - - loadItems.mockImplementationOnce(() => [obj]); - - await store.loadAll({ - namespaces: ["default"], - }); - - expect(store.items).toContain(obj); - - loadItems.mockImplementationOnce(() => []); - - await store.loadAll({ - namespaces: ["default"], - }); - - expect(store.items).not.toContain(obj); - }); - - it("should not remove an object that is not returned, if it is in a different namespace", async () => { - const loadItems = jest.fn(); - const objInDefaultNamespace = new KubeObject({ - apiVersion: "v1", - kind: "Foo", - metadata: { - name: "some-obj-name", - resourceVersion: "1", - uid: "some-uid", - namespace: "default", - selfLink: "/some/self/link", - }, - }); - const objNotInDefaultNamespace = new KubeObject({ - apiVersion: "v1", - kind: "Foo", - metadata: { - name: "some-obj-name", - resourceVersion: "1", - uid: "some-uid", - namespace: "not-default", - selfLink: "/some/self/link", - }, - }); - const store = new FakeKubeObjectStore(loadItems, { - isNamespaced: true, - }); - - loadItems.mockImplementationOnce(() => [objInDefaultNamespace]); - - await store.loadAll({ - namespaces: ["default"], - }); - - expect(store.items).toContain(objInDefaultNamespace); - - loadItems.mockImplementationOnce(() => [objNotInDefaultNamespace]); - - await store.loadAll({ - namespaces: ["not-default"], - }); - - expect(store.items).toContain(objInDefaultNamespace); - }); - - it("should remove all objects not returned if the api is cluster-scoped", async () => { - const loadItems = jest.fn(); - const clusterScopedObject1 = new KubeObject({ - apiVersion: "v1", - kind: "Foo", - metadata: { - name: "some-obj-name", - resourceVersion: "1", - uid: "some-uid", - selfLink: "/some/self/link", - }, - }); - const clusterScopedObject2 = new KubeObject({ - apiVersion: "v1", - kind: "Foo", - metadata: { - name: "some-obj-name", - resourceVersion: "1", - uid: "some-uid", - namespace: "not-default", - selfLink: "/some/self/link", - }, - }); - const store = new FakeKubeObjectStore(loadItems, { - isNamespaced: false, - }); - - loadItems.mockImplementationOnce(() => [clusterScopedObject1]); - - await store.loadAll({}); - - expect(store.items).toContain(clusterScopedObject1); - - loadItems.mockImplementationOnce(() => [clusterScopedObject2]); - - await store.loadAll({}); - - expect(store.items).not.toContain(clusterScopedObject1); - }); -}); diff --git a/src/common/k8s-api/__tests__/kube-object.test.ts b/src/common/k8s-api/__tests__/kube-object.test.ts deleted file mode 100644 index 4d5865c5db..0000000000 --- a/src/common/k8s-api/__tests__/kube-object.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObject } from "../kube-object"; - -describe("KubeObject", () => { - describe("isJsonApiData", () => { - { - type TestCase = [any]; - const tests: TestCase[] = [ - [false], - [true], - [null], - [undefined], - [""], - [1], - [(): unknown => void 0], - [Symbol("hello")], - [{}], - ]; - - it.each(tests)("should reject invalid value: %p", (input) => { - expect(KubeObject.isJsonApiData(input)).toBe(false); - }); - } - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["apiVersion", { kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["metadata", { kind: "", apiVersion: "" }], - ["metadata.uid", { kind: "", apiVersion: "", metadata: { name: "", resourceVersion: "", selfLink: "" }}], - ["metadata.name", { kind: "", apiVersion: "", metadata: { uid: "", resourceVersion: "", selfLink: "" }}], - ["metadata.resourceVersion", { kind: "", apiVersion: "", metadata: { uid: "", name: "", selfLink: "" }}], - ]; - - it.each(tests)("should reject with missing: %s", (missingField, input) => { - expect(KubeObject.isJsonApiData(input)).toBe(false); - }); - } - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { kind: 1, apiVersion: "", metadata: {}}], - ["apiVersion", { apiVersion: 1, kind: "", metadata: {}}], - ["metadata", { kind: "", apiVersion: "", metadata: "" }], - ["metadata.uid", { kind: "", apiVersion: "", metadata: { uid: 1 }}], - ["metadata.name", { kind: "", apiVersion: "", metadata: { uid: "", name: 1 }}], - ["metadata.resourceVersion", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: 1 }}], - ["metadata.selfLink", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: 1 }}], - ["metadata.namespace", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", namespace: 1 }}], - ["metadata.creationTimestamp", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", creationTimestamp: 1 }}], - ["metadata.continue", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", continue: 1 }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: 1 }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: [1] }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: {}}}], - ["metadata.labels", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", labels: 1 }}], - ["metadata.labels", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", labels: { food: 1 }}}], - ["metadata.annotations", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: 1 }}], - ["metadata.annotations", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: 1 }}}], - ]; - - it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => { - expect(KubeObject.isJsonApiData(input)).toBe(false); - }); - } - - it("should accept valid KubeJsonApiData (ignoring other fields)", () => { - const valid = { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: "" }}}; - - expect(KubeObject.isJsonApiData(valid)).toBe(true); - }); - }); - - describe("isPartialJsonApiData", () => { - { - type TestCase = [any]; - const tests: TestCase[] = [ - [false], - [true], - [null], - [undefined], - [""], - [1], - [(): unknown => void 0], - [Symbol("hello")], - ]; - - it.each(tests)("should reject invalid value: %p", (input) => { - expect(KubeObject.isPartialJsonApiData(input)).toBe(false); - }); - } - - it("should accept {}", () => { - expect(KubeObject.isPartialJsonApiData({})).toBe(true); - }); - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["apiVersion", { kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["metadata", { kind: "", apiVersion: "" }], - ]; - - it.each(tests)("should not reject with missing top level field: %s", (missingField, input) => { - expect(KubeObject.isPartialJsonApiData(input)).toBe(true); - }); - } - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { kind: 1, apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["apiVersion", { apiVersion: 1, kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}], - ["metadata", { kind: "", apiVersion: "", metadata: "" }], - ["metadata.uid", { kind: "", apiVersion: "", metadata: { uid: 1, name: "", resourceVersion: "", selfLink: "" }}], - ["metadata.name", { kind: "", apiVersion: "", metadata: { uid: "", name: 1, resourceVersion: "", selfLink: "" }}], - ["metadata.resourceVersion", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: 1, selfLink: "" }}], - ["metadata.selfLink", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: 1 }}], - ["metadata.namespace", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", namespace: 1 }}], - ["metadata.creationTimestamp", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", creationTimestamp: 1 }}], - ["metadata.continue", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", continue: 1 }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: 1 }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: [1] }}], - ["metadata.finalizers", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", finalizers: {}}}], - ["metadata.labels", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", labels: 1 }}], - ["metadata.labels", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", labels: { food: 1 }}}], - ["metadata.annotations", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: 1 }}], - ["metadata.annotations", { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: 1 }}}], - ]; - - it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => { - expect(KubeObject.isPartialJsonApiData(input)).toBe(false); - }); - } - - it("should accept valid Partial (ignoring other fields)", () => { - const valid = { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: "" }}}; - - expect(KubeObject.isPartialJsonApiData(valid)).toBe(true); - }); - }); - - describe("isJsonApiDataList", () => { - function isAny(val: unknown): val is any { - return true; - } - - function isNotAny(val: unknown): val is any { - return false; - } - - function isBoolean(val: unknown): val is boolean { - return typeof val === "boolean"; - } - - { - type TestCase = [any]; - const tests: TestCase[] = [ - [false], - [true], - [null], - [undefined], - [""], - [1], - [(): unknown => void 0], - [Symbol("hello")], - [{}], - ]; - - it.each(tests)("should reject invalid value: %p", (input) => { - expect(KubeObject.isJsonApiDataList(input, isAny)).toBe(false); - }); - } - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { apiVersion: "", items: [], metadata: { resourceVersion: "", selfLink: "" }}], - ["apiVersion", { kind: "", items: [], metadata: { resourceVersion: "", selfLink: "" }}], - ["metadata", { kind: "", items: [], apiVersion: "" }], - ]; - - it.each(tests)("should reject with missing: %s", (missingField, input) => { - expect(KubeObject.isJsonApiDataList(input, isAny)).toBe(false); - }); - } - - { - type TestCase = [string, any]; - const tests: TestCase[] = [ - ["kind", { kind: 1, items: [], apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}], - ["apiVersion", { kind: "", items: [], apiVersion: 1, metadata: { resourceVersion: "", selfLink: "" }}], - ["metadata", { kind: "", items: [], apiVersion: "", metadata: 1 }], - ["metadata.resourceVersion", { kind: "", items: [], apiVersion: "", metadata: { resourceVersion: 1, selfLink: "" }}], - ["metadata.selfLink", { kind: "", items: [], apiVersion: "", metadata: { resourceVersion: "", selfLink: 1 }}], - ["items", { kind: "", items: 1, apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}], - ["items", { kind: "", items: "", apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}], - ["items", { kind: "", items: {}, apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}], - ["items[0]", { kind: "", items: [""], apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}], - ]; - - it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => { - expect(KubeObject.isJsonApiDataList(input, isNotAny)).toBe(false); - }); - } - - it("should accept valid KubeJsonApiDataList (ignoring other fields)", () => { - const valid = { kind: "", items: [false], apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}; - - expect(KubeObject.isJsonApiDataList(valid, isBoolean)).toBe(true); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/node.test.ts b/src/common/k8s-api/__tests__/node.test.ts deleted file mode 100644 index 53ffc59d79..0000000000 --- a/src/common/k8s-api/__tests__/node.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { Node } from "../endpoints"; - -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -describe("Node tests", () => { - describe("isMasterNode()", () => { - it("given a master node labelled before kubernetes 1.20, should return true", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "node-role.kubernetes.io/master": "NoSchedule", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.isMasterNode()).toBe(true); - }); - - it("given a master node labelled after kubernetes 1.20, should return true", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "node-role.kubernetes.io/control-plane": "NoSchedule", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.isMasterNode()).toBe(true); - }); - - it("given a non master node, should return false", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: {}, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.isMasterNode()).toBe(false); - }); - }); - - describe("getRoleLabels()", () => { - it("should return empty string if labels is not present", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe(""); - }); - - it("should return empty string if labels is empty object", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: {}, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe(""); - }); - - it("should return rest of keys with substring node-role.kubernetes.io/", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "node-role.kubernetes.io/foobar": "bat", - "hellonode-role.kubernetes.io/foobar1": "bat", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe("foobar, foobar1"); - }); - - it("should return rest of keys with substring node-role.kubernetes.io/ after last /", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "node-role.kubernetes.io/foobar": "bat", - "hellonode-role.kubernetes.io//////foobar1": "bat", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe("foobar, foobar1"); - }); - - it("should return value of label kubernetes.io/role if present", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "kubernetes.io/role": "master", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe("master"); - }); - - it("should return value of label node.kubernetes.io/role if present", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "node.kubernetes.io/role": "master", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe("master"); - }); - - it("all sources should be joined together", () => { - const node = new Node({ - apiVersion: "foo", - kind: "Node", - metadata: { - name: "bar", - resourceVersion: "1", - uid: "bat", - labels: { - "aksjhdkjahsdnode-role.kubernetes.io/foobar": "bat", - "kubernetes.io/role": "master", - "node.kubernetes.io/role": "master-v2-max", - }, - selfLink: "/api/v1/nodes/bar", - }, - }); - - expect(node.getRoleLabels()).toBe("foobar, master, master-v2-max"); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/pods.api.test.ts b/src/common/k8s-api/__tests__/pods.api.test.ts deleted file mode 100644 index 9b01dff73b..0000000000 --- a/src/common/k8s-api/__tests__/pods.api.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { Pod } from "../endpoints"; - -describe("Pod tests", () => { - it("getAllContainers() should never throw", () => { - const pod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar", - }, - }); - - expect(pod.getAllContainers()).toStrictEqual([]); - }); -}); diff --git a/src/common/k8s-api/__tests__/pods.test.ts b/src/common/k8s-api/__tests__/pods.test.ts deleted file mode 100644 index 3ed979face..0000000000 --- a/src/common/k8s-api/__tests__/pods.test.ts +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import assert from "assert"; -import type { Container, PodContainerStatus } from "../endpoints"; -import { Pod } from "../endpoints"; - -interface GetDummyPodOptions { - running?: number; - dead?: number; - initRunning?: number; - initDead?: number; -} - -function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod { - const { - running = 0, - dead = 0, - initDead = 0, - initRunning = 0, - } = rawOpts; - - const containers: Container[] = []; - const initContainers: Container[] = []; - const containerStatuses: PodContainerStatus[] = []; - const initContainerStatuses: PodContainerStatus[] = []; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - uid: "1", - name: "test", - resourceVersion: "v1", - namespace: "default", - selfLink: "/api/v1/pods/default/test", - }, - spec: { - containers, - initContainers, - serviceAccount: "dummy", - serviceAccountName: "dummy", - }, - status: { - phase: "Running", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses, - initContainerStatuses, - }, - }); - - for (let i = 0; i < running; i += 1) { - const name = `container_running_${i}`; - - containers.push({ - image: "dummy", - imagePullPolicy: "Always", - name, - }); - containerStatuses.push({ - image: "dummy", - imageID: "dummy", - name, - ready: true, - restartCount: i, - state: { - running: { - startedAt: "before", - }, - }, - }); - } - - for (let i = 0; i < dead; i += 1) { - const name = `container_dead_${i}`; - - containers.push({ - image: "dummy", - imagePullPolicy: "Always", - name, - }); - containerStatuses.push({ - image: "dummy", - imageID: "dummy", - name, - ready: false, - restartCount: i, - state: { - terminated: { - startedAt: "before", - exitCode: i+1, - finishedAt: "later", - reason: `reason_${i}`, - }, - }, - }); - } - - for (let i = 0; i < initRunning; i += 1) { - const name = `container_init-running_${i}`; - - initContainers.push({ - image: "dummy", - imagePullPolicy: "Always", - name, - }); - initContainerStatuses.push({ - image: "dummy", - imageID: "dummy", - name, - ready: true, - restartCount: i, - state: { - running: { - startedAt: "before", - }, - }, - }); - } - - for (let i = 0; i < initDead; i += 1) { - const name = `container_init-dead_${i}`; - - initContainers.push({ - image: "dummy", - imagePullPolicy: "Always", - name, - }); - initContainerStatuses.push({ - image: "dummy", - imageID: "dummy", - name, - ready: false, - restartCount: i, - state: { - terminated: { - startedAt: "before", - exitCode: i+1, - finishedAt: "later", - reason: `reason_${i}`, - }, - }, - }); - } - - return pod; -} - -describe("Pods", () => { - const podTests = []; - - for (let r = 0; r < 3; r += 1) { - for (let d = 0; d < 3; d += 1) { - for (let ir = 0; ir < 3; ir += 1) { - for (let id = 0; id < 3; id += 1) { - podTests.push([r, d, ir, id]); - } - } - } - } - - describe.each(podTests)("for [%d running, %d dead] & initial [%d running, %d dead]", (running, dead, initRunning, initDead) => { - const pod = getDummyPod({ running, dead, initRunning, initDead }); - - function getNamedContainer(name: string) { - return { - image: "dummy", - imagePullPolicy: "Always", - name, - }; - } - - it("getRunningContainers should return only running and init running", () => { - const res = [ - ...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)), - ...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)), - ]; - - expect(pod.getRunningContainers()).toStrictEqual(res); - }); - - it("getAllContainers should return all containers", () => { - const res = [ - ...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)), - ...Array.from(new Array(dead), (val, index) => getNamedContainer(`container_dead_${index}`)), - ...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)), - ...Array.from(new Array(initDead), (val, index) => getNamedContainer(`container_init-dead_${index}`)), - ]; - - expect(pod.getAllContainers()).toStrictEqual(res); - }); - - it("getRestartsCount should return total restart counts", () => { - function sum(len: number): number { - let res = 0; - - for (let i = 0; i < len; i += 1) { - res += i; - } - - return res; - } - - expect(pod.getRestartsCount()).toStrictEqual(sum(running) + sum(dead)); - }); - - it("hasIssues should return false", () => { - expect(pod.hasIssues()).toStrictEqual(false); - }); - }); - - describe("getSelectedNodeOs", () => { - it("should return stable", () => { - const pod = getDummyPod(); - - pod.spec.nodeSelector = { - "kubernetes.io/os": "foobar", - }; - - expect(pod.getSelectedNodeOs()).toStrictEqual("foobar"); - }); - - it("should return beta", () => { - const pod = getDummyPod(); - - pod.spec.nodeSelector = { - "beta.kubernetes.io/os": "foobar1", - }; - - expect(pod.getSelectedNodeOs()).toStrictEqual("foobar1"); - }); - - it("should return stable over beta", () => { - const pod = getDummyPod(); - - pod.spec.nodeSelector = { - "kubernetes.io/os": "foobar2", - "beta.kubernetes.io/os": "foobar3", - }; - - expect(pod.getSelectedNodeOs()).toStrictEqual("foobar2"); - }); - - it("should return undefined if none set", () => { - const pod = getDummyPod(); - - expect(pod.getSelectedNodeOs()).toStrictEqual(undefined); - }); - }); - - describe("hasIssues", () => { - it("should return true if a condition isn't ready", () => { - const pod = getDummyPod({ running: 1 }); - - pod.status?.conditions.push({ - type: "Ready", - status: "foobar", - lastProbeTime: 1, - lastTransitionTime: "longer ago", - }); - - expect(pod.hasIssues()).toStrictEqual(true); - }); - - it("should return false if a condition is non-ready", () => { - const pod = getDummyPod({ running: 1 }); - - pod.status?.conditions.push({ - type: "dummy", - status: "foobar", - lastProbeTime: 1, - lastTransitionTime: "longer ago", - }); - - expect(pod.hasIssues()).toStrictEqual(false); - }); - - it("should return true if a current container is in a crash loop back off", () => { - const pod = getDummyPod({ running: 1 }); - const firstStatus = pod.status?.containerStatuses?.[0]; - - assert(firstStatus); - - firstStatus.state = { - waiting: { - reason: "CrashLookBackOff", - message: "too much foobar", - }, - }; - - expect(pod.hasIssues()).toStrictEqual(true); - }); - - it("should return true if a current phase isn't running", () => { - const pod = getDummyPod({ running: 1 }); - - assert(pod.status); - - pod.status.phase = "not running"; - - expect(pod.hasIssues()).toStrictEqual(true); - }); - - it("should return false if a current phase is running", () => { - const pod = getDummyPod({ running: 1 }); - - assert(pod.status); - - pod.status.phase = "Running"; - - expect(pod.hasIssues()).toStrictEqual(false); - }); - }); -}); diff --git a/src/common/k8s-api/__tests__/stateful-set.api.test.ts b/src/common/k8s-api/__tests__/stateful-set.api.test.ts deleted file mode 100644 index b7557c8010..0000000000 --- a/src/common/k8s-api/__tests__/stateful-set.api.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting"; -import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable"; -import type { StatefulSetApi } from "../endpoints"; -import statefulSetApiInjectable from "../endpoints/stateful-set.api.injectable"; -import type { KubeJsonApi } from "../kube-json-api"; - -describe("StatefulSetApi", () => { - let statefulSetApi: StatefulSetApi; - let kubeJsonApi: jest.Mocked; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - kubeJsonApi = { - getResponse: jest.fn(), - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - patch: jest.fn(), - del: jest.fn(), - } as never; - di.override(apiKubeInjectable, () => kubeJsonApi); - - statefulSetApi = di.inject(statefulSetApiInjectable); - }); - - describe("scale", () => { - it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => { - statefulSetApi.scale({ namespace: "default", name: "statefulset-1" }, 5); - - expect(kubeJsonApi.patch).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/statefulsets/statefulset-1/scale", { - data: { - spec: { - replicas: 5, - }, - }, - }, - { - headers: { - "content-type": "application/merge-patch+json", - }, - }); - }); - }); -}); diff --git a/src/common/k8s-api/api-base-configs.ts b/src/common/k8s-api/api-base-configs.ts deleted file mode 100644 index 5ac67229ec..0000000000 --- a/src/common/k8s-api/api-base-configs.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const apiBaseServerAddressInjectionToken = getInjectionToken({ - id: "api-base-config-server-address-token", -}); - -export const apiBaseHostHeaderInjectionToken = getInjectionToken({ - id: "api-base-host-header-token", -}); diff --git a/src/common/k8s-api/api-base.injectable.ts b/src/common/k8s-api/api-base.injectable.ts deleted file mode 100644 index b340882672..0000000000 --- a/src/common/k8s-api/api-base.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 { apiPrefix } from "../vars"; -import isDebuggingInjectable from "../vars/is-debugging.injectable"; -import isDevelopmentInjectable from "../vars/is-development.injectable"; -import { apiBaseHostHeaderInjectionToken, apiBaseServerAddressInjectionToken } from "./api-base-configs"; -import createJsonApiInjectable from "./create-json-api.injectable"; - -const apiBaseInjectable = getInjectable({ - id: "api-base", - instantiate: (di) => { - const createJsonApi = di.inject(createJsonApiInjectable); - const isDebugging = di.inject(isDebuggingInjectable); - const isDevelopment = di.inject(isDevelopmentInjectable); - const serverAddress = di.inject(apiBaseServerAddressInjectionToken); - const hostHeaderValue = di.inject(apiBaseHostHeaderInjectionToken); - - return createJsonApi({ - serverAddress, - apiBase: apiPrefix, - debug: isDevelopment || isDebugging, - }, { - headers: { - "Host": hostHeaderValue, - }, - }); - }, -}); - -export default apiBaseInjectable; diff --git a/src/common/k8s-api/api-kube.ts b/src/common/k8s-api/api-kube.ts deleted file mode 100644 index 58aa95208a..0000000000 --- a/src/common/k8s-api/api-kube.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { KubeJsonApi } from "./kube-json-api"; - -export const apiKubeInjectionToken = getInjectionToken({ - id: "api-kube-injection-token", -}); diff --git a/src/common/k8s-api/api-manager/api-manager.ts b/src/common/k8s-api/api-manager/api-manager.ts deleted file mode 100644 index 81b59ef9c2..0000000000 --- a/src/common/k8s-api/api-manager/api-manager.ts +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectStore } from "../kube-object.store"; - -import type { IComputedValue } from "mobx"; -import { autorun, action, observable } from "mobx"; -import type { KubeApi } from "../kube-api"; -import type { KubeObject, ObjectReference } from "../kube-object"; -import { parseKubeApi, createKubeApiURL } from "../kube-api-parse"; -import { chain, find } from "../../utils/iter"; - -export type RegisterableStore = Store extends KubeObjectStore - ? Store - : never; -export type RegisterableApi = Api extends KubeApi - ? Api - : never; -export type KubeObjectStoreFrom = Api extends KubeApi - ? KubeObjectStore - : never; - -export type FindApiCallback = (api: KubeApi) => boolean; - -interface Dependencies { - readonly apis: IComputedValue; - readonly stores: IComputedValue; -} - -export class ApiManager { - private readonly externalApis = observable.array(); - private readonly externalStores = observable.array(); - - private readonly apis = observable.map(); - - constructor(private readonly dependencies: Dependencies) { - // NOTE: this is done to preserve the old behaviour of an API being discoverable using all previous apiBases - autorun(() => { - const apis = chain(this.dependencies.apis.get().values()) - .concat(this.externalApis.values()); - const removedApis = new Set(this.apis.values()); - - for (const api of apis) { - removedApis.delete(api); - this.apis.set(api.apiBase, api); - } - - for (const api of removedApis) { - for (const [apiBase, storedApi] of this.apis) { - if (storedApi === api) { - this.apis.delete(apiBase); - } - } - } - }); - } - - getApi(pathOrCallback: string | FindApiCallback) { - if (typeof pathOrCallback === "function") { - return find(this.apis.values(), pathOrCallback); - } - - const { apiBase } = parseKubeApi(pathOrCallback); - - return this.apis.get(apiBase); - } - - getApiByKind(kind: string, apiVersion: string) { - return this.getApi(api => api.kind === kind && api.apiVersionWithGroup === apiVersion); - } - - registerApi(api: RegisterableApi): void; - /** - * @deprecated Just register the `api` by itself - */ - registerApi(apiBase: string, api: RegisterableApi): void; - registerApi(...args: [RegisterableApi] | [string, RegisterableApi]) { - if (args.length === 1) { - this.externalApis.push(args[0]); - } else { - this.externalApis.push(args[1]); - } - } - - unregisterApi(apiOrBase: string | KubeApi) { - if (typeof apiOrBase === "string") { - const api = this.externalApis.find(api => api.apiBase === apiOrBase); - - if (api) { - this.externalApis.remove(api); - } - } else { - this.unregisterApi(apiOrBase.apiBase); - } - } - - registerStore(store: RegisterableStore): void; - /** - * @deprecated KubeObjectStore's should only every be about a single KubeApi type - */ - registerStore(store: RegisterableStore, apis: KubeApi[]): void; - - @action - registerStore(store: RegisterableStore): void { - this.externalStores.push(store); - } - - getStore(api: string | undefined): KubeObjectStore | undefined; - getStore(api: RegisterableApi): KubeObjectStoreFrom | undefined; - /** - * @deprecated use an actual cast instead of hiding it with this unused type param - */ - getStore(api: string | KubeApi): Store | undefined ; - getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined { - if (!apiOrBase) { - return undefined; - } - - const { apiBase } = typeof apiOrBase === "string" - ? parseKubeApi(apiOrBase) - : apiOrBase; - const api = this.getApi(apiBase); - - if (!api) { - return undefined; - } - - return chain(this.dependencies.stores.get().values()) - .concat(this.externalStores.values()) - .find(store => store.api.apiBase === api.apiBase); - } - - lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string { - const { - kind, apiVersion = "v1", name, - namespace = parentObject?.getNs(), - } = ref; - - if (!kind) return ""; - - // search in registered apis by 'kind' & 'apiVersion' - const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion); - - if (api) { - return api.formatUrlForNotListing({ namespace, name }); - } - - // lookup api by generated resource link - const apiPrefixes = ["/apis", "/api"]; - const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`; - - for (const apiPrefix of apiPrefixes) { - const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource }); - - if (this.getApi(apiLink)) { - return apiLink; - } - } - - // resolve by kind only (hpa's might use refs to older versions of resources for example) - const apiByKind = this.getApi(api => api.kind === kind); - - if (apiByKind) { - return apiByKind.formatUrlForNotListing({ name, namespace }); - } - - // otherwise generate link with default prefix - // resource still might exists in k8s, but api is not registered in the app - return createKubeApiURL({ apiVersion, name, namespace, resource }); - } -} diff --git a/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts b/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts deleted file mode 100644 index d9a68a988c..0000000000 --- a/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 EventEmitter from "events"; -import type TypedEventEmitter from "typed-emitter"; -import type { CustomResourceDefinition } from "../endpoints"; -import type { KubeApi } from "../kube-api"; - -export interface LegacyAutoRegistration { - customResourceDefinition: (crd: CustomResourceDefinition) => void; - kubeApi: (api: KubeApi) => void; -} - -/** - * This is used to remove dependency cycles from auto registering of instances - * - * - Custom Resource Definitions get their own registered store (will need in the future) - * - All KubeApi's get auto registered (this should be changed in the future) - */ -const autoRegistrationEmitterInjectable = getInjectable({ - id: "auto-registration-emitter", - instantiate: (): TypedEventEmitter => new EventEmitter(), -}); - -export default autoRegistrationEmitterInjectable; diff --git a/src/common/k8s-api/api-manager/index.ts b/src/common/k8s-api/api-manager/index.ts deleted file mode 100644 index 56182e815b..0000000000 --- a/src/common/k8s-api/api-manager/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./api-manager"; diff --git a/src/common/k8s-api/api-manager/kube-object-store-token.ts b/src/common/k8s-api/api-manager/kube-object-store-token.ts deleted file mode 100644 index bbf272db24..0000000000 --- a/src/common/k8s-api/api-manager/kube-object-store-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { KubeObjectStore } from "../kube-object.store"; - -export const kubeObjectStoreInjectionToken = getInjectionToken>({ - id: "kube-object-store-token", -}); diff --git a/src/common/k8s-api/api-manager/manager.injectable.ts b/src/common/k8s-api/api-manager/manager.injectable.ts deleted file mode 100644 index f0b61c28b6..0000000000 --- a/src/common/k8s-api/api-manager/manager.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ApiManager } from "./api-manager"; -import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; -import { kubeObjectStoreInjectionToken } from "./kube-object-store-token"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import { computed } from "mobx"; - -const apiManagerInjectable = getInjectable({ - id: "api-manager", - instantiate: (di) => { - const computedInjectMany = di.inject(computedInjectManyInjectable); - const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken); - - return new ApiManager({ - apis: storesAndApisCanBeCreated - ? computedInjectMany(kubeApiInjectionToken) - : computed(() => []), - stores: storesAndApisCanBeCreated - ? computedInjectMany(kubeObjectStoreInjectionToken) - : computed(() => []), - }); - }, -}); - -export default apiManagerInjectable; diff --git a/src/common/k8s-api/api-manager/resource.store.ts b/src/common/k8s-api/api-manager/resource.store.ts deleted file mode 100644 index c81ce7daec..0000000000 --- a/src/common/k8s-api/api-manager/resource.store.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeApi } from "../kube-api"; -import type { KubeObjectStoreDependencies } from "../kube-object.store"; -import { KubeObjectStore } from "../kube-object.store"; -import type { KubeObject } from "../kube-object"; - -export class CustomResourceStore extends KubeObjectStore> { - constructor(deps: KubeObjectStoreDependencies, api: KubeApi) { - super(deps, api); - } -} diff --git a/src/common/k8s-api/create-json-api.injectable.ts b/src/common/k8s-api/create-json-api.injectable.ts deleted file mode 100644 index 7f8559bf3a..0000000000 --- a/src/common/k8s-api/create-json-api.injectable.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 { Agent } from "https"; -import type { RequestInit } from "node-fetch"; -import lensProxyCertificateInjectable from "../certificate/lens-proxy-certificate.injectable"; -import fetchInjectable from "../fetch/fetch.injectable"; -import loggerInjectable from "../logger.injectable"; -import type { JsonApiConfig, JsonApiData, JsonApiDependencies, JsonApiParams } from "./json-api"; -import { JsonApi } from "./json-api"; - -export type CreateJsonApi = = JsonApiParams>(config: JsonApiConfig, reqInit?: RequestInit) => JsonApi; - -const createJsonApiInjectable = getInjectable({ - id: "create-json-api", - instantiate: (di): CreateJsonApi => { - const deps: JsonApiDependencies = { - fetch: di.inject(fetchInjectable), - logger: di.inject(loggerInjectable), - }; - const lensProxyCert = di.inject(lensProxyCertificateInjectable); - - return (config, reqInit) => { - if (!config.getRequestOptions) { - config.getRequestOptions = async () => { - const agent = new Agent({ - ca: lensProxyCert.get().cert, - }); - - return { - agent, - }; - }; - } - - return new JsonApi(deps, config, reqInit); - }; - }, -}); - -export default createJsonApiInjectable; diff --git a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts b/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts deleted file mode 100644 index dc3a3fef69..0000000000 --- a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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 loggerInjectable from "../logger.injectable"; -import { apiKubePrefix } from "../vars"; -import isDevelopmentInjectable from "../vars/is-development.injectable"; -import apiBaseInjectable from "./api-base.injectable"; -import type { KubeApiConstructor } from "./create-kube-api-for-remote-cluster.injectable"; -import createKubeJsonApiInjectable from "./create-kube-json-api.injectable"; -import { KubeApi } from "./kube-api"; -import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "./kube-object"; - -export interface CreateKubeApiForLocalClusterConfig { - metadata: { - uid: string; - }; -} - -export interface CreateKubeApiForCluster { - , Data extends KubeJsonApiDataFor>( - cluster: CreateKubeApiForLocalClusterConfig, - kubeClass: KubeObjectConstructor, - apiClass: KubeApiConstructor, - ): Api; - >( - cluster: CreateKubeApiForLocalClusterConfig, - kubeClass: KubeObjectConstructor, - apiClass?: KubeApiConstructor>, - ): KubeApi; -} - -const createKubeApiForClusterInjectable = getInjectable({ - id: "create-kube-api-for-cluster", - instantiate: (di): CreateKubeApiForCluster => { - const apiBase = di.inject(apiBaseInjectable); - const isDevelopment = di.inject(isDevelopmentInjectable); - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - const logger = di.inject(loggerInjectable); - - return ( - cluster: CreateKubeApiForLocalClusterConfig, - kubeClass: KubeObjectConstructor>, - apiClass?: KubeApiConstructor>, - ) => { - const request = createKubeJsonApi( - { - serverAddress: apiBase.config.serverAddress, - apiBase: apiKubePrefix, - debug: isDevelopment, - }, { - headers: { - "Host": `${cluster.metadata.uid}.lens.app:${new URL(apiBase.config.serverAddress).port}`, - }, - }); - - if (apiClass) { - return new apiClass({ - objectConstructor: kubeClass, - request, - }); - } - - return new KubeApi( - { - logger, - maybeKubeApi: undefined, - }, - { - objectConstructor: kubeClass, - request, - }, - ); - }; - }, -}); - -export default createKubeApiForClusterInjectable; diff --git a/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts b/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts deleted file mode 100644 index 8b39387d1d..0000000000 --- a/src/common/k8s-api/create-kube-api-for-remote-cluster.injectable.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * 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 { AgentOptions } from "https"; -import { Agent } from "https"; -import type { RequestInit } from "node-fetch"; -import loggerInjectable from "../logger.injectable"; -import isDevelopmentInjectable from "../vars/is-development.injectable"; -import createKubeJsonApiInjectable from "./create-kube-json-api.injectable"; -import type { KubeApiOptions } from "./kube-api"; -import { KubeApi } from "./kube-api"; -import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "./kube-object"; - -export interface CreateKubeApiForRemoteClusterConfig { - cluster: { - server: string; - caData?: string; - skipTLSVerify?: boolean; - }; - user: { - token?: string | (() => Promise); - clientCertificateData?: string; - clientKeyData?: string; - }; - /** - * Custom instance of https.agent to use for the requests - * - * @remarks the custom agent replaced default agent, options skipTLSVerify, - * clientCertificateData, clientKeyData and caData are ignored. - */ - agent?: Agent; -} - -export type KubeApiConstructor> = new (apiOpts: KubeApiOptions) => Api; - -export interface CreateKubeApiForRemoteCluster { - , Data extends KubeJsonApiDataFor>( - config: CreateKubeApiForRemoteClusterConfig, - kubeClass: KubeObjectConstructor, - apiClass: KubeApiConstructor, - ): Api; - >( - config: CreateKubeApiForRemoteClusterConfig, - kubeClass: KubeObjectConstructor, - apiClass?: KubeApiConstructor>, - ): KubeApi; -} - -const createKubeApiForRemoteClusterInjectable = getInjectable({ - id: "create-kube-api-for-remote-cluster", - instantiate: (di): CreateKubeApiForRemoteCluster => { - const isDevelopment = di.inject(isDevelopmentInjectable); - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - const logger = di.inject(loggerInjectable); - - return ( - config: CreateKubeApiForRemoteClusterConfig, - kubeClass: KubeObjectConstructor>, - apiClass?: KubeApiConstructor>, - ) => { - const reqInit: RequestInit = {}; - const agentOptions: AgentOptions = {}; - - if (config.cluster.skipTLSVerify === true) { - agentOptions.rejectUnauthorized = false; - } - - if (config.user.clientCertificateData) { - agentOptions.cert = config.user.clientCertificateData; - } - - if (config.user.clientKeyData) { - agentOptions.key = config.user.clientKeyData; - } - - if (config.cluster.caData) { - agentOptions.ca = config.cluster.caData; - } - - if (Object.keys(agentOptions).length > 0) { - reqInit.agent = new Agent(agentOptions); - } - - if (config.agent) { - reqInit.agent = config.agent; - } - - const token = config.user.token; - const request = createKubeJsonApi({ - serverAddress: config.cluster.server, - apiBase: "", - debug: isDevelopment, - ...(token ? { - getRequestOptions: async () => ({ - headers: { - "Authorization": `Bearer ${typeof token === "function" ? await token() : token}`, - }, - }), - } : {}), - }, reqInit); - - if (apiClass) { - return new apiClass({ - objectConstructor: kubeClass, - request, - }); - } - - return new KubeApi( - { - logger, - maybeKubeApi: undefined, - }, - { - objectConstructor: kubeClass, - request, - }, - ); - }; - }, -}); - -export default createKubeApiForRemoteClusterInjectable; diff --git a/src/common/k8s-api/create-kube-api.injectable.ts b/src/common/k8s-api/create-kube-api.injectable.ts deleted file mode 100644 index cce470b132..0000000000 --- a/src/common/k8s-api/create-kube-api.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 loggerInjectable from "../logger.injectable"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "./kube-api"; -import maybeKubeApiInjectable from "./maybe-kube-api.injectable"; - -export interface CreateKubeApi { - (ctor: new (deps: KubeApiDependencies, opts: DerivedKubeApiOptions) => Api, opts?: DerivedKubeApiOptions): Api; -} - -const createKubeApiInjectable = getInjectable({ - id: "create-kube-api", - instantiate: (di): CreateKubeApi => { - const deps: KubeApiDependencies = { - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }; - - return (ctor, opts) => new ctor(deps, opts ?? {}); - }, -}); - -export default createKubeApiInjectable; diff --git a/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts b/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts deleted file mode 100644 index 799b0bf963..0000000000 --- a/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 { apiKubePrefix } from "../vars"; -import isDebuggingInjectable from "../vars/is-debugging.injectable"; -import { apiBaseHostHeaderInjectionToken, apiBaseServerAddressInjectionToken } from "./api-base-configs"; -import createKubeJsonApiInjectable from "./create-kube-json-api.injectable"; -import type { KubeJsonApi } from "./kube-json-api"; - -export type CreateKubeJsonApiForCluster = (clusterId: string) => KubeJsonApi; - -const createKubeJsonApiForClusterInjectable = getInjectable({ - id: "create-kube-json-api-for-cluster", - instantiate: (di): CreateKubeJsonApiForCluster => { - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - const isDebugging = di.inject(isDebuggingInjectable); - - return (clusterId) => createKubeJsonApi( - { - serverAddress: di.inject(apiBaseServerAddressInjectionToken), - apiBase: apiKubePrefix, - debug: isDebugging, - }, - { - headers: { - "Host": `${clusterId}.${di.inject(apiBaseHostHeaderInjectionToken)}`, - }, - }, - ); - }, -}); - -export default createKubeJsonApiForClusterInjectable; diff --git a/src/common/k8s-api/create-kube-json-api.injectable.ts b/src/common/k8s-api/create-kube-json-api.injectable.ts deleted file mode 100644 index f7b5a152ee..0000000000 --- a/src/common/k8s-api/create-kube-json-api.injectable.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 { Agent } from "https"; -import type { RequestInit } from "node-fetch"; -import lensProxyCertificateInjectable from "../certificate/lens-proxy-certificate.injectable"; -import fetchInjectable from "../fetch/fetch.injectable"; -import loggerInjectable from "../logger.injectable"; -import type { JsonApiConfig, JsonApiDependencies } from "./json-api"; -import { KubeJsonApi } from "./kube-json-api"; - -export type CreateKubeJsonApi = (config: JsonApiConfig, reqInit?: RequestInit) => KubeJsonApi; - -const createKubeJsonApiInjectable = getInjectable({ - id: "create-kube-json-api", - instantiate: (di): CreateKubeJsonApi => { - const dependencies: JsonApiDependencies = { - fetch: di.inject(fetchInjectable), - logger: di.inject(loggerInjectable), - }; - const lensProxyCert = di.inject(lensProxyCertificateInjectable); - - return (config, reqInit) => { - if (!config.getRequestOptions) { - config.getRequestOptions = async () => { - const agent = new Agent({ - ca: lensProxyCert.get().cert, - }); - - return { - agent, - }; - }; - } - - return new KubeJsonApi(dependencies, config, reqInit); - }; - }, -}); - -export default createKubeJsonApiInjectable; diff --git a/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts b/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts deleted file mode 100644 index 4965a86edc..0000000000 --- a/src/common/k8s-api/endpoints/cluster-role-binding.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ClusterRoleBindingApi } from "./cluster-role-binding.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const clusterRoleBindingApiInjectable = getInjectable({ - id: "cluster-role-binding-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleBindingApi is only accessible in certain environments"); - - return new ClusterRoleBindingApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default clusterRoleBindingApiInjectable; diff --git a/src/common/k8s-api/endpoints/cluster-role-binding.api.ts b/src/common/k8s-api/endpoints/cluster-role-binding.api.ts deleted file mode 100644 index 827a3ce95d..0000000000 --- a/src/common/k8s-api/endpoints/cluster-role-binding.api.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { RoleRef } from "./types/role-ref"; -import type { Subject } from "./types/subject"; - -export interface ClusterRoleBindingData extends KubeJsonApiData, void, void> { - subjects?: Subject[]; - roleRef: RoleRef; -} - -export class ClusterRoleBinding extends KubeObject< - ClusterScopedMetadata, - void, - void -> { - static kind = "ClusterRoleBinding"; - static namespaced = false; - static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings"; - - subjects?: Subject[]; - roleRef: RoleRef; - - constructor({ - subjects, - roleRef, - ...rest - }: ClusterRoleBindingData) { - super(rest); - this.subjects = subjects; - this.roleRef = roleRef; - } - - getSubjects() { - return this.subjects ?? []; - } - - getSubjectNames(): string { - return this.getSubjects().map(subject => subject.name).join(", "); - } -} - -export class ClusterRoleBindingApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: ClusterRoleBinding, - }); - } -} diff --git a/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts b/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts deleted file mode 100644 index b22e198d1e..0000000000 --- a/src/common/k8s-api/endpoints/cluster-role.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ClusterRoleApi } from "./cluster-role.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const clusterRoleApiInjectable = getInjectable({ - id: "cluster-role-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleApi is only available in certain environments"); - - return new ClusterRoleApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default clusterRoleApiInjectable; diff --git a/src/common/k8s-api/endpoints/cluster-role.api.ts b/src/common/k8s-api/endpoints/cluster-role.api.ts deleted file mode 100644 index e55f934df9..0000000000 --- a/src/common/k8s-api/endpoints/cluster-role.api.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { AggregationRule } from "./types/aggregation-rule"; -import type { PolicyRule } from "./types/policy-rule"; - -export interface ClusterRoleData extends KubeJsonApiData, void, void> { - rules?: PolicyRule[]; - aggregationRule?: AggregationRule; -} - -export class ClusterRole extends KubeObject< - ClusterScopedMetadata, - void, - void -> { - static kind = "ClusterRole"; - static namespaced = false; - static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterroles"; - - rules?: PolicyRule[]; - aggregationRule?: AggregationRule; - - constructor({ rules, aggregationRule, ...rest }: ClusterRoleData) { - super(rest); - this.rules = rules; - this.aggregationRule = aggregationRule; - } - - getRules() { - return this.rules || []; - } -} - -export class ClusterRoleApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: ClusterRole, - }); - } -} diff --git a/src/common/k8s-api/endpoints/cluster.api.injectable.ts b/src/common/k8s-api/endpoints/cluster.api.injectable.ts deleted file mode 100644 index 494ef9d94b..0000000000 --- a/src/common/k8s-api/endpoints/cluster.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ClusterApi } from "./cluster.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const clusterApiInjectable = getInjectable({ - id: "cluster-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterApi is only available in certain environments"); - - return new ClusterApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default clusterApiInjectable; diff --git a/src/common/k8s-api/endpoints/cluster.api.ts b/src/common/k8s-api/endpoints/cluster.api.ts deleted file mode 100644 index 92f42f1e4e..0000000000 --- a/src/common/k8s-api/endpoints/cluster.api.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export class ClusterApi extends KubeApi { - /** - * @deprecated This field is legacy and never used. - */ - static kind = "Cluster"; - - /** - * @deprecated This field is legacy and never used. - */ - static namespaced = true; - - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Cluster, - }); - } -} - -export enum ClusterStatus { - ACTIVE = "Active", - CREATING = "Creating", - REMOVING = "Removing", - ERROR = "Error", -} - -export interface Cluster { - spec: { - clusterNetwork?: { - serviceDomain?: string; - pods?: { - cidrBlocks?: string[]; - }; - services?: { - cidrBlocks?: string[]; - }; - }; - providerSpec: { - value: { - profile: string; - }; - }; - }; - status?: { - apiEndpoints: { - host: string; - port: string; - }[]; - providerStatus: { - adminUser?: string; - adminPassword?: string; - kubeconfig?: string; - processState?: string; - lensAddress?: string; - }; - errorMessage?: string; - errorReason?: string; - }; -} - -export class Cluster extends KubeObject { - static kind = "Cluster"; - static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters"; - static namespaced = true; - - getStatus() { - if (this.metadata.deletionTimestamp) return ClusterStatus.REMOVING; - if (!this.status || !this.status) return ClusterStatus.CREATING; - if (this.status.errorMessage) return ClusterStatus.ERROR; - - return ClusterStatus.ACTIVE; - } -} diff --git a/src/common/k8s-api/endpoints/component-status.api.injectable.ts b/src/common/k8s-api/endpoints/component-status.api.injectable.ts deleted file mode 100644 index 01bb16e6b4..0000000000 --- a/src/common/k8s-api/endpoints/component-status.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ComponentStatusApi } from "./component-status.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; -import loggerInjectable from "../../logger.injectable"; - -const componentStatusApiInjectable = getInjectable({ - id: "component-status-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "componentStatusApi is only available in certain environments"); - - return new ComponentStatusApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default componentStatusApiInjectable; diff --git a/src/common/k8s-api/endpoints/component-status.api.ts b/src/common/k8s-api/endpoints/component-status.api.ts deleted file mode 100644 index a35fc92c44..0000000000 --- a/src/common/k8s-api/endpoints/component-status.api.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export interface ComponentStatusCondition { - type: string; - status: string; - message: string; -} - -export interface ComponentStatus { - conditions: ComponentStatusCondition[]; -} - -export class ComponentStatus extends KubeObject { - static kind = "ComponentStatus"; - static namespaced = false; - static apiBase = "/api/v1/componentstatuses"; - - getTruthyConditions() { - return this.conditions.filter(c => c.status === "True"); - } -} - -export class ComponentStatusApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: ComponentStatus, - }); - } -} diff --git a/src/common/k8s-api/endpoints/config-map.api.injectable.ts b/src/common/k8s-api/endpoints/config-map.api.injectable.ts deleted file mode 100644 index e41cc23111..0000000000 --- a/src/common/k8s-api/endpoints/config-map.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ConfigMapApi } from "./config-map.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const configMapApiInjectable = getInjectable({ - id: "config-map-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "configMapApi is only available in certain environments"); - - return new ConfigMapApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default configMapApiInjectable; diff --git a/src/common/k8s-api/endpoints/config-map.api.ts b/src/common/k8s-api/endpoints/config-map.api.ts deleted file mode 100644 index a2860246b1..0000000000 --- a/src/common/k8s-api/endpoints/config-map.api.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import { autoBind } from "../../utils"; - -export interface ConfigMapData extends KubeJsonApiData, void, void> { - data?: Partial>; - binaryData?: Partial>; - immutable?: boolean; -} - -export class ConfigMap extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static kind = "ConfigMap"; - static namespaced = true; - static apiBase = "/api/v1/configmaps"; - - data: Partial>; - binaryData: Partial>; - immutable?: boolean; - - constructor({ data, binaryData, immutable, ...rest }: ConfigMapData) { - super(rest); - autoBind(this); - - this.data = data ?? {}; - this.binaryData = binaryData ?? {}; - this.immutable = immutable; - } - - getKeys(): string[] { - return Object.keys(this.data); - } -} - -export class ConfigMapApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - objectConstructor: ConfigMap, - ...opts ?? {}, - }); - } -} diff --git a/src/common/k8s-api/endpoints/cron-job.api.injectable.ts b/src/common/k8s-api/endpoints/cron-job.api.injectable.ts deleted file mode 100644 index 2831a726aa..0000000000 --- a/src/common/k8s-api/endpoints/cron-job.api.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { CronJobApi } from "./cron-job.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const cronJobApiInjectable = getInjectable({ - id: "cron-job-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "cronJobApi is only available in certain environments"); - - return new CronJobApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }, { - checkPreferredVersion: true, - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default cronJobApiInjectable; diff --git a/src/common/k8s-api/endpoints/cron-job.api.ts b/src/common/k8s-api/endpoints/cron-job.api.ts deleted file mode 100644 index 2ccb8c910c..0000000000 --- a/src/common/k8s-api/endpoints/cron-job.api.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; -import type { NamespaceScopedMetadata, ObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { formatDuration } from "../../utils/formatDuration"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { JobTemplateSpec } from "./types/job-template-spec"; - -export class CronJobApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions) { - super(deps, { - ...opts, - objectConstructor: CronJob, - }); - } - - suspend(params: { namespace: string; name: string }) { - return this.request.patch(this.getUrl(params), { - data: { - spec: { - suspend: true, - }, - }, - }, - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } - - resume(params: { namespace: string; name: string }) { - return this.request.patch(this.getUrl(params), { - data: { - spec: { - suspend: false, - }, - }, - }, - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } -} - -export interface CronJobSpec { - concurrencyPolicy?: string; - failedJobsHistoryLimit?: number; - jobTemplate?: JobTemplateSpec; - schedule: string; - startingDeadlineSeconds?: number; - successfulJobsHistoryLimit?: number; - suspend?: boolean; -} - -export interface CronJobStatus { - lastScheduleTime?: string; - lastSuccessfulTime?: string; - active?: ObjectReference[]; -} - -export class CronJob extends KubeObject< - NamespaceScopedMetadata, - CronJobStatus, - CronJobSpec -> { - static readonly kind = "CronJob"; - static readonly namespaced = true; - static readonly apiBase = "/apis/batch/v1/cronjobs"; - - getSuspendFlag() { - return (this.spec.suspend ?? false).toString(); - } - - getLastScheduleTime() { - if (!this.status?.lastScheduleTime) return "-"; - const diff = moment().diff(this.status.lastScheduleTime); - - return formatDuration(diff, true); - } - - getSchedule() { - return this.spec.schedule; - } - - isNeverRun() { - const schedule = this.getSchedule(); - const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - const stamps = schedule.split(" "); - const day = Number(stamps[stamps.length - 3]); // 1-31 - const month = Number(stamps[stamps.length - 2]); // 1-12 - - if (schedule.startsWith("@")) return false; - - return day > daysInMonth[month - 1]; - } - - isSuspend() { - return this.spec.suspend; - } -} diff --git a/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts b/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts deleted file mode 100644 index cf8a6560a0..0000000000 --- a/src/common/k8s-api/endpoints/custom-resource-definition.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { CustomResourceDefinitionApi } from "./custom-resource-definition.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; -import loggerInjectable from "../../logger.injectable"; - -const customResourceDefinitionApiInjectable = getInjectable({ - id: "custom-resource-definition-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "customResourceDefinitionApi is only available in certain environments"); - - return new CustomResourceDefinitionApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default customResourceDefinitionApiInjectable; diff --git a/src/common/k8s-api/endpoints/custom-resource-definition.api.ts b/src/common/k8s-api/endpoints/custom-resource-definition.api.ts deleted file mode 100644 index b438f06b3a..0000000000 --- a/src/common/k8s-api/endpoints/custom-resource-definition.api.ts +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getLegacyGlobalDiForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import customResourcesRouteInjectable from "../../front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable"; -import { buildURL } from "../../utils/buildUrl"; -import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { JSONSchemaProps } from "./types/json-schema-props"; - -interface AdditionalPrinterColumnsCommon { - name: string; - type: "integer" | "number" | "string" | "boolean" | "date"; - priority?: number; - format?: "int32" | "int64" | "float" | "double" | "byte" | "binary" | "date" | "date-time" | "password"; - description?: string; -} - -export type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & { - jsonPath: string; -}; - -type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & { - JSONPath: string; -}; - -export interface CustomResourceValidation { - openAPIV3Schema?: JSONSchemaProps; -} - -export interface CustomResourceDefinitionVersion { - name: string; - served: boolean; - storage: boolean; - schema?: CustomResourceValidation; // required in v1 but not present in v1beta - additionalPrinterColumns?: AdditionalPrinterColumnsV1[]; -} - -export interface CustomResourceDefinitionNames { - categories?: string[]; - kind: string; - listKind?: string; - plural: string; - shortNames?: string[]; - singular?: string; -} - -export interface CustomResourceConversion { - strategy?: string; - webhook?: WebhookConversion; -} - -export interface WebhookConversion { - clientConfig?: WebhookClientConfig[]; - conversionReviewVersions: string[]; -} - -export interface WebhookClientConfig { - caBundle?: string; - url?: string; - service?: ServiceReference; -} - -export interface ServiceReference { - name: string; - namespace: string; - path?: string; - port?: number; -} - -export interface CustomResourceDefinitionSpec { - group: string; - /** - * @deprecated for apiextensions.k8s.io/v1 but used in v1beta1 - */ - version?: string; - names: CustomResourceDefinitionNames; - scope: "Namespaced" | "Cluster"; - /** - * @deprecated for apiextensions.k8s.io/v1 but used in v1beta1 - */ - validation?: object; - versions?: CustomResourceDefinitionVersion[]; - conversion?: CustomResourceConversion; - /** - * @deprecated for apiextensions.k8s.io/v1 but used in v1beta1 - */ - additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[]; - preserveUnknownFields?: boolean; -} - -export interface CustomResourceDefinitionConditionAcceptedNames { - plural: string; - singular: string; - kind: string; - shortNames: string[]; - listKind: string; -} - -export interface CustomResourceDefinitionStatus { - conditions?: BaseKubeObjectCondition[]; - acceptedNames: CustomResourceDefinitionConditionAcceptedNames; - storedVersions: string[]; -} - -export class CustomResourceDefinition extends KubeObject< - ClusterScopedMetadata, - CustomResourceDefinitionStatus, - CustomResourceDefinitionSpec -> { - static kind = "CustomResourceDefinition"; - static namespaced = false; - static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions"; - - getResourceUrl() { - const di = getLegacyGlobalDiForExtensionApi(); - - const customResourcesRoute = di.inject(customResourcesRouteInjectable); - - return buildURL(customResourcesRoute.path, { - params: { - group: this.getGroup(), - name: this.getPluralName(), - }, - }); - } - - getResourceApiBase() { - const { group } = this.spec; - - return `/apis/${group}/${this.getVersion()}/${this.getPluralName()}`; - } - - getPluralName() { - return this.getNames().plural; - } - - getResourceKind() { - return this.spec.names.kind; - } - - getResourceTitle() { - const name = this.getPluralName(); - - return name[0].toUpperCase() + name.slice(1); - } - - getGroup() { - return this.spec.group; - } - - getScope() { - return this.spec.scope; - } - - getPreferedVersion(): CustomResourceDefinitionVersion { - const { apiVersion } = this; - - switch (apiVersion) { - case "apiextensions.k8s.io/v1": - for (const version of this.spec.versions ?? []) { - if (version.storage) { - return version; - } - } - break; - - case "apiextensions.k8s.io/v1beta1": { - const { additionalPrinterColumns: apc } = this.spec; - const additionalPrinterColumns = apc?.map(({ JSONPath, ...apc }) => ({ ...apc, jsonPath: JSONPath })); - - return { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - name: this.spec.version!, - served: true, - storage: true, - schema: this.spec.validation, - additionalPrinterColumns, - }; - } - } - - throw new Error(`Unknown apiVersion=${apiVersion}: Failed to find a version for CustomResourceDefinition ${this.metadata.name}`); - } - - getVersion() { - return this.getPreferedVersion().name; - } - - isNamespaced() { - return this.getScope() === "Namespaced"; - } - - getStoredVersions() { - return this.status?.storedVersions.join(", ") ?? ""; - } - - getNames() { - return this.spec.names; - } - - getConversion() { - return JSON.stringify(this.spec.conversion); - } - - getPrinterColumns(ignorePriority = true): AdditionalPrinterColumnsV1[] { - const columns = this.getPreferedVersion().additionalPrinterColumns ?? []; - - return columns - .filter(column => column.name.toLowerCase() != "age" && (ignorePriority || !column.priority)); - } - - getValidation() { - return JSON.stringify(this.getPreferedVersion().schema, null, 2); - } - - getConditions() { - if (!this.status?.conditions) return []; - - return this.status.conditions.map(condition => { - const { message, reason, lastTransitionTime, status } = condition; - - return { - ...condition, - isReady: status === "True", - tooltip: `${message || reason} (${lastTransitionTime})`, - }; - }); - } -} - -export class CustomResourceDefinitionApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: CustomResourceDefinition, - checkPreferredVersion: true, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts b/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts deleted file mode 100644 index 8a904519b6..0000000000 --- a/src/common/k8s-api/endpoints/daemon-set.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { DaemonSetApi } from "./daemon-set.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const daemonSetApiInjectable = getInjectable({ - id: "daemon-set-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "daemonSetApi is only available in certain environements"); - - return new DaemonSetApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default daemonSetApiInjectable; diff --git a/src/common/k8s-api/endpoints/daemon-set.api.ts b/src/common/k8s-api/endpoints/daemon-set.api.ts deleted file mode 100644 index 6661e2ff43..0000000000 --- a/src/common/k8s-api/endpoints/daemon-set.api.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { PodTemplateSpec } from "./types/pod-template-spec"; - -export interface RollingUpdateDaemonSet { - maxUnavailable?: number | string; - maxSurge?: number | string; -} - -export interface DaemonSetUpdateStrategy { - type: string; - rollingUpdate: RollingUpdateDaemonSet; -} - -export interface DaemonSetSpec { - selector: LabelSelector; - template: PodTemplateSpec; - updateStrategy: DaemonSetUpdateStrategy; - minReadySeconds?: number; - revisionHistoryLimit?: number; -} - -export interface DaemonSetStatus extends KubeObjectStatus { - collisionCount?: number; - currentNumberScheduled: number; - desiredNumberScheduled: number; - numberAvailable?: number; - numberMisscheduled: number; - numberReady: number; - numberUnavailable?: number; - observedGeneration?: number; - updatedNumberScheduled?: number; -} - -export class DaemonSet extends KubeObject< - NamespaceScopedMetadata, - DaemonSetStatus, - DaemonSetSpec -> { - static kind = "DaemonSet"; - static namespaced = true; - static apiBase = "/apis/apps/v1/daemonsets"; - - getSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.selector.matchLabels); - } - - getNodeSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.template.spec?.nodeSelector); - } - - getTemplateLabels(): string[] { - return KubeObject.stringifyLabels(this.spec.template.metadata?.labels); - } - - getTolerations() { - return this.spec.template.spec?.tolerations ?? []; - } - - getAffinity() { - return this.spec.template.spec?.affinity; - } - - getAffinityNumber() { - return Object.keys(this.getAffinity() ?? {}).length; - } - - getImages() { - const containers = this.spec.template?.spec?.containers ?? []; - const initContainers = this.spec.template?.spec?.initContainers ?? []; - - return [...containers, ...initContainers].map(container => container.image); - } -} - -export class DaemonSetApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: DaemonSet, - }); - } - - restart(params: { namespace: string; name: string }) { - return this.request.patch(this.getUrl(params), { - data: { - spec: { - template: { - metadata: { - annotations: { "kubectl.kubernetes.io/restartedAt" : moment.utc().format() }, - }, - }, - }, - }, - }, - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } -} diff --git a/src/common/k8s-api/endpoints/deployment.api.injectable.ts b/src/common/k8s-api/endpoints/deployment.api.injectable.ts deleted file mode 100644 index 47236b1a6f..0000000000 --- a/src/common/k8s-api/endpoints/deployment.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { DeploymentApi } from "./deployment.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const deploymentApiInjectable = getInjectable({ - id: "deployment-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "deploymentApi is only available in certain environments"); - - return new DeploymentApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default deploymentApiInjectable; diff --git a/src/common/k8s-api/endpoints/deployment.api.ts b/src/common/k8s-api/endpoints/deployment.api.ts deleted file mode 100644 index 31bc55ae76..0000000000 --- a/src/common/k8s-api/endpoints/deployment.api.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { PodSpec } from "./pod.api"; -import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { hasTypedProperty, isNumber, isObject } from "../../utils"; - -export class DeploymentApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Deployment, - }); - } - - protected getScaleApiUrl(params: { namespace: string; name: string }) { - return `${this.getUrl(params)}/scale`; - } - - async getReplicas(params: { namespace: string; name: string }): Promise { - const { status } = await this.request.get(this.getScaleApiUrl(params)); - - if (isObject(status) && hasTypedProperty(status, "replicas", isNumber)) { - return status.replicas; - } - - return 0; - } - - scale(params: { namespace: string; name: string }, replicas: number) { - return this.request.patch(this.getScaleApiUrl(params), { - data: { - spec: { - replicas, - }, - }, - }, - { - headers: { - "content-type": "application/merge-patch+json", - }, - }); - } - - restart(params: { namespace: string; name: string }) { - return this.request.patch(this.getUrl(params), { - data: { - spec: { - template: { - metadata: { - annotations: { "kubectl.kubernetes.io/restartedAt" : moment.utc().format() }, - }, - }, - }, - }, - }, - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } -} - -export interface DeploymentSpec { - replicas: number; - selector: LabelSelector; - template: { - metadata: { - creationTimestamp?: string; - labels: Partial>; - annotations?: Partial>; - }; - spec: PodSpec; - }; - strategy: { - type: string; - rollingUpdate: { - maxUnavailable: number; - maxSurge: number; - }; - }; -} - -export interface DeploymentStatus extends KubeObjectStatus { - observedGeneration: number; - replicas: number; - updatedReplicas: number; - readyReplicas: number; - availableReplicas?: number; - unavailableReplicas?: number; -} - -export class Deployment extends KubeObject< - NamespaceScopedMetadata, - DeploymentStatus, - DeploymentSpec -> { - static kind = "Deployment"; - static namespaced = true; - static apiBase = "/apis/apps/v1/deployments"; - - getSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.selector.matchLabels); - } - - getNodeSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.template.spec.nodeSelector); - } - - getTemplateLabels(): string[] { - return KubeObject.stringifyLabels(this.spec.template.metadata.labels); - } - - getTolerations() { - return this.spec.template.spec.tolerations ?? []; - } - - getAffinity() { - return this.spec.template.spec.affinity; - } - - getAffinityNumber() { - return Object.keys(this.getAffinity() ?? {}).length; - } - - getConditions(activeOnly = false) { - const { conditions = [] } = this.status ?? {}; - - if (activeOnly) { - return conditions.filter(c => c.status === "True"); - } - - return conditions; - } - - getConditionsText(activeOnly = true) { - return this.getConditions(activeOnly) - .map(({ type }) => type) - .join(" "); - } - - getReplicas() { - return this.spec.replicas || 0; - } -} diff --git a/src/common/k8s-api/endpoints/endpoint.api.injectable.ts b/src/common/k8s-api/endpoints/endpoint.api.injectable.ts deleted file mode 100644 index 6288dc2448..0000000000 --- a/src/common/k8s-api/endpoints/endpoint.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { EndpointsApi } from "./endpoint.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const endpointsApiInjectable = getInjectable({ - id: "endpoints-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "endpointsApi is only available in certain environments"); - - return new EndpointsApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default endpointsApiInjectable; diff --git a/src/common/k8s-api/endpoints/endpoint.api.ts b/src/common/k8s-api/endpoints/endpoint.api.ts deleted file mode 100644 index c60377a887..0000000000 --- a/src/common/k8s-api/endpoints/endpoint.api.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { autoBind } from "../../utils"; -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata, ObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; - -export function formatEndpointSubset(subset: EndpointSubset): string { - const { addresses, ports } = subset; - - if (!addresses || !ports) { - return ""; - } - - return addresses - .map(address => ( - ports - .map(port => `${address.ip}:${port.port}`) - .join(", ") - )) - .join(", "); - -} - -export interface ForZone { - name: string; -} - -export interface EndpointHints { - forZones?: ForZone[]; -} - -export interface EndpointConditions { - ready?: boolean; - serving?: boolean; - terminating?: boolean; -} - -export interface EndpointData { - addresses: string[]; - conditions?: EndpointConditions; - hints?: EndpointHints; - hostname?: string; - nodeName?: string; - targetRef?: ObjectReference; - zone?: string; -} - -export interface EndpointPort { - appProtocol?: string; - name?: string; - protocol?: string; - port: number; -} - -export interface EndpointAddress { - hostname?: string; - ip: string; - nodeName?: string; - targetRef?: ObjectReference; -} - -export interface EndpointSubset { - addresses?: EndpointAddress[]; - notReadyAddresses?: EndpointAddress[]; - ports?: EndpointPort[]; -} - -export interface EndpointsData extends KubeJsonApiData, void, void> { - subsets?: EndpointSubset[]; -} - -export class Endpoints extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static kind = "Endpoints"; - static namespaced = true; - static apiBase = "/api/v1/endpoints"; - - subsets?: EndpointSubset[]; - - constructor({ subsets, ...rest }: EndpointsData) { - super(rest); - autoBind(this); - this.subsets = subsets; - } - - getEndpointSubsets(): Required[] { - return this.subsets?.map(({ - addresses = [], - notReadyAddresses = [], - ports = [], - }) => ({ - addresses, - notReadyAddresses, - ports, - })) ?? []; - } - - toString(): string { - return this.getEndpointSubsets() - .map(formatEndpointSubset) - .join(", ") || ""; - } -} - -export class EndpointsApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: Endpoints, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/events.api.injectable.ts b/src/common/k8s-api/endpoints/events.api.injectable.ts deleted file mode 100644 index a64afb877e..0000000000 --- a/src/common/k8s-api/endpoints/events.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { KubeEventApi } from "./events.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const kubeEventApiInjectable = getInjectable({ - id: "kube-event-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "kubeEventApi is only available in certain environments"); - - return new KubeEventApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default kubeEventApiInjectable; diff --git a/src/common/k8s-api/endpoints/events.api.ts b/src/common/k8s-api/endpoints/events.api.ts deleted file mode 100644 index aca77712c8..0000000000 --- a/src/common/k8s-api/endpoints/events.api.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; -import type { KubeObjectMetadata, KubeObjectScope, ObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { formatDuration } from "../../utils/formatDuration"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; - -export interface EventSeries { - count?: number; - lastObservedTime?: string; -} - -export interface EventSource { - component?: string; - host?: string; -} - -export interface KubeEventData extends KubeJsonApiData, void, void> { - action?: string; - count?: number; - eventTime?: string; - firstTimestamp?: string; - involvedObject: Required; - lastTimestamp?: string; - message?: string; - reason?: string; - related?: ObjectReference; - reportingComponent?: string; - reportingInstance?: string; - series?: EventSeries; - source?: EventSource; - type?: string; -} - -export class KubeEvent extends KubeObject, void, void> { - static kind = "Event"; - static namespaced = true; - static apiBase = "/api/v1/events"; - - action?: string; - count?: number; - eventTime?: string; - firstTimestamp?: string; - involvedObject: Required; - lastTimestamp?: string; - message?: string; - reason?: string; - related?: ObjectReference; - reportingComponent?: string; - reportingInstance?: string; - series?: EventSeries; - source?: EventSource; - - /** - * Current supported values are: - * - "Normal" - * - "Warning" - */ - type?: string; - - constructor({ - action, - count, - eventTime, - firstTimestamp, - involvedObject, - lastTimestamp, - message, - reason, - related, - reportingComponent, - reportingInstance, - series, - source, - type, - ...rest - }: KubeEventData) { - super(rest); - this.action = action; - this.count = count; - this.eventTime = eventTime; - this.firstTimestamp = firstTimestamp; - this.involvedObject = involvedObject; - this.lastTimestamp = lastTimestamp; - this.message = message; - this.reason = reason; - this.related = related; - this.reportingComponent = reportingComponent; - this.reportingInstance = reportingInstance; - this.series = series; - this.source = source; - this.type = type; - } - - isWarning() { - return this.type === "Warning"; - } - - getSource() { - if (!this.source?.component) { - return ""; - } - - const { component, host = "" } = this.source; - - return `${component} ${host}`; - } - - /** - * @deprecated This function is not reactive to changing of time. If rendering use `` instead - */ - getFirstSeenTime() { - const diff = moment().diff(this.firstTimestamp); - - return formatDuration(diff, true); - } - - /** - * @deprecated This function is not reactive to changing of time. If rendering use `` instead - */ - getLastSeenTime() { - const diff = moment().diff(this.lastTimestamp); - - return formatDuration(diff, true); - } -} - -export class KubeEventApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: KubeEvent, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/helm-charts.api.ts b/src/common/k8s-api/endpoints/helm-charts.api.ts deleted file mode 100644 index 26da740830..0000000000 --- a/src/common/k8s-api/endpoints/helm-charts.api.ts +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { autoBind, bifurcateArray } from "../../utils"; -import Joi from "joi"; - -export interface RawHelmChart { - apiVersion: string; - name: string; - version: string; - repo: string; - created: string; - digest?: string; - kubeVersion?: string; - description?: string; - home?: string; - engine?: string; - icon?: string; - appVersion?: string; - type?: string; - tillerVersion?: string; - deprecated?: boolean; - keywords?: string[]; - sources?: string[]; - urls?: string[]; - maintainers?: HelmChartMaintainer[]; - dependencies?: RawHelmChartDependency[]; - annotations?: Record; -} - -const helmChartMaintainerValidator = Joi.object({ - name: Joi - .string() - .required(), - email: Joi - .string() - .required(), - url: Joi - .string() - .optional(), -}); - -const helmChartDependencyValidator = Joi.object({ - name: Joi - .string() - .required(), - repository: Joi - .string() - .required(), - condition: Joi - .string() - .optional(), - version: Joi - .string() - .required(), - tags: Joi - .array() - .items(Joi.string()) - .default(() => ([])), -}); - -const helmChartValidator = Joi.object({ - apiVersion: Joi - .string() - .required(), - name: Joi - .string() - .required(), - version: Joi - .string() - .required(), - repo: Joi - .string() - .required(), - created: Joi - .string() - .required(), - digest: Joi - .string() - .optional(), - kubeVersion: Joi - .string() - .optional(), - description: Joi - .string() - .default(""), - home: Joi - .string() - .optional(), - engine: Joi - .string() - .optional(), - icon: Joi - .string() - .optional(), - appVersion: Joi - .string() - .optional(), - tillerVersion: Joi - .string() - .optional(), - type: Joi - .string() - .optional(), - deprecated: Joi - .boolean() - .default(false), - keywords: Joi - .array() - .items(Joi.string()) - .options({ - stripUnknown: { - arrays: true, - }, - }) - .default(() => ([])), - sources: Joi - .array() - .items(Joi.string()) - .options({ - stripUnknown: { - arrays: true, - }, - }) - .default(() => ([])), - urls: Joi - .array() - .items(Joi.string()) - .options({ - stripUnknown: { - arrays: true, - }, - }) - .default(() => ([])), - maintainers: Joi - .array() - .items(helmChartMaintainerValidator) - .options({ - stripUnknown: { - arrays: true, - }, - }) - .default(() => ([])), - dependencies: Joi - .array() - .items(helmChartDependencyValidator) - .options({ - stripUnknown: { - arrays: true, - }, - }) - .default(() => ([])), - annotations: Joi - .object({}) - .pattern(/.*/, Joi.string()) - .default(() => ({})), -}); - -export interface HelmChartCreateOpts { - onError?: "throw" | "log"; -} - -export interface HelmChartMaintainer { - name: string; - email: string; - url?: string; -} - -export interface RawHelmChartDependency { - name: string; - repository: string; - condition?: string; - version: string; - tags?: string[]; -} - -export type HelmChartDependency = Required> - & Pick; - -export interface HelmChartData { - apiVersion: string; - name: string; - version: string; - repo: string; - created: string; - description: string; - keywords: string[]; - sources: string[]; - urls: string[]; - annotations: Record; - dependencies: HelmChartDependency[]; - maintainers: HelmChartMaintainer[]; - deprecated: boolean; - kubeVersion?: string; - digest?: string; - home?: string; - engine?: string; - icon?: string; - appVersion?: string; - type?: string; - tillerVersion?: string; -} - -export class HelmChart implements HelmChartData { - apiVersion: string; - name: string; - version: string; - repo: string; - created: string; - description: string; - keywords: string[]; - sources: string[]; - urls: string[]; - annotations: Record; - dependencies: HelmChartDependency[]; - maintainers: HelmChartMaintainer[]; - deprecated: boolean; - kubeVersion?: string; - digest?: string; - home?: string; - engine?: string; - icon?: string; - appVersion?: string; - type?: string; - tillerVersion?: string; - - private constructor(value: HelmChart | HelmChartData) { - this.apiVersion = value.apiVersion; - this.name = value.name; - this.version = value.version; - this.repo = value.repo; - this.kubeVersion = value.kubeVersion; - this.created = value.created; - this.description = value.description; - this.digest = value.digest; - this.keywords = value.keywords; - this.home = value.home; - this.sources = value.sources; - this.maintainers = value.maintainers; - this.engine = value.engine; - this.icon = value.icon; - this.apiVersion = value.apiVersion; - this.deprecated = value.deprecated; - this.tillerVersion = value.tillerVersion; - this.annotations = value.annotations; - this.urls = value.urls; - this.dependencies = value.dependencies; - this.type = value.type; - - autoBind(this); - } - - static create(data: RawHelmChart): HelmChart; - static create(data: RawHelmChart, opts?: HelmChartCreateOpts): HelmChart | undefined; - static create(data: RawHelmChart, { onError = "throw" }: HelmChartCreateOpts = {}): HelmChart | undefined { - const result = helmChartValidator.validate(data, { - abortEarly: false, - }); - - if (!result.error) { - return new HelmChart(result.value); - } - - const [actualErrors, unknownDetails] = bifurcateArray(result.error.details, ({ type }) => type === "object.unknown"); - - if (unknownDetails.length > 0) { - console.warn("HelmChart data has unexpected fields", { original: data, unknownFields: unknownDetails.flatMap(d => d.path) }); - } - - if (actualErrors.length === 0) { - return new HelmChart(result.value as unknown as HelmChartData); - } - - const validationError = new Joi.ValidationError(actualErrors.map(er => er.message).join(". "), actualErrors, result.error._original); - - if (onError === "throw") { - throw validationError; - } - - console.warn("[HELM-CHART]: failed to validate data", data, validationError); - - return undefined; - } - - getId(): string { - const digestPart = this.digest - ? `+${this.digest}` - : ""; - - return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}${digestPart}`; - } - - getName(): string { - return this.name; - } - - getFullName(seperator = "/"): string { - return [this.getRepository(), this.getName()].join(seperator); - } - - getDescription(): string { - return this.description; - } - - getIcon(): string | undefined { - return this.icon; - } - - getHome(): string | undefined { - return this.home; - } - - getMaintainers(): HelmChartMaintainer[] { - return this.maintainers; - } - - getVersion(): string { - return this.version; - } - - getRepository(): string { - return this.repo; - } - - getAppVersion(): string | undefined { - return this.appVersion; - } - - getKeywords(): string[] { - return this.keywords; - } -} diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts deleted file mode 100644 index 4d9bfc55b1..0000000000 --- a/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 { RawHelmChart } from "../helm-charts.api"; -import { HelmChart } from "../helm-charts.api"; -import { isDefined } from "../../../utils"; -import apiBaseInjectable from "../../api-base.injectable"; - -export type RequestHelmCharts = () => Promise; -export type RepoHelmChartList = Record; - -/** - * Get a list of all helm charts from all saved helm repos - */ -const requestHelmChartsInjectable = getInjectable({ - id: "request-helm-charts", - instantiate: (di) => { - const apiBase = di.inject(apiBaseInjectable); - - return async () => { - const data = await apiBase.get>("/v2/charts"); - - return Object - .values(data) - .reduce((allCharts, repoCharts) => allCharts.concat(Object.values(repoCharts)), new Array()) - .map(([chart]) => HelmChart.create(chart, { onError: "log" })) - .filter(isDefined); - }; - }, -}); - -export default requestHelmChartsInjectable; diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts deleted file mode 100644 index fb8eaafa10..0000000000 --- a/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 { AsyncResult } from "../../../utils/async-result"; -import { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -const requestReadmeEndpoint = urlBuilderFor("/v2/charts/:repo/:name/readme"); - -export type RequestHelmChartReadme = (repo: string, name: string, version?: string) => Promise>; - -const requestHelmChartReadmeInjectable = getInjectable({ - id: "request-helm-chart-readme", - instantiate: (di): RequestHelmChartReadme => { - const apiBase = di.inject(apiBaseInjectable); - - return (repo, name, version) => ( - apiBase.get(requestReadmeEndpoint.compile({ name, repo }, { version })) - ); - }, -}); - -export default requestHelmChartReadmeInjectable; diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts deleted file mode 100644 index 71105c9ff9..0000000000 --- a/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 { AsyncResult } from "../../../utils/async-result"; -import { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -const requestValuesEndpoint = urlBuilderFor("/v2/charts/:repo/:name/values"); - -export type RequestHelmChartValues = (repo: string, name: string, version: string) => Promise>; - -const requestHelmChartValuesInjectable = getInjectable({ - id: "request-helm-chart-values", - instantiate: (di): RequestHelmChartValues => { - const apiBase = di.inject(apiBaseInjectable); - - return (repo, name, version) => ( - apiBase.get(requestValuesEndpoint.compile({ repo, name }, { version })) - ); - }, -}); - -export default requestHelmChartValuesInjectable; diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts deleted file mode 100644 index ab85594ec6..0000000000 --- a/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import { HelmChart } from "../helm-charts.api"; -import type { RawHelmChart } from "../helm-charts.api"; -import { isDefined } from "../../../utils"; -import apiBaseInjectable from "../../api-base.injectable"; - -const requestVersionsEndpoint = urlBuilderFor("/v2/charts/:repo/:name/versions"); - -export type RequestHelmChartVersions = (repo: string, chartName: string) => Promise; - -const requestHelmChartVersionsInjectable = getInjectable({ - id: "request-helm-chart-versions", - instantiate: (di): RequestHelmChartVersions => { - const apiBase = di.inject(apiBaseInjectable); - - return async (repo, name) => { - const rawVersions = await apiBase.get(requestVersionsEndpoint.compile({ name, repo })) as RawHelmChart[]; - - return rawVersions - .map(version => HelmChart.create(version, { onError: "log" })) - .filter(isDefined); - }; - }, -}); - -export default requestHelmChartVersionsInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api.ts b/src/common/k8s-api/endpoints/helm-releases.api.ts deleted file mode 100644 index 3460378e59..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ItemObject } from "../../item.store"; -import type { HelmReleaseDetails } from "./helm-releases.api/request-details.injectable"; - -export interface HelmReleaseUpdateDetails { - log: string; - release: HelmReleaseDetails; -} - -export interface HelmReleaseDto { - appVersion: string; - name: string; - namespace: string; - chart: string; - status: string; - updated: string; - revision: string; -} - -export interface HelmRelease extends HelmReleaseDto, ItemObject { - getNs: () => string; - getChart: (withVersion?: boolean) => string; - getRevision: () => number; - getStatus: () => string; - getVersion: () => string; - getUpdated: (humanize?: boolean, compact?: boolean) => string | number; - getRepo: () => Promise; -} diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.global-override-for-injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.global-override-for-injectable.ts deleted file mode 100644 index 18b3bd0ec0..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../../../test-utils/get-global-override"; -import requestHelmReleaseConfigurationInjectable from "./request-configuration.injectable"; - -export default getGlobalOverride(requestHelmReleaseConfigurationInjectable, () => () => { - throw new Error("Tried to call requestHelmReleaseConfiguration with no override"); -}); diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts deleted file mode 100644 index e1581c5d76..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export type RequestHelmReleaseConfiguration = ( - name: string, - namespace: string, - all: boolean -) => Promise; - -const requestConfigurationEnpoint = urlBuilderFor("/v2/releases/:namespace/:name/values"); - -const requestHelmReleaseConfigurationInjectable = getInjectable({ - id: "request-helm-release-configuration", - - instantiate: (di): RequestHelmReleaseConfiguration => { - const apiBase = di.inject(apiBaseInjectable); - - return (name, namespace, all: boolean) => ( - apiBase.get(requestConfigurationEnpoint.compile({ name, namespace }, { all })) - ); - }, -}); - -export default requestHelmReleaseConfigurationInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts deleted file mode 100644 index c1cd09d40f..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import yaml from "js-yaml"; -import { getInjectable } from "@ogre-tools/injectable"; -import type { HelmReleaseUpdateDetails } from "../helm-releases.api"; -import { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -interface HelmReleaseCreatePayload { - name?: string; - repo: string; - chart: string; - namespace: string; - version: string; - values: string; -} - -export type RequestCreateHelmRelease = (payload: HelmReleaseCreatePayload) => Promise; - -const requestCreateEndpoint = urlBuilderFor("/v2/releases"); - -const requestCreateHelmReleaseInjectable = getInjectable({ - id: "request-create-helm-release", - - instantiate: (di): RequestCreateHelmRelease => { - const apiBase = di.inject(apiBaseInjectable); - - return ({ repo, chart, values, ...data }) => { - return apiBase.post(requestCreateEndpoint.compile({}), { - data: { - chart: `${repo}/${chart}`, - values: yaml.load(values), - ...data, - }, - }); - }; - }, -}); - -export default requestCreateHelmReleaseInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts deleted file mode 100644 index 44af4311a9..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export type RequestDeleteHelmRelease = (name: string, namespace: string) => Promise; - -const requestDeleteEndpoint = urlBuilderFor("/v2/releases/:namespace/:name"); - -const requestDeleteHelmReleaseInjectable = getInjectable({ - id: "request-delete-helm-release", - instantiate: (di): RequestDeleteHelmRelease => { - const apiBase = di.inject(apiBaseInjectable); - - return (name, namespace) => apiBase.del(requestDeleteEndpoint.compile({ name, namespace })); - }, -}); - -export default requestDeleteHelmReleaseInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts deleted file mode 100644 index 37f2287377..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 { KubeJsonApiData } from "../../kube-json-api"; -import { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export interface HelmReleaseDetails { - resources: KubeJsonApiData[]; - name: string; - namespace: string; - version: string; - config: string; // release values - manifest: string; - info: { - deleted: string; - description: string; - first_deployed: string; - last_deployed: string; - notes: string; - status: string; - }; -} - -export type CallForHelmReleaseDetails = (name: string, namespace: string) => Promise; - -const requestDetailsEnpoint = urlBuilderFor("/v2/releases/:namespace/:name"); - -const requestHelmReleaseDetailsInjectable = getInjectable({ - id: "call-for-helm-release-details", - - instantiate: (di): CallForHelmReleaseDetails => { - const apiBase = di.inject(apiBaseInjectable); - - return (name, namespace) => apiBase.get(requestDetailsEnpoint.compile({ name, namespace })); - }, -}); - -export default requestHelmReleaseDetailsInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts deleted file mode 100644 index 58b6a37dbb..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export interface HelmReleaseRevision { - revision: number; - updated: string; - status: string; - chart: string; - app_version: string; - description: string; -} - -export type RequestHelmReleaseHistory = (name: string, namespace: string) => Promise; - -const requestHistoryEnpoint = urlBuilderFor("/v2/releases/:namespace/:name/history"); - -const requestHelmReleaseHistoryInjectable = getInjectable({ - id: "request-helm-release-history", - instantiate: (di): RequestHelmReleaseHistory => { - const apiBase = di.inject(apiBaseInjectable); - - return (name, namespace) => apiBase.get(requestHistoryEnpoint.compile({ name, namespace })); - }, -}); - -export default requestHelmReleaseHistoryInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts deleted file mode 100644 index ee6503ca99..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; -import type { HelmReleaseDto } from "../helm-releases.api"; - -export type RequestHelmReleases = (namespace?: string) => Promise; - -const requestHelmReleasesEndpoint = urlBuilderFor("/v2/releases/:namespace?"); - -const requestHelmReleasesInjectable = getInjectable({ - id: "request-helm-releases", - - instantiate: (di): RequestHelmReleases => { - const apiBase = di.inject(apiBaseInjectable); - - return (namespace) => apiBase.get(requestHelmReleasesEndpoint.compile({ namespace })); - }, -}); - -export default requestHelmReleasesInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts deleted file mode 100644 index 036b399ef2..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export type RequestHelmReleaseRollback = (name: string, namespace: string, revision: number) => Promise; - -const requestRollbackEndpoint = urlBuilderFor("/v2/releases/:namespace/:name"); - -const requestHelmReleaseRollbackInjectable = getInjectable({ - id: "request-helm-release-rollback", - instantiate: (di): RequestHelmReleaseRollback => { - const apiBase = di.inject(apiBaseInjectable); - - return async (name, namespace, revision) => { - await apiBase.put( - requestRollbackEndpoint.compile({ name, namespace }), - { data: { revision }}, - ); - }; - }, -}); - -export default requestHelmReleaseRollbackInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts deleted file mode 100644 index 715a21cea9..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import type { AsyncResult } from "../../../utils/async-result"; -import apiBaseInjectable from "../../api-base.injectable"; - -interface HelmReleaseUpdatePayload { - repo: string; - chart: string; - version: string; - values: string; -} - -export type RequestHelmReleaseUpdate = ( - name: string, - namespace: string, - payload: HelmReleaseUpdatePayload -) => Promise>; - -const requestUpdateEndpoint = urlBuilderFor("/v2/releases/:namespace/:name"); - -const requestHelmReleaseUpdateInjectable = getInjectable({ - id: "request-helm-release-update", - - instantiate: (di): RequestHelmReleaseUpdate => { - const apiBase = di.inject(apiBaseInjectable); - - return async (name, namespace, { repo, chart, values, version }) => { - try { - await apiBase.put(requestUpdateEndpoint.compile({ name, namespace }), { - data: { - chart: `${repo}/${chart}`, - values, - version, - }, - }); - } catch (e) { - return { callWasSuccessful: false, error: e }; - } - - return { callWasSuccessful: true }; - }; - }, -}); - -export default requestHelmReleaseUpdateInjectable; diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts deleted file mode 100644 index 99f1cc17d0..0000000000 --- a/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { urlBuilderFor } from "../../../utils/buildUrl"; -import apiBaseInjectable from "../../api-base.injectable"; - -export type RequestHelmReleaseValues = (name: string, namespace: string, all?: boolean) => Promise; - -const requestValuesEndpoint = urlBuilderFor("/v2/release/:namespace/:name/values"); - -const requestHelmReleaseValuesInjectable = getInjectable({ - id: "request-helm-release-values", - instantiate: (di): RequestHelmReleaseValues => { - const apiBase = di.inject(apiBaseInjectable); - - return (name, namespace, all) => apiBase.get(requestValuesEndpoint.compile({ name, namespace }, { all })); - }, -}); - -export default requestHelmReleaseValuesInjectable; diff --git a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts deleted file mode 100644 index d891e45e0f..0000000000 --- a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { HorizontalPodAutoscalerApi } from "./horizontal-pod-autoscaler.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const horizontalPodAutoscalerApiInjectable = getInjectable({ - id: "horizontal-pod-autoscaler-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "horizontalPodAutoscalerApi is only available in certain environments"); - - return new HorizontalPodAutoscalerApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default horizontalPodAutoscalerApiInjectable; diff --git a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts b/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts deleted file mode 100644 index ba5483da2c..0000000000 --- a/src/common/k8s-api/endpoints/horizontal-pod-autoscaler.api.ts +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { OptionVarient } from "../../utils"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { BaseKubeObjectCondition, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; - -export enum HpaMetricType { - Resource = "Resource", - Pods = "Pods", - Object = "Object", - External = "External", - ContainerResource = "ContainerResource", -} - -export interface MetricCurrentTarget { - current?: string; - target?: string; -} - -export interface HorizontalPodAutoscalerMetricTarget { - kind: string; - name: string; - apiVersion: string; -} - -export interface V2ContainerResourceMetricSource { - container: string; - name: string; - target?: { - averageUtilization?: number; - averageValue?: string; - type?: string; - }; -} - -export interface V2Beta1ContainerResourceMetricSource { - container: string; - name: string; - targetAverageUtilization?: number; - targetAverageValue?: string; -} - -export type ContainerResourceMetricSource = - | V2ContainerResourceMetricSource - | V2Beta1ContainerResourceMetricSource; - -export interface V2ExternalMetricSource { - metricName?: string; - metricSelector?: LabelSelector; - metric?: { - name?: string; - selector?: LabelSelector; - }; - target?: { - type: string; - value?: string; - averageValue?: string; - }; -} - -export interface V2Beta1ExternalMetricSource { - metricName?: string; - metricSelector?: LabelSelector; - targetAverageValue?: string; - targetValue?: string; - metric?: { - selector?: LabelSelector; - }; -} - -export type ExternalMetricSource = - | V2Beta1ExternalMetricSource - | V2ExternalMetricSource; - -export interface V2ObjectMetricSource { - metric?: { - name?: string; - selector?: LabelSelector; - }; - target?: { - type?: string; - value?: string; - averageValue?: string; - }; - describedObject?: CrossVersionObjectReference; -} - -export interface V2Beta1ObjectMetricSource { - averageValue?: string; - metricName?: string; - selector?: LabelSelector; - targetValue?: string; - describedObject?: CrossVersionObjectReference; -} - -export type ObjectMetricSource = - | V2ObjectMetricSource - | V2Beta1ObjectMetricSource; - -export interface V2PodsMetricSource { - metric?: { - name?: string; - selector?: LabelSelector; - }; - target?: { - averageValue?: string; - type?: string; - }; -} - -export interface V2Beta1PodsMetricSource { - metricName?: string; - selector?: LabelSelector; - targetAverageValue?: string; -} - -export type PodsMetricSource = - | V2PodsMetricSource - | V2Beta1PodsMetricSource; - -export interface V2ResourceMetricSource { - name: string; - target?: { - averageUtilization?: number; - averageValue?: string; - type?: string; - }; -} - -export interface V2Beta1ResourceMetricSource { - name: string; - targetAverageUtilization?: number; - targetAverageValue?: string; -} - -export type ResourceMetricSource = - | V2ResourceMetricSource - | V2Beta1ResourceMetricSource; - -export interface BaseHorizontalPodAutoscalerMetricSpec { - containerResource: ContainerResourceMetricSource; - external: ExternalMetricSource; - object: ObjectMetricSource; - pods: PodsMetricSource; - resource: ResourceMetricSource; -} - -export type HorizontalPodAutoscalerMetricSpec = - | OptionVarient - | OptionVarient - | OptionVarient - | OptionVarient - | OptionVarient; - -interface HorizontalPodAutoscalerBehavior { - scaleUp?: HPAScalingRules; - scaleDown?: HPAScalingRules; -} - -interface HPAScalingRules { - stabilizationWindowSecond?: number; - selectPolicy?: ScalingPolicySelect; - policies?: HPAScalingPolicy[]; -} - -type ScalingPolicySelect = string; - -interface HPAScalingPolicy { - type: HPAScalingPolicyType; - value: number; - periodSeconds: number; -} - -type HPAScalingPolicyType = string; - -export interface V2ContainerResourceMetricStatus { - container?: string; - name: string; - current?: { - averageUtilization?: number; - averageValue?: string; - }; -} - -export interface V2Beta1ContainerResourceMetricStatus { - container?: string; - currentAverageUtilization?: number; - currentAverageValue?: string; - name: string; -} - -export type ContainerResourceMetricStatus = - | V2ContainerResourceMetricStatus - | V2Beta1ContainerResourceMetricStatus; - -export interface V2ExternalMetricStatus { - metric?: { - name?: string; - selector?: LabelSelector; - }; - current?: { - averageValue?: string; - value?: string; - }; -} - -export interface V2Beta1ExternalMetricStatus { - currentAverageValue?: string; - currentValue?: string; - metricName?: string; - metricSelector?: LabelSelector; -} - -export type ExternalMetricStatus = - | V2Beta1ExternalMetricStatus - | V2ExternalMetricStatus; - -export interface V2ObjectMetricStatus { - metric?: { - name?: string; - selector?: LabelSelector; - }; - current?: { - type?: string; - value?: string; - averageValue?: string; - }; - describedObject?: CrossVersionObjectReference; -} - -export interface V2Beta1ObjectMetricStatus { - averageValue?: string; - currentValue?: string; - metricName?: string; - selector?: LabelSelector; - describedObject?: CrossVersionObjectReference; -} - -export type ObjectMetricStatus = - | V2Beta1ObjectMetricStatus - | V2ObjectMetricStatus; - -export interface V2PodsMetricStatus { - selector?: LabelSelector; - metric?: { - name?: string; - selector?: LabelSelector; - }; - current?: { - averageValue?: string; - }; -} - -export interface V2Beta1PodsMetricStatus { - currentAverageValue?: string; - metricName?: string; - selector?: LabelSelector; -} - -export type PodsMetricStatus = - | V2Beta1PodsMetricStatus - | V2PodsMetricStatus; - -export interface V2ResourceMetricStatus { - name: string; - current?: { - averageUtilization?: number; - averageValue?: string; - }; -} - -export interface V2Beta1ResourceMetricStatus { - currentAverageUtilization?: number; - currentAverageValue?: string; - name: string; -} - -export type ResourceMetricStatus = - | V2Beta1ResourceMetricStatus - | V2ResourceMetricStatus; - -export interface BaseHorizontalPodAutoscalerMetricStatus { - containerResource: ContainerResourceMetricStatus; - external: ExternalMetricStatus; - object: ObjectMetricStatus; - pods: PodsMetricStatus; - resource: ResourceMetricStatus; -} - -export type HorizontalPodAutoscalerMetricStatus = - | OptionVarient - | OptionVarient - | OptionVarient - | OptionVarient - | OptionVarient; - -export interface CrossVersionObjectReference { - kind: string; - name: string; - apiVersion: string; -} - -export interface HorizontalPodAutoscalerSpec { - scaleTargetRef: CrossVersionObjectReference; - minReplicas?: number; - maxReplicas: number; - metrics?: HorizontalPodAutoscalerMetricSpec[]; - behavior?: HorizontalPodAutoscalerBehavior; - targetCPUUtilizationPercentage?: number; // used only in autoscaling/v1 -} - -export interface HorizontalPodAutoscalerStatus { - conditions?: BaseKubeObjectCondition[]; - currentReplicas: number; - desiredReplicas: number; - currentMetrics?: HorizontalPodAutoscalerMetricStatus[]; - currentCPUUtilizationPercentage?: number; // used only in autoscaling/v1 -} - -export class HorizontalPodAutoscaler extends KubeObject< - NamespaceScopedMetadata, - HorizontalPodAutoscalerStatus, - HorizontalPodAutoscalerSpec -> { - static readonly kind = "HorizontalPodAutoscaler"; - static readonly namespaced = true; - static readonly apiBase = "/apis/autoscaling/v2/horizontalpodautoscalers"; - - getMaxPods() { - return this.spec.maxReplicas ?? 0; - } - - getMinPods() { - return this.spec.minReplicas ?? 0; - } - - getReplicas() { - return this.status?.currentReplicas ?? 0; - } - - getReadyConditions() { - return this.getConditions().filter(({ isReady }) => isReady); - } - - getConditions() { - return this.status?.conditions?.map(condition => { - const { message, reason, lastTransitionTime, status } = condition; - - return { - ...condition, - isReady: status === "True", - tooltip: `${message || reason} (${lastTransitionTime})`, - }; - }) ?? []; - } - - getMetrics() { - return this.spec.metrics ?? []; - } - - getCurrentMetrics() { - return this.status?.currentMetrics ?? []; - } -} - -export class HorizontalPodAutoscalerApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: HorizontalPodAutoscaler, - checkPreferredVersion: true, - }); - } -} diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts deleted file mode 100644 index 9d3fa94815..0000000000 --- a/src/common/k8s-api/endpoints/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Kubernetes apis -// Docs: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/ - -export * from "./cluster.api"; -export * from "./cluster-role.api"; -export * from "./cluster-role-binding.api"; -export * from "./config-map.api"; -export * from "./custom-resource-definition.api"; -export * from "./cron-job.api"; -export * from "./daemon-set.api"; -export * from "./deployment.api"; -export * from "./endpoint.api"; -export * from "./events.api"; -export * from "./horizontal-pod-autoscaler.api"; -export * from "./ingress.api"; -export * from "./ingress-class.api"; -export * from "./job.api"; -export * from "./lease.api"; -export * from "./limit-range.api"; -export * from "./namespace.api"; -export * from "./network-policy.api"; -export * from "./node.api"; -export * from "./persistent-volume.api"; -export * from "./persistent-volume-claim.api"; -export * from "./pod.api"; -export * from "./pod-disruption-budget.api"; -export * from "./pod-metrics.api"; -export * from "./pod-security-policy.api"; -export * from "./priority-class.api"; -export * from "./replica-set.api"; -export * from "./resource-quota.api"; -export * from "./role.api"; -export * from "./role-binding.api"; -export * from "./runtime-class.api"; -export * from "./secret.api"; -export * from "./self-subject-rules-reviews.api"; -export * from "./service.api"; -export * from "./service-account.api"; -export * from "./stateful-set.api"; -export * from "./storage-class.api"; -export * from "./types"; diff --git a/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts b/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts deleted file mode 100644 index d7d9bf32b9..0000000000 --- a/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { IngressClassApi } from "./ingress-class.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const ingressClassApiInjectable = getInjectable({ - id: "ingress-class-api", - instantiate: (di) => new IngressClassApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }), - - injectionToken: kubeApiInjectionToken, -}); - -export default ingressClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/ingress-class.api.ts b/src/common/k8s-api/endpoints/ingress-class.api.ts deleted file mode 100644 index 5950192179..0000000000 --- a/src/common/k8s-api/endpoints/ingress-class.api.ts +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { KubeApiDependencies, ResourceDescriptor } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export class IngressClassApi extends KubeApi { - constructor(dependencies: KubeApiDependencies) { - super(dependencies, { - objectConstructor: IngressClass, - checkPreferredVersion: true, - fallbackApiBases: ["/apis/extensions/v1beta1/ingressclasses"], - }); - } - - setAsDefault({ name }: ResourceDescriptor, isDefault = true) { - const reqUrl = this.formatUrlForNotListing({ name }); - - return this.request.patch(reqUrl, { - data: { - metadata: { - annotations: { - [IngressClass.ANNOTATION_IS_DEFAULT]: JSON.stringify(isDefault), - }, - }, - }, - }, { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } -} - -// API docs: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#ingressclass-v1-networking-k8s-io -export type IngressClassMetadata = KubeObjectMetadata & { - "name": string; - "labels"?: { - [name: string]: string | undefined; - "app.kubernetes.io/component"?: "controller"; - }; - "annotations"?: { - [name: string]: string | undefined; - "ingressclass.kubernetes.io/is-default-class"?: "true"; - }; -}; - -export interface IngressClassParametersReference { - "apiGroup": string; // k8s.example.net - "scope": "Namespace" | "Cluster"; - "kind": "ClusterIngressParameter" | "IngressParameter"; - "name": string; // external-config-1 - "namespace"?: string; // namespaced for IngressClass must be defined in `spec.parameters.namespace` instead of `metadata.namespace` (!) -} - -export interface IngressClassSpec { - controller: string; // "example.com/ingress-controller" - parameters?: IngressClassParametersReference; -} - -export interface IngressClassStatus { -} - -export class IngressClass extends KubeObject { - static readonly kind = "IngressClass"; - static readonly namespaced = false; - static readonly apiBase = "/apis/networking.k8s.io/v1/ingressclasses"; - static readonly ANNOTATION_IS_DEFAULT = "ingressclass.kubernetes.io/is-default-class"; - - getController(): string { - return this.spec.controller; - } - - getCtrlApiGroup() { - return this.spec?.parameters?.apiGroup; - } - - getCtrlScope() { - return this.spec?.parameters?.scope; - } - - getCtrlNs() { - return this.spec?.parameters?.namespace; - } - - getCtrlKind() { - return this.spec?.parameters?.kind; - } - - getCtrlName() { - return this.spec?.parameters?.name as string; - } - - get isDefault() { - return this.metadata.annotations?.[IngressClass.ANNOTATION_IS_DEFAULT] === "true"; - } -} diff --git a/src/common/k8s-api/endpoints/ingress.api.injectable.ts b/src/common/k8s-api/endpoints/ingress.api.injectable.ts deleted file mode 100644 index ec25b199b0..0000000000 --- a/src/common/k8s-api/endpoints/ingress.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { IngressApi } from "./ingress.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const ingressApiInjectable = getInjectable({ - id: "ingress-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "ingressApi is only available in certain environments"); - - return new IngressApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default ingressApiInjectable; diff --git a/src/common/k8s-api/endpoints/ingress.api.ts b/src/common/k8s-api/endpoints/ingress.api.ts deleted file mode 100644 index d35cd77c0b..0000000000 --- a/src/common/k8s-api/endpoints/ingress.api.ts +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { NamespaceScopedMetadata, TypedLocalObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { hasTypedProperty, isString, iter } from "../../utils"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { RequireExactlyOne } from "type-fest"; - -export class IngressApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Ingress, - // Add fallback for Kubernetes <1.19 - checkPreferredVersion: true, - fallbackApiBases: ["/apis/extensions/v1beta1/ingresses"], - }); - } -} - -export interface ILoadBalancerIngress { - hostname?: string; - ip?: string; -} - -// extensions/v1beta1 -export interface ExtensionsBackend { - serviceName?: string; - servicePort?: number | string; -} - -// networking.k8s.io/v1 -export interface NetworkingBackend { - service?: IngressService; -} - -export type IngressBackend = (ExtensionsBackend | NetworkingBackend) & { - resource?: TypedLocalObjectReference; -}; - -export interface IngressService { - name: string; - port: RequireExactlyOne<{ - name: string; - number: number; - }>; -} - -function isExtensionsBackend(backend: IngressBackend): backend is ExtensionsBackend { - return hasTypedProperty(backend, "serviceName", isString); -} - -/** - * Format an ingress backend into the name of the service and port - * @param backend The ingress target - */ -export function getBackendServiceNamePort(backend: IngressBackend | undefined): string { - if (!backend) { - return ""; - } - - if (isExtensionsBackend(backend)) { - return `${backend.serviceName}:${backend.servicePort}`; - } - - if (backend.service) { - const { name, port } = backend.service; - - return `${name}:${port.number ?? port.name}`; - } - - return ""; -} - -export interface HTTPIngressPath { - pathType: "Exact" | "Prefix" | "ImplementationSpecific"; - path?: string; - backend?: IngressBackend; -} - -export interface HTTPIngressRuleValue { - paths: HTTPIngressPath[]; -} - -export interface IngressRule { - host?: string; - http?: HTTPIngressRuleValue; -} - -export interface IngressSpec { - tls: { - secretName: string; - }[]; - rules?: IngressRule[]; - // extensions/v1beta1 - backend?: ExtensionsBackend; - /** - * The default backend which is exactly on of: - * - service - * - resource - */ - defaultBackend?: RequireExactlyOne; -} - -export interface IngressStatus { - loadBalancer: { - ingress?: ILoadBalancerIngress[]; - }; -} - -export class Ingress extends KubeObject< - NamespaceScopedMetadata, - IngressStatus, - IngressSpec -> { - static readonly kind = "Ingress"; - static readonly namespaced = true; - static readonly apiBase = "/apis/networking.k8s.io/v1/ingresses"; - - getRules() { - return this.spec.rules ?? []; - } - - getRoutes(): string[] { - return computeRouteDeclarations(this).map(({ url, service }) => `${url} ⇢ ${service}`); - } - - getServiceNamePort(): ExtensionsBackend | undefined { - const { spec: { backend, defaultBackend } = {}} = this; - - const serviceName = defaultBackend?.service?.name ?? backend?.serviceName; - const servicePort = defaultBackend?.service?.port.number ?? defaultBackend?.service?.port.name ?? backend?.servicePort; - - if (!serviceName || !servicePort) { - return undefined; - } - - return { - serviceName, - servicePort, - }; - } - - getHosts() { - const { spec: { rules = [] }} = this; - - return [...iter.filterMap(rules, rule => rule.host)]; - } - - getPorts() { - const ports: number[] = []; - const { spec: { tls, rules = [], backend, defaultBackend }} = this; - const httpPort = 80; - const tlsPort = 443; - // Note: not using the port name (string) - const servicePort = defaultBackend?.service?.port.number ?? backend?.servicePort; - - if (rules.length > 0) { - if (rules.some(rule => rule.http)) { - ports.push(httpPort); - } - } else if (servicePort !== undefined) { - ports.push(Number(servicePort)); - } - - if (tls && tls.length > 0) { - ports.push(tlsPort); - } - - return ports.join(", "); - } - - getLoadBalancers() { - return this.status?.loadBalancer?.ingress?.map(address => ( - address.hostname || address.ip - )) ?? []; - } -} - -export interface ComputedIngressRoute { - displayAsLink: boolean; - pathname: string; - url: string; - service: string; -} - -export function computeRuleDeclarations(ingress: Ingress, rule: IngressRule): ComputedIngressRoute[] { - const { host = "*", http: { paths } = { paths: [] }} = rule; - const protocol = (ingress.spec?.tls?.length ?? 0) === 0 - ? "http" - : "https"; - - return paths.map(({ path = "/", backend }) => ({ - displayAsLink: !host.includes("*"), - pathname: path, - url: `${protocol}://${host}${path}`, - service: getBackendServiceNamePort(backend), - })); -} - -export function computeRouteDeclarations(ingress: Ingress): ComputedIngressRoute[] { - return ingress.getRules().flatMap(rule => computeRuleDeclarations(ingress, rule)); -} diff --git a/src/common/k8s-api/endpoints/job.api.injectable.ts b/src/common/k8s-api/endpoints/job.api.injectable.ts deleted file mode 100644 index 923b96a2eb..0000000000 --- a/src/common/k8s-api/endpoints/job.api.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { JobApi } from "./job.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const jobApiInjectable = getInjectable({ - id: "job-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "jobApi is only available in certain environments"); - - return new JobApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }, { - checkPreferredVersion: true, - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default jobApiInjectable; diff --git a/src/common/k8s-api/endpoints/job.api.ts b/src/common/k8s-api/endpoints/job.api.ts deleted file mode 100644 index ef47419e80..0000000000 --- a/src/common/k8s-api/endpoints/job.api.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { PodSpec } from "./pod.api"; -import type { Container } from "./types/container"; -import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; - -export interface JobSpec { - parallelism?: number; - completions?: number; - backoffLimit?: number; - selector?: LabelSelector; - template: { - metadata: { - creationTimestamp?: string; - labels?: Partial>; - annotations?: Partial>; - }; - spec: PodSpec; - }; - containers?: Container[]; - restartPolicy?: string; - terminationGracePeriodSeconds?: number; - dnsPolicy?: string; - serviceAccountName?: string; - serviceAccount?: string; - schedulerName?: string; -} - -export interface JobStatus extends KubeObjectStatus { - startTime: string; - completionTime: string; - succeeded: number; -} - -export class Job extends KubeObject< - NamespaceScopedMetadata, - JobStatus, - JobSpec -> { - static readonly kind = "Job"; - static readonly namespaced = true; - static readonly apiBase = "/apis/batch/v1/jobs"; - - getSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.selector?.matchLabels); - } - - getNodeSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.template.spec.nodeSelector); - } - - getTemplateLabels(): string[] { - return KubeObject.stringifyLabels(this.spec.template.metadata.labels); - } - - getTolerations() { - return this.spec.template.spec.tolerations ?? []; - } - - getAffinity() { - return this.spec.template.spec.affinity; - } - - getAffinityNumber() { - return Object.keys(this.getAffinity() ?? {}).length; - } - - getDesiredCompletions() { - return this.spec.completions ?? 0; - } - - getCompletions() { - return this.status?.succeeded ?? 0; - } - - getParallelism() { - return this.spec.parallelism; - } - - getCondition() { - // Type of Job condition could be only Complete or Failed - // https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#jobcondition-v1-batch - return this.status?.conditions?.find(({ status }) => status === "True"); - } - - getImages() { - return this.spec.template.spec.containers?.map(container => container.image) ?? []; - } -} - -export class JobApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Job, - }); - } -} diff --git a/src/common/k8s-api/endpoints/lease.api.injectable.ts b/src/common/k8s-api/endpoints/lease.api.injectable.ts deleted file mode 100644 index f5d8e73b55..0000000000 --- a/src/common/k8s-api/endpoints/lease.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { LeaseApi } from "./lease.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const leaseApiInjectable = getInjectable({ - id: "lease-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "leaseApi is only available in certain environments"); - - return new LeaseApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default leaseApiInjectable; diff --git a/src/common/k8s-api/endpoints/lease.api.ts b/src/common/k8s-api/endpoints/lease.api.ts deleted file mode 100644 index 25f636dcfb..0000000000 --- a/src/common/k8s-api/endpoints/lease.api.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; - -export interface LeaseSpec { - acquireTime?: string; - holderIdentity: string; - leaseDurationSeconds: number; - leaseTransitions?: number; - renewTime: string; -} - -export class Lease extends KubeObject< - NamespaceScopedMetadata, - void, - LeaseSpec -> { - static readonly kind = "Lease"; - static readonly namespaced = true; - static readonly apiBase = "/apis/coordination.k8s.io/v1/leases"; - - getAcquireTime(): string { - return this.spec.acquireTime || ""; - } - - getHolderIdentity(): string { - return this.spec.holderIdentity; - } - - getLeaseDurationSeconds(): number { - return this.spec.leaseDurationSeconds; - } - - getLeaseTransitions(): number | undefined { - return this.spec.leaseTransitions; - } - - getRenewTime(): string { - return this.spec.renewTime; - } -} - -export class LeaseApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Lease, - }); - } -} diff --git a/src/common/k8s-api/endpoints/limit-range.api.injectable.ts b/src/common/k8s-api/endpoints/limit-range.api.injectable.ts deleted file mode 100644 index 7f50e2df92..0000000000 --- a/src/common/k8s-api/endpoints/limit-range.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { LimitRangeApi } from "./limit-range.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const limitRangeApiInjectable = getInjectable({ - id: "limit-range-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "limitRangeApi is only available in certain environments"); - - return new LimitRangeApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default limitRangeApiInjectable; diff --git a/src/common/k8s-api/endpoints/limit-range.api.ts b/src/common/k8s-api/endpoints/limit-range.api.ts deleted file mode 100644 index eec533993c..0000000000 --- a/src/common/k8s-api/endpoints/limit-range.api.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export enum LimitType { - CONTAINER = "Container", - POD = "Pod", - PVC = "PersistentVolumeClaim", -} - -export enum Resource { - MEMORY = "memory", - CPU = "cpu", - STORAGE = "storage", - EPHEMERAL_STORAGE = "ephemeral-storage", -} - -export enum LimitPart { - MAX = "max", - MIN = "min", - DEFAULT = "default", - DEFAULT_REQUEST = "defaultRequest", - MAX_LIMIT_REQUEST_RATIO = "maxLimitRequestRatio", -} - -type LimitRangeParts = Partial>>; - -export interface LimitRangeItem extends LimitRangeParts { - type: string; -} - -export interface LimitRangeSpec { - limits: LimitRangeItem[]; -} - -export class LimitRange extends KubeObject< - NamespaceScopedMetadata, - void, - LimitRangeSpec -> { - static readonly kind = "LimitRange"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/limitranges"; - - getContainerLimits() { - return this.spec.limits.filter(limit => limit.type === LimitType.CONTAINER); - } - - getPodLimits() { - return this.spec.limits.filter(limit => limit.type === LimitType.POD); - } - - getPVCLimits() { - return this.spec.limits.filter(limit => limit.type === LimitType.PVC); - } -} - -export class LimitRangeApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - objectConstructor: LimitRange, - ...opts ?? {}, - }); - } -} diff --git a/src/common/k8s-api/endpoints/metrics.api.ts b/src/common/k8s-api/endpoints/metrics.api.ts deleted file mode 100644 index 406ab1d0b2..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Metrics api - -import moment from "moment"; -import { isDefined, object } from "../../utils"; - -export interface MetricData { - status: string; - data: { - resultType: string; - result: MetricResult[]; - }; -} - -export interface MetricResult { - metric: { - [name: string]: string | undefined; - instance?: string; - node?: string; - pod?: string; - kubernetes?: string; - kubernetes_node?: string; - kubernetes_namespace?: string; - }; - values: [number, string][]; -} - -export function normalizeMetrics(metrics: MetricData | undefined | null, frames = 60): MetricData { - if (!metrics?.data?.result) { - return { - data: { - resultType: "", - result: [{ - metric: {}, - values: [], - }], - }, - status: "", - }; - } - - const { result } = metrics.data; - - if (result.length) { - if (frames > 0) { - // fill the gaps - result.forEach(res => { - if (!res.values || !res.values.length) return; - - let now = moment().startOf("minute").subtract(1, "minute").unix(); - let timestamp = res.values[0][0]; - - while (timestamp <= now) { - timestamp = moment.unix(timestamp).add(1, "minute").unix(); - - if (!res.values.find((value) => value[0] === timestamp)) { - res.values.push([timestamp, "0"]); - } - } - - while (res.values.length < frames) { - const timestamp = moment.unix(res.values[0][0]).subtract(1, "minute").unix(); - - if (!res.values.find((value) => value[0] === timestamp)) { - res.values.unshift([timestamp, "0"]); - } - now = timestamp; - } - }); - } - } - else { - // always return at least empty values array - result.push({ - metric: {}, - values: [], - } as MetricResult); - } - - return metrics; -} - -export function isMetricsEmpty(metrics: Partial>) { - return Object.values(metrics).every(metric => !metric?.data?.result?.length); -} - -export function getItemMetrics(metrics: Partial> | null | undefined, itemName: string): Partial> | undefined { - if (!metrics) { - return undefined; - } - - const itemMetrics = { ...metrics }; - - for (const metric in metrics) { - if (!metrics[metric]?.data?.result) { - continue; - } - const results = metrics[metric]?.data.result; - const result = results?.find(res => Object.values(res.metric)[0] == itemName); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - itemMetrics[metric]!.data.result = result ? [result] : []; - } - - return itemMetrics; -} - -export function getMetricLastPoints(metrics: Partial>): Partial> { - return object.fromEntries( - object.entries(metrics) - .map(([metricName, metric]) => { - try { - return [metricName, +metric.data.result[0].values.slice(-1)[0][1]] as const; - } catch { - return undefined; - } - }) - .filter(isDefined), - ); -} diff --git a/src/common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable.ts deleted file mode 100644 index 84268e3f6a..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import type { RequestMetricsParams } from "./request-metrics.injectable"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface ClusterMetricData { - memoryUsage: MetricData; - memoryRequests: MetricData; - memoryLimits: MetricData; - memoryCapacity: MetricData; - memoryAllocatableCapacity: MetricData; - cpuUsage: MetricData; - cpuRequests: MetricData; - cpuLimits: MetricData; - cpuCapacity: MetricData; - cpuAllocatableCapacity: MetricData; - podUsage: MetricData; - podCapacity: MetricData; - podAllocatableCapacity: MetricData; - fsSize: MetricData; - fsUsage: MetricData; -} - -export type RequestClusterMetricsByNodeNames = (nodeNames: string[], params?: RequestMetricsParams) => Promise; - -const requestClusterMetricsByNodeNamesInjectable = getInjectable({ - id: "get-cluster-metrics-by-node-names", - instantiate: (di): RequestClusterMetricsByNodeNames => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (nodeNames, params) => { - const opts = { - category: "cluster", - nodes: nodeNames.join("|"), - }; - - return requestMetrics({ - memoryUsage: opts, - workloadMemoryUsage: opts, - memoryRequests: opts, - memoryLimits: opts, - memoryCapacity: opts, - memoryAllocatableCapacity: opts, - cpuUsage: opts, - cpuRequests: opts, - cpuLimits: opts, - cpuCapacity: opts, - cpuAllocatableCapacity: opts, - podUsage: opts, - podCapacity: opts, - podAllocatableCapacity: opts, - fsSize: opts, - fsUsage: opts, - }, params); - }; - }, -}); - -export default requestClusterMetricsByNodeNamesInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable.ts deleted file mode 100644 index 8167a6bef4..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-ingress-metrics.injectable.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface IngressMetricData { - bytesSentSuccess: MetricData; - bytesSentFailure: MetricData; - requestDurationSeconds: MetricData; - responseDurationSeconds: MetricData; -} - -export type RequestIngressMetrics = (ingress: string, namespace: string) => Promise; - -const requestIngressMetricsInjectable = getInjectable({ - id: "request-ingress-metrics", - instantiate: (di): RequestIngressMetrics => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (ingress, namespace) => { - const opts = { category: "ingress", ingress, namespace }; - - return requestMetrics({ - bytesSentSuccess: opts, - bytesSentFailure: opts, - requestDurationSeconds: opts, - responseDurationSeconds: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestIngressMetricsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-metrics-for-all-nodes.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-metrics-for-all-nodes.injectable.ts deleted file mode 100644 index 6789d6debc..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-metrics-for-all-nodes.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface NodeMetricData { - memoryUsage: MetricData; - workloadMemoryUsage: MetricData; - memoryCapacity: MetricData; - memoryAllocatableCapacity: MetricData; - cpuUsage: MetricData; - cpuCapacity: MetricData; - fsUsage: MetricData; - fsSize: MetricData; -} - -export type RequestAllNodeMetrics = () => Promise; - -const requestAllNodeMetricsInjectable = getInjectable({ - id: "request-all-node-metrics", - instantiate: (di): RequestAllNodeMetrics => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return () => { - const opts = { category: "nodes" }; - - return requestMetrics({ - memoryUsage: opts, - workloadMemoryUsage: opts, - memoryCapacity: opts, - memoryAllocatableCapacity: opts, - cpuUsage: opts, - cpuCapacity: opts, - fsSize: opts, - fsUsage: opts, - }); - }; - }, -}); - -export default requestAllNodeMetricsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts deleted file mode 100644 index e83c52b9aa..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 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 { getSecondsFromUnixEpoch } from "../../../utils/date/get-current-date-time"; -import apiBaseInjectable from "../../api-base.injectable"; -import type { MetricData } from "../metrics.api"; - - -export interface RequestMetricsParams { - /** - * timestamp in seconds or valid date-string - */ - start?: number | string; - - /** - * timestamp in seconds or valid date-string - */ - end?: number | string; - - /** - * step in seconds - * @default 60 (1 minute) - */ - step?: number; - - /** - * time-range in seconds for data aggregation - * @default 3600 (1 hour) - */ - range?: number; - - /** - * rbac-proxy validation param - */ - namespace?: string; -} - -export interface RequestMetrics { - (query: string, params?: RequestMetricsParams): Promise; - (query: string[], params?: RequestMetricsParams): Promise; - (query: Record>>, params?: RequestMetricsParams): Promise>; -} - -const requestMetricsInjectable = getInjectable({ - id: "request-metrics", - instantiate: (di) => { - const apiBase = di.inject(apiBaseInjectable); - - return (async (query: object, params: RequestMetricsParams = {}) => { - const { range = 3600, step = 60, namespace } = params; - let { start, end } = params; - - if (!start && !end) { - const now = getSecondsFromUnixEpoch(); - - start = now - range; - end = now; - } - - return apiBase.post("/metrics", { - data: query, - query: { - start, end, step, - "kubernetes_namespace": namespace, - }, - }); - }) as RequestMetrics; - }, -}); - -export default requestMetricsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable.ts deleted file mode 100644 index 5cafb5ade0..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-persistent-volume-claim-metrics.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import type { PersistentVolumeClaim } from "../persistent-volume-claim.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface PersistentVolumeClaimMetricData { - diskUsage: MetricData; - diskCapacity: MetricData; -} - -export type RequestPersistentVolumeClaimMetrics = (claim: PersistentVolumeClaim) => Promise; - -const requestPersistentVolumeClaimMetricsInjectable = getInjectable({ - id: "request-persistent-volume-claim-metrics", - instantiate: (di): RequestPersistentVolumeClaimMetrics => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (claim) => { - const opts = { category: "pvc", pvc: claim.getName(), namespace: claim.getNs() }; - - return requestMetrics({ - diskUsage: opts, - diskCapacity: opts, - }, { - namespace: opts.namespace, - }); - }; - }, -}); - -export default requestPersistentVolumeClaimMetricsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable.ts deleted file mode 100644 index d5653574ff..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { DaemonSet } from "../daemon-set.api"; -import type { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface DaemonSetPodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsForDaemonSets = (daemonsets: DaemonSet[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsForDaemonSetsInjectable = getInjectable({ - id: "request-pod-metrics-for-daemon-sets", - instantiate: (di): RequestPodMetricsForDaemonSets => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (daemonSets, namespace, selector = "") => { - const podSelector = daemonSets.map(daemonSet => `${daemonSet.getName()}-[[:alnum:]]{5}`).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsForDaemonSetsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable.ts deleted file mode 100644 index ced8ccd7fe..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { Deployment } from "../deployment.api"; -import type { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface DeploymentPodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsForDeployments = (deployments: Deployment[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsForDeploymentsInjectable = getInjectable({ - id: "request-pod-metrics-for-deployments", - instantiate: (di): RequestPodMetricsForDeployments => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (deployments, namespace, selector = "") => { - const podSelector = deployments.map(deployment => `${deployment.getName()}-[[:alnum:]]{9,}-[[:alnum:]]{5}`).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsForDeploymentsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts deleted file mode 100644 index d52f028cf0..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { Job } from "../job.api"; -import type { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface JobPodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsForJobs = (jobs: Job[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsForJobsInjectable = getInjectable({ - id: "request-pod-metrics-for-jobs", - instantiate: (di): RequestPodMetricsForJobs => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (jobs, namespace, selector = "") => { - const podSelector = jobs.map(job => `${job.getName()}-[[:alnum:]]{5}`).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsForJobsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable.ts deleted file mode 100644 index 4186cba7b4..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { MetricData } from "../metrics.api"; -import type { ReplicaSet } from "../replica-set.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface ReplicaSetPodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsForReplicaSets = (replicaSets: ReplicaSet[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsForReplicaSetsInjectable = getInjectable({ - id: "request-pod-metrics-for-replica-sets", - instantiate: (di): RequestPodMetricsForReplicaSets => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (replicaSets, namespace, selector = "") => { - const podSelector = replicaSets.map(replicaSet => `${replicaSet.getName()}-[[:alnum:]]{5}`).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsForReplicaSetsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable.ts deleted file mode 100644 index 227961bc02..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import type { StatefulSet } from "../stateful-set.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface StatefulSetPodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsForStatefulSets = (statefulSets: StatefulSet[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsForStatefulSetsInjectable = getInjectable({ - id: "request-pod-metrics-for-stateful-sets", - instantiate: (di): RequestPodMetricsForStatefulSets => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (statefulSets, namespace, selector = "") => { - const podSelector = statefulSets.map(statefulset => `${statefulset.getName()}-[[:digit:]]+`).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsForStatefulSetsInjectable; - diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable.ts deleted file mode 100644 index 872ffd740e..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics-in-namespace.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface PodMetricInNamespaceData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; -} - -export type RequestPodMetricsInNamespace = (namespace: string, selector?: string) => Promise; - -const requestPodMetricsInNamespaceInjectable = getInjectable({ - id: "request-pod-metrics-in-namespace", - instantiate: (di): RequestPodMetricsInNamespace => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (namespace, selector) => { - const opts = { category: "pods", pods: ".*", namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - memoryUsage: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsInNamespaceInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable.ts deleted file mode 100644 index e14e5dc293..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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 { MetricData } from "../metrics.api"; -import type { Pod } from "../pod.api"; -import requestMetricsInjectable from "./request-metrics.injectable"; - -export interface PodMetricData { - cpuUsage: MetricData; - memoryUsage: MetricData; - fsUsage: MetricData; - fsWrites: MetricData; - fsReads: MetricData; - networkReceive: MetricData; - networkTransmit: MetricData; - cpuRequests: MetricData; - cpuLimits: MetricData; - memoryRequests: MetricData; - memoryLimits: MetricData; -} - -export type RequestPodMetrics = (pods: Pod[], namespace: string, selector?: string) => Promise; - -const requestPodMetricsInjectable = getInjectable({ - id: "request-pod-metrics", - instantiate: (di): RequestPodMetrics => { - const requestMetrics = di.inject(requestMetricsInjectable); - - return (pods, namespace, selector = "pod, namespace") => { - const podSelector = pods.map(pod => pod.getName()).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; - - return requestMetrics({ - cpuUsage: opts, - cpuRequests: opts, - cpuLimits: opts, - memoryUsage: opts, - memoryRequests: opts, - memoryLimits: opts, - fsUsage: opts, - fsWrites: opts, - fsReads: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - }; - }, -}); - -export default requestPodMetricsInjectable; diff --git a/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts deleted file mode 100644 index 0c74c4d58d..0000000000 --- a/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 apiBaseInjectable from "../../api-base.injectable"; - -export interface MetricProviderInfo { - name: string; - id: string; - isConfigurable: boolean; -} - -export type RequestMetricsProviders = () => Promise; - -const requestMetricsProvidersInjectable = getInjectable({ - id: "request-metrics-providers", - instantiate: (di): RequestMetricsProviders => { - const apiBase = di.inject(apiBaseInjectable); - - return () => apiBase.get("/metrics/providers"); - }, -}); - -export default requestMetricsProvidersInjectable; diff --git a/src/common/k8s-api/endpoints/namespace.api.injectable.ts b/src/common/k8s-api/endpoints/namespace.api.injectable.ts deleted file mode 100644 index fe722433c1..0000000000 --- a/src/common/k8s-api/endpoints/namespace.api.injectable.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { NamespaceApi } from "./namespace.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const namespaceApiInjectable = getInjectable({ - id: "namespace-api", - - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "namespaceApi is only available in certain environments"); - - return new NamespaceApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default namespaceApiInjectable; diff --git a/src/common/k8s-api/endpoints/namespace.api.ts b/src/common/k8s-api/endpoints/namespace.api.ts deleted file mode 100644 index 3de24d3df9..0000000000 --- a/src/common/k8s-api/endpoints/namespace.api.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { ClusterScopedMetadata, KubeObjectStatus } from "../kube-object"; -import { KubeObject } from "../kube-object"; - -export enum NamespaceStatusKind { - ACTIVE = "Active", - TERMINATING = "Terminating", -} - -export interface NamespaceSpec { - finalizers?: string[]; -} - -export interface NamespaceStatus extends KubeObjectStatus { - phase?: string; -} - -export class Namespace extends KubeObject< - ClusterScopedMetadata, - NamespaceStatus, - NamespaceSpec -> { - static readonly kind = "Namespace"; - static readonly namespaced = false; - static readonly apiBase = "/api/v1/namespaces"; - - getStatus() { - return this.status?.phase ?? "-"; - } -} - -export class NamespaceApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Namespace, - }); - } -} diff --git a/src/common/k8s-api/endpoints/network-policy.api.injectable.ts b/src/common/k8s-api/endpoints/network-policy.api.injectable.ts deleted file mode 100644 index 66c6a95732..0000000000 --- a/src/common/k8s-api/endpoints/network-policy.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { NetworkPolicyApi } from "./network-policy.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const networkPolicyApiInjectable = getInjectable({ - id: "network-policy-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "networkPolicyApi is only available in certain environments"); - - return new NetworkPolicyApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default networkPolicyApiInjectable; diff --git a/src/common/k8s-api/endpoints/network-policy.api.ts b/src/common/k8s-api/endpoints/network-policy.api.ts deleted file mode 100644 index 78520d5539..0000000000 --- a/src/common/k8s-api/endpoints/network-policy.api.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export interface IPolicyIpBlock { - cidr: string; - except?: string[]; -} - -/** - * @deprecated Use `LabelSelector` instead - */ -export type IPolicySelector = LabelSelector; - -export interface NetworkPolicyPort { - /** - * The protocol which network traffic must match. - * - * One of: - * - `"TCP"` - * - `"UDP"` - * - `"SCTP"` - * - * @default "TCP" - */ - protocol?: string; - - /** - * The port on the given protocol. This can either be a numerical or named - * port on a pod. If this field is not provided, this matches all port names and - * numbers. - * - * If present, only traffic on the specified protocol AND port will be matched. - */ - port?: number | string; - - /** - * If set, indicates that the range of ports from port to endPort, inclusive, - * should be allowed by the policy. This field cannot be defined if the port field - * is not defined or if the port field is defined as a named (string) port. - * - * The endPort must be equal or greater than port. - */ - endPort?: number; -} - -export interface NetworkPolicyPeer { - /** - * IPBlock defines policy on a particular IPBlock. If this field is set then - * neither of the other fields can be. - */ - ipBlock?: IPolicyIpBlock; - - /** - * Selects Namespaces using cluster-scoped labels. This field follows standard label - * selector semantics; if present but empty, it selects all namespaces. - * - * If PodSelector is also set, then the NetworkPolicyPeer as a whole selects - * the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. - * - * Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector. - */ - namespaceSelector?: LabelSelector; - - /** - * This is a label selector which selects Pods. This field follows standard label - * selector semantics; if present but empty, it selects all pods. - * - * If NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects - * the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. - * - * Otherwise it selects the Pods matching PodSelector in the policy's own Namespace. - */ - podSelector?: LabelSelector; -} - -export interface IPolicyIngress { - from?: NetworkPolicyPeer[]; - ports?: NetworkPolicyPort[]; -} - -export interface IPolicyEgress { - to?: NetworkPolicyPeer[]; - ports?: NetworkPolicyPort[]; -} - -export type PolicyType = "Ingress" | "Egress"; - -export interface NetworkPolicySpec { - podSelector: LabelSelector; - policyTypes?: PolicyType[]; - ingress?: IPolicyIngress[]; - egress?: IPolicyEgress[]; -} - -export class NetworkPolicy extends KubeObject< - NamespaceScopedMetadata, - void, - NetworkPolicySpec -> { - static readonly kind = "NetworkPolicy"; - static readonly namespaced = true; - static readonly apiBase = "/apis/networking.k8s.io/v1/networkpolicies"; - - getMatchLabels(): string[] { - if (!this.spec.podSelector || !this.spec.podSelector.matchLabels) return []; - - return Object - .entries(this.spec.podSelector.matchLabels) - .map(data => data.join(":")); - } - - getTypes(): string[] { - if (!this.spec.policyTypes) return []; - - return this.spec.policyTypes; - } -} - -export class NetworkPolicyApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: NetworkPolicy, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/node.api.injectable.ts b/src/common/k8s-api/endpoints/node.api.injectable.ts deleted file mode 100644 index b780d69ee7..0000000000 --- a/src/common/k8s-api/endpoints/node.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { NodeApi } from "./node.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const nodeApiInjectable = getInjectable({ - id: "node-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "nodeApi is only available in certain environments"); - - return new NodeApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default nodeApiInjectable; diff --git a/src/common/k8s-api/endpoints/node.api.ts b/src/common/k8s-api/endpoints/node.api.ts deleted file mode 100644 index 158f359a57..0000000000 --- a/src/common/k8s-api/endpoints/node.api.ts +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { cpuUnitsToNumber, unitsToBytes, isObject } from "../../../renderer/utils"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import { TypedRegEx } from "typed-regex"; - -export class NodeApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Node, - }); - } -} - -export interface NodeTaint { - key: string; - value?: string; - effect: string; - timeAdded: string; -} - -export function formatNodeTaint(taint: NodeTaint): string { - if (taint.value) { - return `${taint.key}=${taint.value}:${taint.effect}`; - } - - return `${taint.key}:${taint.effect}`; -} - -export interface NodeCondition extends BaseKubeObjectCondition { - /** - * Last time we got an update on a given condition. - */ - lastHeartbeatTime?: string; -} - -/** - * These role label prefixs are the ones that are for master nodes - * - * The `master` label has been deprecated in Kubernetes 1.20, and will be removed in 1.25 so we - * have to also use the newer `control-plane` label - */ -const masterNodeLabels = [ - "master", - "control-plane", -]; - -/** - * This regex is used in the `getRoleLabels()` method bellow, but placed here - * as factoring out regexes is best practice. - */ -const nodeRoleLabelKeyMatcher = TypedRegEx("^.*node-role.kubernetes.io/+(?.+)$"); - -export interface NodeSpec { - podCIDR?: string; - podCIDRs?: string[]; - providerID?: string; - /** - * @deprecated see https://issues.k8s.io/61966 - */ - externalID?: string; - taints?: NodeTaint[]; - unschedulable?: boolean; -} - -export interface NodeAddress { - type: "Hostname" | "ExternalIP" | "InternalIP"; - address: string; -} - -export interface NodeStatusResources extends Partial> { - cpu?: string; - "ephemeral-storage"?: string; - "hugepages-1Gi"?: string; - "hugepages-2Mi"?: string; - memory?: string; - pods?: string; -} - -export interface ConfigMapNodeConfigSource { - kubeletConfigKey: string; - name: string; - namespace: string; - resourceVersion?: string; - uid?: string; -} - -export interface NodeConfigSource { - configMap?: ConfigMapNodeConfigSource; -} - -export interface NodeConfigStatus { - active?: NodeConfigSource; - assigned?: NodeConfigSource; - lastKnownGood?: NodeConfigSource; - error?: string; -} - -export interface DaemonEndpoint { - Port: number; //it must be uppercase for backwards compatibility -} - -export interface NodeDaemonEndpoints { - kubeletEndpoint?: DaemonEndpoint; -} - -export interface ContainerImage { - names?: string[]; - sizeBytes?: number; -} - -export interface NodeSystemInfo { - architecture: string; - bootID: string; - containerRuntimeVersion: string; - kernelVersion: string; - kubeProxyVersion: string; - kubeletVersion: string; - machineID: string; - operatingSystem: string; - osImage: string; - systemUUID: string; -} - -export interface AttachedVolume { - name: string; - devicePath: string; -} - -export interface NodeStatus { - capacity?: NodeStatusResources; - allocatable?: NodeStatusResources; - conditions?: NodeCondition[]; - addresses?: NodeAddress[]; - config?: NodeConfigStatus; - daemonEndpoints?: NodeDaemonEndpoints; - images?: ContainerImage[]; - nodeInfo?: NodeSystemInfo; - phase?: string; - volumesInUse?: string[]; - volumesAttached?: AttachedVolume[]; -} - -export class Node extends KubeObject< - ClusterScopedMetadata, - NodeStatus, - NodeSpec -> { - static readonly kind = "Node"; - static readonly namespaced = false; - static readonly apiBase = "/api/v1/nodes"; - - /** - * Returns the concatination of all current condition types which have a status - * of `"True"` - */ - getNodeConditionText(): string { - if (!this.status?.conditions) { - return ""; - } - - return this.status.conditions - .filter(condition => condition.status === "True") - .map(condition => condition.type) - .join(" "); - } - - getTaints() { - return this.spec.taints || []; - } - - isMasterNode(): boolean { - return this.getRoleLabelItems() - .some(roleLabel => masterNodeLabels.includes(roleLabel)); - } - - getRoleLabelItems(): string[] { - const { labels } = this.metadata; - const roleLabels: string[] = []; - - if (!isObject(labels)) { - return roleLabels; - } - - for (const labelKey of Object.keys(labels)) { - const match = nodeRoleLabelKeyMatcher.match(labelKey); - - if (match?.groups) { - roleLabels.push(match.groups.role); - } - } - - if (typeof labels["kubernetes.io/role"] === "string") { - roleLabels.push(labels["kubernetes.io/role"]); - } - - if (typeof labels["node.kubernetes.io/role"] === "string") { - roleLabels.push(labels["node.kubernetes.io/role"]); - } - - return roleLabels; - } - - getRoleLabels(): string { - return this.getRoleLabelItems().join(", "); - } - - getCpuCapacity() { - if (!this.status?.capacity || !this.status.capacity.cpu) return 0; - - return cpuUnitsToNumber(this.status.capacity.cpu); - } - - getMemoryCapacity() { - if (!this.status?.capacity || !this.status.capacity.memory) return 0; - - return unitsToBytes(this.status.capacity.memory); - } - - getConditions(): NodeCondition[] { - const conditions = this.status?.conditions || []; - - if (this.isUnschedulable()) { - return [{ type: "SchedulingDisabled", status: "True" }, ...conditions]; - } - - return conditions; - } - - getActiveConditions() { - return this.getConditions().filter(c => c.status === "True"); - } - - getWarningConditions() { - const goodConditions = ["Ready", "HostUpgrades", "SchedulingDisabled"]; - - return this.getActiveConditions().filter(condition => { - return !goodConditions.includes(condition.type); - }); - } - - getKubeletVersion() { - return this.status?.nodeInfo?.kubeletVersion ?? ""; - } - - getOperatingSystem(): string { - return this.metadata?.labels?.["kubernetes.io/os"] - || this.metadata?.labels?.["beta.kubernetes.io/os"] - || this.status?.nodeInfo?.operatingSystem - || "linux"; - } - - isUnschedulable() { - return this.spec.unschedulable; - } -} diff --git a/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts b/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts deleted file mode 100644 index 5d21ba9587..0000000000 --- a/src/common/k8s-api/endpoints/persistent-volume-claim.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PersistentVolumeClaimApi } from "./persistent-volume-claim.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const persistentVolumeClaimApiInjectable = getInjectable({ - id: "persistent-volume-claim-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeClaimApi is only available in certain environments"); - - return new PersistentVolumeClaimApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default persistentVolumeClaimApiInjectable; diff --git a/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts b/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts deleted file mode 100644 index 947d9139a3..0000000000 --- a/src/common/k8s-api/endpoints/persistent-volume-claim.api.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LabelSelector, NamespaceScopedMetadata, TypedLocalObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { Pod } from "./pod.api"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import { object } from "../../utils"; -import type { ResourceRequirements } from "./types/resource-requirements"; - -export class PersistentVolumeClaimApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: PersistentVolumeClaim, - }); - } -} - -export interface PersistentVolumeClaimSpec { - accessModes?: string[]; - dataSource?: TypedLocalObjectReference; - dataSourceRef?: TypedLocalObjectReference; - resources?: ResourceRequirements; - selector?: LabelSelector; - storageClassName?: string; - volumeMode?: string; - volumeName?: string; -} - -export interface PersistentVolumeClaimStatus { - phase: string; // Pending -} - -export class PersistentVolumeClaim extends KubeObject< - NamespaceScopedMetadata, - PersistentVolumeClaimStatus, - PersistentVolumeClaimSpec -> { - static readonly kind = "PersistentVolumeClaim"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/persistentvolumeclaims"; - - getPods(pods: Pod[]): Pod[] { - return pods - .filter(pod => pod.getNs() === this.getNs()) - .filter(pod => ( - pod.getVolumes() - .filter(volume => volume.persistentVolumeClaim?.claimName === this.getName()) - .length > 0 - )); - } - - getStorage(): string { - return this.spec.resources?.requests?.storage ?? "-"; - } - - getMatchLabels(): string[] { - return object.entries(this.spec.selector?.matchLabels) - .map(([name, val]) => `${name}:${val}`); - } - - getMatchExpressions() { - return this.spec.selector?.matchExpressions ?? []; - } - - getStatus(): string { - return this.status?.phase ?? "-"; - } -} diff --git a/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts b/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts deleted file mode 100644 index cde587942c..0000000000 --- a/src/common/k8s-api/endpoints/persistent-volume.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PersistentVolumeApi } from "./persistent-volume.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const persistentVolumeApiInjectable = getInjectable({ - id: "persistent-volume-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeApi is only available in certain environments"); - - return new PersistentVolumeApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default persistentVolumeApiInjectable; diff --git a/src/common/k8s-api/endpoints/persistent-volume.api.ts b/src/common/k8s-api/endpoints/persistent-volume.api.ts deleted file mode 100644 index f4d80a82eb..0000000000 --- a/src/common/k8s-api/endpoints/persistent-volume.api.ts +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterScopedMetadata, LabelSelector, ObjectReference, TypedLocalObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import { unitsToBytes } from "../../utils"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { ResourceRequirements } from "./types/resource-requirements"; - -export interface PersistentVolumeSpec { - /** - * AccessModes contains the desired access modes the volume should have. - * - * More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 - */ - accessModes?: string[]; - dataSource?: TypedLocalObjectReference; - dataSourceRef?: TypedLocalObjectReference; - resources?: ResourceRequirements; - selector?: LabelSelector; - - /** - * Name of the StorageClass required by the claim. - * - * More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - */ - storageClassName?: string; - - /** - * Defines what type of volume is required by the claim. Value of Filesystem is implied when not - * included in claim spec. - */ - volumeMode?: string; - - /** - * A description of the persistent volume\'s resources and capacity. - * - * More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity - */ - capacity?: Partial>; - flexVolume?: { - driver: string; // ceph.rook.io/rook-ceph-system, - options?: { - clusterNamespace: string; // rook-ceph, - image: string; // pvc-c5d7c485-9f1b-11e8-b0ea-9600000e54fb, - pool: string; // replicapool, - storageClass: string; // rook-ceph-block - }; - }; - mountOptions?: string[]; - claimRef?: ObjectReference; - persistentVolumeReclaimPolicy?: string; // Delete, - nfs?: { - path: string; - server: string; - }; -} - -export interface PersistentVolumeStatus { - phase: string; - reason?: string; -} - -export class PersistentVolume extends KubeObject< - ClusterScopedMetadata, - PersistentVolumeStatus, - PersistentVolumeSpec -> { - static kind = "PersistentVolume"; - static namespaced = false; - static apiBase = "/api/v1/persistentvolumes"; - - getCapacity(inBytes = false) { - const capacity = this.spec.capacity; - - if (capacity?.storage) { - if (inBytes) return unitsToBytes(capacity.storage); - - return capacity.storage; - } - - return 0; - } - - getStatus() { - return this.status?.phase || "-"; - } - - getStorageClass(): string { - return this.spec.storageClassName ?? ""; - } - - getClaimRefName(): string { - return this.spec.claimRef?.name ?? ""; - } - - getStorageClassName() { - return this.spec.storageClassName || ""; - } -} - -export class PersistentVolumeApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: PersistentVolume, - }); - } -} diff --git a/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts b/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts deleted file mode 100644 index 28ecff01b1..0000000000 --- a/src/common/k8s-api/endpoints/pod-disruption-budget.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PodDisruptionBudgetApi } from "./pod-disruption-budget.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const podDisruptionBudgetApiInjectable = getInjectable({ - id: "pod-disruption-budget-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podDisruptionBudgetApi is only available in certain environments"); - - return new PodDisruptionBudgetApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default podDisruptionBudgetApiInjectable; diff --git a/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts b/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts deleted file mode 100644 index 0010bbb642..0000000000 --- a/src/common/k8s-api/endpoints/pod-disruption-budget.api.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export interface PodDisruptionBudgetSpec { - minAvailable: string; - maxUnavailable: string; - selector: LabelSelector; -} - -export interface PodDisruptionBudgetStatus { - currentHealthy: number; - desiredHealthy: number; - disruptionsAllowed: number; - expectedPods: number; -} - -export class PodDisruptionBudget extends KubeObject< - NamespaceScopedMetadata, - PodDisruptionBudgetStatus, - PodDisruptionBudgetSpec -> { - static readonly kind = "PodDisruptionBudget"; - static readonly namespaced = true; - static readonly apiBase = "/apis/policy/v1beta1/poddisruptionbudgets"; - - getSelectors() { - return KubeObject.stringifyLabels(this.spec.selector.matchLabels); - } - - getMinAvailable() { - return this.spec.minAvailable || "N/A"; - } - - getMaxUnavailable() { - return this.spec.maxUnavailable || "N/A"; - } - - getCurrentHealthy() { - return this.status?.currentHealthy ?? 0; - } - - getDesiredHealthy() { - return this.status?.desiredHealthy ?? 0; - } -} - -export class PodDisruptionBudgetApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: PodDisruptionBudget, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts b/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts deleted file mode 100644 index ddd2b109ef..0000000000 --- a/src/common/k8s-api/endpoints/pod-metrics.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PodMetricsApi } from "./pod-metrics.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const podMetricsApiInjectable = getInjectable({ - id: "pod-metrics-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podMetricsApi is only available in certain environments"); - - return new PodMetricsApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default podMetricsApiInjectable; diff --git a/src/common/k8s-api/endpoints/pod-metrics.api.ts b/src/common/k8s-api/endpoints/pod-metrics.api.ts deleted file mode 100644 index 84a58026d5..0000000000 --- a/src/common/k8s-api/endpoints/pod-metrics.api.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; - -export interface PodMetricsData extends KubeJsonApiData, void, void> { - timestamp: string; - window: string; - containers: PodMetricsContainer[]; -} - -export interface PodMetricsContainerUsage { - cpu: string; - memory: string; -} - -export interface PodMetricsContainer { - name: string; - usage: PodMetricsContainerUsage; -} - -export class PodMetrics extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static readonly kind = "PodMetrics"; - static readonly namespaced = true; - static readonly apiBase = "/apis/metrics.k8s.io/v1beta1/pods"; - - timestamp: string; - window: string; - containers: PodMetricsContainer[]; - - constructor({ - timestamp, - window, - containers, - ...rest - }: PodMetricsData) { - super(rest); - this.timestamp = timestamp; - this.window = window; - this.containers = containers; - } -} - -export class PodMetricsApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: PodMetrics, - }); - } -} diff --git a/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts b/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts deleted file mode 100644 index c62b568e87..0000000000 --- a/src/common/k8s-api/endpoints/pod-security-policy.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PodSecurityPolicyApi } from "./pod-security-policy.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const podSecurityPolicyApiInjectable = getInjectable({ - id: "pod-security-policy-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podSecurityPolicyApi is only available in certain environments"); - - return new PodSecurityPolicyApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default podSecurityPolicyApiInjectable; diff --git a/src/common/k8s-api/endpoints/pod-security-policy.api.ts b/src/common/k8s-api/endpoints/pod-security-policy.api.ts deleted file mode 100644 index f0e9e1110c..0000000000 --- a/src/common/k8s-api/endpoints/pod-security-policy.api.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export interface PodSecurityPolicySpec { - allowPrivilegeEscalation?: boolean; - allowedCSIDrivers?: { - name: string; - }[]; - allowedCapabilities: string[]; - allowedFlexVolumes?: { - driver: string; - }[]; - allowedHostPaths?: { - pathPrefix: string; - readOnly: boolean; - }[]; - allowedProcMountTypes?: string[]; - allowedUnsafeSysctls?: string[]; - defaultAddCapabilities?: string[]; - defaultAllowPrivilegeEscalation?: boolean; - forbiddenSysctls?: string[]; - fsGroup?: { - rule: string; - ranges: { - max: number; - min: number; - }[]; - }; - hostIPC?: boolean; - hostNetwork?: boolean; - hostPID?: boolean; - hostPorts?: { - max: number; - min: number; - }[]; - privileged?: boolean; - readOnlyRootFilesystem?: boolean; - requiredDropCapabilities?: string[]; - runAsGroup?: { - ranges: { - max: number; - min: number; - }[]; - rule: string; - }; - runAsUser?: { - rule: string; - ranges: { - max: number; - min: number; - }[]; - }; - runtimeClass?: { - allowedRuntimeClassNames: string[]; - defaultRuntimeClassName: string; - }; - seLinux?: { - rule: string; - seLinuxOptions: { - level: string; - role: string; - type: string; - user: string; - }; - }; - supplementalGroups?: { - rule: string; - ranges: { - max: number; - min: number; - }[]; - }; - volumes?: string[]; -} - -export class PodSecurityPolicy extends KubeObject< - ClusterScopedMetadata, - void, - PodSecurityPolicySpec -> { - static readonly kind = "PodSecurityPolicy"; - static readonly namespaced = false; - static readonly apiBase = "/apis/policy/v1beta1/podsecuritypolicies"; - - isPrivileged() { - return !!this.spec.privileged; - } - - getVolumes() { - return this.spec.volumes || []; - } - - getRules() { - const { fsGroup, runAsGroup, runAsUser, supplementalGroups, seLinux } = this.spec; - - return { - fsGroup: fsGroup ? fsGroup.rule : "", - runAsGroup: runAsGroup ? runAsGroup.rule : "", - runAsUser: runAsUser ? runAsUser.rule : "", - supplementalGroups: supplementalGroups ? supplementalGroups.rule : "", - seLinux: seLinux ? seLinux.rule : "", - }; - } -} - -export class PodSecurityPolicyApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: PodSecurityPolicy, - - }); - } -} diff --git a/src/common/k8s-api/endpoints/pod.api.injectable.ts b/src/common/k8s-api/endpoints/pod.api.injectable.ts deleted file mode 100644 index f2c1635b14..0000000000 --- a/src/common/k8s-api/endpoints/pod.api.injectable.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PodApi } from "./pod.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const podApiInjectable = getInjectable({ - id: "pod-api", - - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podApi is only available in certain environments"); - - return new PodApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default podApiInjectable; diff --git a/src/common/k8s-api/endpoints/pod.api.ts b/src/common/k8s-api/endpoints/pod.api.ts deleted file mode 100644 index 3a0bed57cd..0000000000 --- a/src/common/k8s-api/endpoints/pod.api.ts +++ /dev/null @@ -1,853 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies, ResourceDescriptor } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { RequireExactlyOne } from "type-fest"; -import type { KubeObjectMetadata, LocalObjectReference, Affinity, Toleration, NamespaceScopedMetadata } from "../kube-object"; -import type { SecretReference } from "./secret.api"; -import type { PersistentVolumeClaimSpec } from "./persistent-volume-claim.api"; -import { KubeObject } from "../kube-object"; -import { isDefined } from "../../utils"; -import type { PodSecurityContext } from "./types/pod-security-context"; -import type { Probe } from "./types/probe"; -import type { Container } from "./types/container"; -import type { ObjectFieldSelector, ResourceFieldSelector } from "./types"; - -export class PodApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: Pod, - }); - } - - async getLogs(params: ResourceDescriptor, query?: PodLogsQuery): Promise { - const path = `${this.getUrl(params)}/log`; - - return this.request.get(path, { query }); - } -} - -// Reference: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#read-log-pod-v1-core -export interface PodLogsQuery { - container?: string; - tailLines?: number; - timestamps?: boolean; - sinceTime?: string; // Date.toISOString()-format - follow?: boolean; - previous?: boolean; -} - -export enum PodStatusPhase { - TERMINATED = "Terminated", - FAILED = "Failed", - PENDING = "Pending", - RUNNING = "Running", - SUCCEEDED = "Succeeded", - EVICTED = "Evicted", -} - -export interface ContainerStateRunning { - startedAt: string; -} - -export interface ContainerStateWaiting { - reason: string; - message: string; -} - -export interface ContainerStateTerminated { - startedAt: string; - finishedAt: string; - exitCode: number; - reason: string; - containerID?: string; - message?: string; - signal?: number; -} - -/** - * ContainerState holds a possible state of container. Only one of its members - * may be specified. If none of them is specified, the default one is - * `ContainerStateWaiting`. - */ -export interface ContainerState { - running?: ContainerStateRunning; - waiting?: ContainerStateWaiting; - terminated?: ContainerStateTerminated; -} - -export interface PodContainerStatus { - name: string; - state?: ContainerState; - lastState?: ContainerState; - ready: boolean; - restartCount: number; - image: string; - imageID: string; - containerID?: string; - started?: boolean; -} - -export interface AwsElasticBlockStoreSource { - volumeID: string; - fsType: string; -} - -export interface AzureDiskSource { - /** - * The name of the VHD blob object OR the name of an Azure managed data disk if `kind` is `"Managed"`. - */ - diskName: string; - /** - * The URI of the vhd blob object OR the `resourceID` of an Azure managed data disk if `kind` is `"Managed"`. - */ - diskURI: string; - /** - * Kind of disk - * @default "Shared" - */ - kind?: "Shared" | "Dedicated" | "Managed"; - /** - * Disk caching mode. - * @default "None" - */ - cachingMode?: "None" | "ReadOnly" | "ReadWrite"; - /** - * The filesystem type to mount. - * @default "ext4" - */ - fsType?: string; - /** - * Whether the filesystem is used as readOnly. - * @default false - */ - readonly?: boolean; -} - -export interface AzureFileSource { - /** - * The name of the secret that contains both Azure storage account name and key. - */ - secretName: string; - /** - * The share name to be used. - */ - shareName: string; - /** - * In case the secret is stored in a different namespace. - * @default "default" - */ - secretNamespace?: string; - /** - * Whether the filesystem is used as readOnly. - */ - readOnly: boolean; -} - -export interface CephfsSource { - /** - * List of Ceph monitors - */ - monitors: string[]; - /** - * Used as the mounted root, rather than the full Ceph tree. - * @default "/" - */ - path?: string; - /** - * The RADOS user name. - * @default "admin" - */ - user?: string; - /** - * The path to the keyring file. - * @default "/etc/ceph/user.secret" - */ - secretFile?: string; - /** - * Reference to Ceph authentication secrets. If provided, then the secret overrides `secretFile` - */ - secretRef?: SecretReference; - /** - * Whether the filesystem is used as readOnly. - * - * @default false - */ - readOnly?: boolean; -} - -export interface CinderSource { - volumeID: string; - fsType: string; - /** - * @default false - */ - readOnly?: boolean; - secretRef?: SecretReference; -} - -export interface ConfigMapSource { - name: string; - items: { - key: string; - path: string; - }[]; -} - -export interface DownwardApiSource { - items: { - path: string; - fieldRef: { - fieldPath: string; - }; - }[]; -} - -export interface EphemeralSource { - volumeClaimTemplate: { - /** - * All the rest of the fields are ignored and rejected during validation - */ - metadata?: Pick; - spec: PersistentVolumeClaimSpec; - }; -} - -export interface EmptyDirSource { - medium?: string; - sizeLimit?: string; -} - -export interface FiberChannelSource { - /** - * A list of World Wide Names - */ - targetWWNs: string[]; - /** - * Logical Unit number - */ - lun: number; - /** - * The type of filesystem - * @default "ext4" - */ - fsType?: string; - readOnly: boolean; -} - -export interface FlockerSource { - datasetName: string; -} - -export interface FlexVolumeSource { - driver: string; - fsType?: string; - secretRef?: LocalObjectReference; - /** - * @default false - */ - readOnly?: boolean; - options?: Record; -} - -export interface GcePersistentDiskSource { - pdName: string; - fsType: string; -} - -export interface GitRepoSource { - repository: string; - revision: string; -} - -export interface GlusterFsSource { - /** - * The name of the Endpoints object that represents a Gluster cluster configuration. - */ - endpoints: string; - /** - * The Glusterfs volume name. - */ - path: string; - /** - * The boolean that sets the mountpoint readOnly or readWrite. - */ - readOnly: boolean; -} - -export interface HostPathSource { - path: string; - /** - * Determines the sorts of checks that will be done - * @default "" - */ - type?: "" | "DirectoryOrCreate" | "Directory" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice"; -} - -export interface IScsiSource { - targetPortal: string; - iqn: string; - lun: number; - fsType: string; - readOnly: boolean; - chapAuthDiscovery?: boolean; - chapAuthSession?: boolean; - secretRef?: SecretReference; -} - -export interface LocalSource { - path: string; -} - -export interface NetworkFsSource { - server: string; - path: string; - readOnly?: boolean; -} - -export interface PersistentVolumeClaimSource { - claimName: string; -} - -export interface PhotonPersistentDiskSource { - pdID: string; - /** - * @default "ext4" - */ - fsType?: string; -} - -export interface PortworxVolumeSource { - volumeID: string; - fsType?: string; - readOnly?: boolean; -} - -export interface KeyToPath { - key: string; - path: string; - mode?: number; -} - -export interface ConfigMapProjection { - name: string; - items?: KeyToPath[]; - optional?: boolean; -} - -export interface DownwardAPIVolumeFile { - path: string; - fieldRef?: ObjectFieldSelector; - resourceFieldRef?: ResourceFieldSelector; - mode?: number; -} - -export interface DownwardAPIProjection { - items?: DownwardAPIVolumeFile[]; -} - -export interface SecretProjection { - name: string; - items?: KeyToPath[]; - optional?: boolean; -} - -export interface ServiceAccountTokenProjection { - audience?: string; - expirationSeconds?: number; - path: string; -} - -export interface VolumeProjection { - secret?: SecretProjection; - downwardAPI?: DownwardAPIProjection; - configMap?: ConfigMapProjection; - serviceAccountToken?: ServiceAccountTokenProjection; -} - -export interface ProjectedSource { - sources?: VolumeProjection[]; - defaultMode?: number; -} - -export interface QuobyteSource { - registry: string; - volume: string; - /** - * @default false - */ - readOnly?: boolean; - /** - * @default "serivceaccount" - */ - user?: string; - group?: string; - tenant?: string; -} - -export interface RadosBlockDeviceSource { - monitors: string[]; - image: string; - /** - * @default "ext4" - */ - fsType?: string; - /** - * @default "rbd" - */ - pool?: string; - /** - * @default "admin" - */ - user?: string; - /** - * @default "/etc/ceph/keyring" - */ - keyring?: string; - secretRef?: SecretReference; - /** - * @default false - */ - readOnly?: boolean; -} - -export interface ScaleIoSource { - gateway: string; - system: string; - secretRef?: LocalObjectReference; - /** - * @default false - */ - sslEnabled?: boolean; - protectionDomain?: string; - storagePool?: string; - /** - * @default "ThinProvisioned" - */ - storageMode?: "ThickProvisioned" | "ThinProvisioned"; - volumeName: string; - /** - * @default "xfs" - */ - fsType?: string; - /** - * @default false - */ - readOnly?: boolean; -} - -export interface SecretSource { - secretName: string; - items?: { - key: string; - path: string; - mode?: number; - }[]; - defaultMode?: number; - optional?: boolean; -} - -export interface StorageOsSource { - volumeName: string; - /** - * @default Pod.metadata.namespace - */ - volumeNamespace?: string; - /** - * @default "ext4" - */ - fsType?: string; - /** - * @default false - */ - readOnly?: boolean; - secretRef?: LocalObjectReference; -} - -export interface VsphereVolumeSource { - volumePath: string; - /** - * @default "ext4" - */ - fsType?: string; - storagePolicyName?: string; - storagePolicyID?: string; -} - -export interface ContainerStorageInterfaceSource { - driver: string; - /** - * @default false - */ - readOnly?: boolean; - /** - * @default "ext4" - */ - fsType?: string; - volumeAttributes?: Record; - controllerPublishSecretRef?: SecretReference; - nodeStageSecretRef?: SecretReference; - nodePublishSecretRef?: SecretReference; - controllerExpandSecretRef?: SecretReference; -} - -export interface PodVolumeVariants { - awsElasticBlockStore: AwsElasticBlockStoreSource; - azureDisk: AzureDiskSource; - azureFile: AzureFileSource; - cephfs: CephfsSource; - cinder: CinderSource; - configMap: ConfigMapSource; - csi: ContainerStorageInterfaceSource; - downwardAPI: DownwardApiSource; - emptyDir: EmptyDirSource; - ephemeral: EphemeralSource; - fc: FiberChannelSource; - flexVolume: FlexVolumeSource; - flocker: FlockerSource; - gcePersistentDisk: GcePersistentDiskSource; - gitRepo: GitRepoSource; - glusterfs: GlusterFsSource; - hostPath: HostPathSource; - iscsi: IScsiSource; - local: LocalSource; - nfs: NetworkFsSource; - persistentVolumeClaim: PersistentVolumeClaimSource; - photonPersistentDisk: PhotonPersistentDiskSource; - portworxVolume: PortworxVolumeSource; - projected: ProjectedSource; - quobyte: QuobyteSource; - rbd: RadosBlockDeviceSource; - scaleIO: ScaleIoSource; - secret: SecretSource; - storageos: StorageOsSource; - vsphereVolume: VsphereVolumeSource; -} - -/** - * The valid kinds of volume - */ -export type PodVolumeKind = keyof PodVolumeVariants; - -export type PodSpecVolume = RequireExactlyOne & { - name: string; -}; - - -export interface HostAlias { - ip: string; - hostnames: string[]; -} - -export interface Sysctl { - name: string; - value: string; -} - -export interface TopologySpreadConstraint { - -} - -export interface PodSpec { - activeDeadlineSeconds?: number; - affinity?: Affinity; - automountServiceAccountToken?: boolean; - containers?: Container[]; - dnsPolicy?: string; - enableServiceLinks?: boolean; - ephemeralContainers?: unknown[]; - hostAliases?: HostAlias[]; - hostIPC?: boolean; - hostname?: string; - hostNetwork?: boolean; - hostPID?: boolean; - imagePullSecrets?: LocalObjectReference[]; - initContainers?: Container[]; - nodeName?: string; - nodeSelector?: Partial>; - overhead?: Partial>; - preemptionPolicy?: string; - priority?: number; - priorityClassName?: string; - readinessGates?: unknown[]; - restartPolicy?: string; - runtimeClassName?: string; - schedulerName?: string; - securityContext?: PodSecurityContext; - serviceAccount?: string; - serviceAccountName?: string; - setHostnameAsFQDN?: boolean; - shareProcessNamespace?: boolean; - subdomain?: string; - terminationGracePeriodSeconds?: number; - tolerations?: Toleration[]; - topologySpreadConstraints?: TopologySpreadConstraint[]; - volumes?: PodSpecVolume[]; -} - -export interface PodCondition { - lastProbeTime?: number; - lastTransitionTime?: string; - message?: string; - reason?: string; - type: string; - status: string; -} - -export interface PodStatus { - phase: string; - conditions: PodCondition[]; - hostIP: string; - podIP: string; - podIPs?: { - ip: string; - }[]; - startTime: string; - initContainerStatuses?: PodContainerStatus[]; - containerStatuses?: PodContainerStatus[]; - qosClass?: string; - reason?: string; -} - -export class Pod extends KubeObject< - NamespaceScopedMetadata, - PodStatus, - PodSpec -> { - static kind = "Pod"; - static namespaced = true; - static apiBase = "/api/v1/pods"; - - getAffinityNumber() { - return Object.keys(this.getAffinity()).length; - } - - getInitContainers() { - return this.spec?.initContainers ?? []; - } - - getContainers() { - return this.spec?.containers ?? []; - } - - getAllContainers() { - return [...this.getContainers(), ...this.getInitContainers()]; - } - - getRunningContainers() { - const runningContainerNames = new Set( - this.getContainerStatuses() - .filter(({ state }) => state?.running) - .map(({ name }) => name), - ); - - return this.getAllContainers() - .filter(({ name }) => runningContainerNames.has(name)); - } - - getContainerStatuses(includeInitContainers = true) { - const { containerStatuses = [], initContainerStatuses = [] } = this.status ?? {}; - - if (includeInitContainers) { - return [...containerStatuses, ...initContainerStatuses]; - } - - return [...containerStatuses]; - } - - getRestartsCount(): number { - const { containerStatuses = [] } = this.status ?? {}; - - return containerStatuses.reduce((totalCount, { restartCount }) => totalCount + restartCount, 0); - } - - getQosClass() { - return this.status?.qosClass || ""; - } - - getReason() { - return this.status?.reason || ""; - } - - getPriorityClassName() { - return this.spec?.priorityClassName || ""; - } - - getRuntimeClassName() { - return this.spec?.runtimeClassName || ""; - } - - getServiceAccountName() { - return this.spec?.serviceAccountName || ""; - } - - getStatus(): PodStatusPhase { - const phase = this.getStatusPhase(); - const reason = this.getReason(); - const trueConditionTypes = new Set(this.getConditions() - .filter(({ status }) => status === "True") - .map(({ type }) => type)); - const isInGoodCondition = ["Initialized", "Ready"].every(condition => trueConditionTypes.has(condition)); - - if (reason === PodStatusPhase.EVICTED) { - return PodStatusPhase.EVICTED; - } - - if (phase === PodStatusPhase.FAILED) { - return PodStatusPhase.FAILED; - } - - if (phase === PodStatusPhase.SUCCEEDED) { - return PodStatusPhase.SUCCEEDED; - } - - if (phase === PodStatusPhase.RUNNING && isInGoodCondition) { - return PodStatusPhase.RUNNING; - } - - return PodStatusPhase.PENDING; - } - - // Returns pod phase or container error if occurred - getStatusMessage(): string { - if (this.getReason() === PodStatusPhase.EVICTED) { - return "Evicted"; - } - - if (this.metadata.deletionTimestamp) { - return "Terminating"; - } - - return this.getStatusPhase() || "Waiting"; - } - - getStatusPhase() { - return this.status?.phase; - } - - getConditions() { - return this.status?.conditions ?? []; - } - - getVolumes() { - return this.spec?.volumes ?? []; - } - - getSecrets(): string[] { - return this.getVolumes() - .map(vol => vol.secret?.secretName) - .filter(isDefined); - } - - getNodeSelectors(): string[] { - return Object.entries(this.spec?.nodeSelector ?? {}) - .map(values => values.join(": ")); - } - - getTolerations() { - return this.spec?.tolerations ?? []; - } - - getAffinity(): Affinity { - return this.spec?.affinity ?? {}; - } - - hasIssues() { - for (const { type, status } of this.getConditions()) { - if (type === "Ready" && status !== "True") { - return true; - } - } - - for (const { state } of this.getContainerStatuses()) { - if (state?.waiting?.reason === "CrashLookBackOff") { - return true; - } - } - - return this.getStatusPhase() !== "Running"; - } - - getLivenessProbe(container: Container) { - return this.getProbe(container, container.livenessProbe); - } - - getReadinessProbe(container: Container) { - return this.getProbe(container, container.readinessProbe); - } - - getStartupProbe(container: Container) { - return this.getProbe(container, container.startupProbe); - } - - private getProbe(container: Container, probe: Probe | undefined): string[] { - const probeItems: string[] = []; - - if (!probe) { - return probeItems; - } - - const { - httpGet, - exec, - tcpSocket, - initialDelaySeconds = 0, - timeoutSeconds = 0, - periodSeconds = 0, - successThreshold = 0, - failureThreshold = 0, - } = probe; - - // HTTP Request - if (httpGet) { - const { path = "", port, host = "", scheme = "HTTP" } = httpGet; - const resolvedPort = typeof port === "number" - ? port - // Try and find the port number associated witht the name or fallback to the name itself - : container.ports?.find(containerPort => containerPort.name === port)?.containerPort || port; - - probeItems.push( - "http-get", - `${scheme.toLowerCase()}://${host}:${resolvedPort}${path}`, - ); - } - - // Command - if (exec?.command) { - probeItems.push(`exec [${exec.command.join(" ")}]`); - } - - // TCP Probe - if (tcpSocket?.port) { - probeItems.push(`tcp-socket :${tcpSocket.port}`); - } - - probeItems.push( - `delay=${initialDelaySeconds}s`, - `timeout=${timeoutSeconds}s`, - `period=${periodSeconds}s`, - `#success=${successThreshold}`, - `#failure=${failureThreshold}`, - ); - - return probeItems; - } - - getNodeName(): string | undefined { - return this.spec?.nodeName; - } - - getSelectedNodeOs(): string | undefined { - return this.spec?.nodeSelector?.["kubernetes.io/os"] || this.spec?.nodeSelector?.["beta.kubernetes.io/os"]; - } - - getIPs(): string[] { - const podIPs = this.status?.podIPs ?? []; - - return podIPs.map(value => value.ip); - } -} diff --git a/src/common/k8s-api/endpoints/priority-class.api.injectable.ts b/src/common/k8s-api/endpoints/priority-class.api.injectable.ts deleted file mode 100644 index e21a85a290..0000000000 --- a/src/common/k8s-api/endpoints/priority-class.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { PriorityClassApi } from "./priority-class.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const priorityClassApiInjectable = getInjectable({ - id: "priority-class-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "PriorityClassApi is only available in certain environments"); - - return new PriorityClassApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default priorityClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/priority-class.api.ts b/src/common/k8s-api/endpoints/priority-class.api.ts deleted file mode 100644 index 1d181b6038..0000000000 --- a/src/common/k8s-api/endpoints/priority-class.api.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { PreemptionPolicy } from "./types/preemption-policy"; - -export interface PriorityClassData extends KubeJsonApiData, void, void> { - description?: string; - globalDefault?: boolean; - preemptionPolicy?: PreemptionPolicy; - value: number; -} - -export class PriorityClass extends KubeObject< - ClusterScopedMetadata, - void, - void -> { - static readonly kind = "PriorityClass"; - static readonly namespaced = false; - static readonly apiBase = "/apis/scheduling.k8s.io/v1/priorityclasses"; - - description?: string; - globalDefault?: boolean; - preemptionPolicy?: PreemptionPolicy; - value?: number; - - constructor({ description, globalDefault, preemptionPolicy, value, ...rest }: PriorityClassData) { - super(rest); - this.description = description; - this.globalDefault = globalDefault; - this.preemptionPolicy = preemptionPolicy; - this.value = value; - } - - getDescription() { - return this.description || ""; - } - - getGlobalDefault() { - return (this.globalDefault || false).toString(); - } - - getPreemptionPolicy() { - return this.preemptionPolicy || "PreemptLowerPriority"; - } - - getValue() { - return this.value; - } -} - -export class PriorityClassApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: PriorityClass, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/replica-set.api.injectable.ts b/src/common/k8s-api/endpoints/replica-set.api.injectable.ts deleted file mode 100644 index c6c75d8192..0000000000 --- a/src/common/k8s-api/endpoints/replica-set.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ReplicaSetApi } from "./replica-set.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const replicaSetApiInjectable = getInjectable({ - id: "replica-set-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "replicaSetApi is only available in certain environments"); - - return new ReplicaSetApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default replicaSetApiInjectable; diff --git a/src/common/k8s-api/endpoints/replica-set.api.ts b/src/common/k8s-api/endpoints/replica-set.api.ts deleted file mode 100644 index 1fee553b8d..0000000000 --- a/src/common/k8s-api/endpoints/replica-set.api.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { PodTemplateSpec } from "./types/pod-template-spec"; - -export class ReplicaSetApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: ReplicaSet, - }); - } - - protected getScaleApiUrl(params: { namespace: string; name: string }) { - return `${this.getUrl(params)}/scale`; - } - - async getReplicas(params: { namespace: string; name: string }): Promise { - const { status } = await this.request.get(this.getScaleApiUrl(params)); - - return (status as { replicas: number })?.replicas; - } - - scale(params: { namespace: string; name: string }, replicas: number) { - return this.request.put(this.getScaleApiUrl(params), { - data: { - metadata: params, - spec: { - replicas, - }, - }, - }); - } -} - -export interface ReplicaSetSpec { - replicas?: number; - selector: LabelSelector; - template?: PodTemplateSpec; - minReadySeconds?: number; -} - -export interface ReplicaSetStatus extends KubeObjectStatus { - replicas: number; - fullyLabeledReplicas?: number; - readyReplicas?: number; - availableReplicas?: number; - observedGeneration?: number; -} - -export class ReplicaSet extends KubeObject< - NamespaceScopedMetadata, - ReplicaSetStatus, - ReplicaSetSpec -> { - static kind = "ReplicaSet"; - static namespaced = true; - static apiBase = "/apis/apps/v1/replicasets"; - - getSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.selector.matchLabels); - } - - getNodeSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.template?.spec?.nodeSelector); - } - - getTemplateLabels(): string[] { - return KubeObject.stringifyLabels(this.spec.template?.metadata?.labels); - } - - getTolerations() { - return this.spec.template?.spec?.tolerations ?? []; - } - - getAffinity() { - return this.spec.template?.spec?.affinity; - } - - getAffinityNumber() { - return Object.keys(this.getAffinity() ?? {}).length; - } - - getDesired() { - return this.spec.replicas ?? 0; - } - - getCurrent() { - return this.status?.availableReplicas ?? 0; - } - - getReady() { - return this.status?.readyReplicas ?? 0; - } - - getImages() { - const containers = this.spec.template?.spec?.containers ?? []; - - return containers.map(container => container.image); - } -} diff --git a/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts b/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts deleted file mode 100644 index 49271fb6d2..0000000000 --- a/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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 { Patch } from "rfc6902"; -import apiBaseInjectable from "../../api-base.injectable"; -import type { AsyncResult } from "../../../utils/async-result"; -import type { KubeJsonApiData } from "../../kube-json-api"; - -export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise>; - -const requestKubeObjectPatchInjectable = getInjectable({ - id: "request-kube-object-patch", - instantiate: (di): RequestKubeObjectPatch => { - const apiBase = di.inject(apiBaseInjectable); - - return async (name, kind, ns, patch) => { - const result = await apiBase.patch("/stack", { - data: { - name, - kind, - ns, - patch, - }, - }) as AsyncResult; - - if (!result.callWasSuccessful) { - return result; - } - - try { - const response = JSON.parse(result.response); - - return { - callWasSuccessful: true, - response, - }; - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } - }; - }, -}); - -export default requestKubeObjectPatchInjectable; diff --git a/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts b/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts deleted file mode 100644 index 1891a779cf..0000000000 --- a/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 apiBaseInjectable from "../../api-base.injectable"; -import type { AsyncResult } from "../../../utils/async-result"; -import type { KubeJsonApiData } from "../../kube-json-api"; - -export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise>; - -const requestKubeObjectCreationInjectable = getInjectable({ - id: "request-kube-object-creation", - instantiate: (di): RequestKubeObjectCreation => { - const apiBase = di.inject(apiBaseInjectable); - - return async (data) => { - const result = await apiBase.post("/stack", { data }) as AsyncResult; - - if (!result.callWasSuccessful) { - return result; - } - - try { - const response = JSON.parse(result.response); - - return { - callWasSuccessful: true, - response, - }; - } catch (error) { - return { - callWasSuccessful: false, - error: String(error), - }; - } - }; - }, -}); - -export default requestKubeObjectCreationInjectable; diff --git a/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts b/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts deleted file mode 100644 index a82fa3abd9..0000000000 --- a/src/common/k8s-api/endpoints/resource-quota.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ResourceQuotaApi } from "./resource-quota.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const resourceQuotaApiInjectable = getInjectable({ - id: "resource-quota-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "resourceQuotaApi is only available in certain environments"); - - return new ResourceQuotaApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default resourceQuotaApiInjectable; diff --git a/src/common/k8s-api/endpoints/resource-quota.api.ts b/src/common/k8s-api/endpoints/resource-quota.api.ts deleted file mode 100644 index 9316f9d932..0000000000 --- a/src/common/k8s-api/endpoints/resource-quota.api.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export type IResourceQuotaValues = Partial> & { - // Compute Resource Quota - "limits.cpu"?: string; - "limits.memory"?: string; - "requests.cpu"?: string; - "requests.memory"?: string; - - // Storage Resource Quota - "requests.storage"?: string; - "persistentvolumeclaims"?: string; - - // Object Count Quota - "count/pods"?: string; - "count/persistentvolumeclaims"?: string; - "count/services"?: string; - "count/secrets"?: string; - "count/configmaps"?: string; - "count/replicationcontrollers"?: string; - "count/deployments.apps"?: string; - "count/replicasets.apps"?: string; - "count/statefulsets.apps"?: string; - "count/jobs.batch"?: string; - "count/cronjobs.batch"?: string; - "count/deployments.extensions"?: string; -}; - -export interface ResourceQuotaSpec { - hard: IResourceQuotaValues; - scopeSelector?: { - matchExpressions: { - operator: string; - scopeName: string; - values: string[]; - }[]; - }; -} - -export interface ResourceQuotaStatus { - hard: IResourceQuotaValues; - used: IResourceQuotaValues; -} - -export class ResourceQuota extends KubeObject< - NamespaceScopedMetadata, - ResourceQuotaStatus, - ResourceQuotaSpec -> { - static readonly kind = "ResourceQuota"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/resourcequotas"; - - getScopeSelector() { - return this.spec.scopeSelector?.matchExpressions ?? []; - } -} - -export class ResourceQuotaApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: ResourceQuota, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/role-binding.api.injectable.ts b/src/common/k8s-api/endpoints/role-binding.api.injectable.ts deleted file mode 100644 index 489b20401a..0000000000 --- a/src/common/k8s-api/endpoints/role-binding.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { RoleBindingApi } from "./role-binding.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const roleBindingApiInjectable = getInjectable({ - id: "role-binding-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleBindingApi is only available in certain environments"); - - return new RoleBindingApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default roleBindingApiInjectable; diff --git a/src/common/k8s-api/endpoints/role-binding.api.ts b/src/common/k8s-api/endpoints/role-binding.api.ts deleted file mode 100644 index 3923efee89..0000000000 --- a/src/common/k8s-api/endpoints/role-binding.api.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { RoleRef } from "./types/role-ref"; -import type { Subject } from "./types/subject"; - -export interface RoleBindingData extends KubeJsonApiData, void, void> { - subjects?: Subject[]; - roleRef: RoleRef; -} - -export class RoleBinding extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static readonly kind = "RoleBinding"; - static readonly namespaced = true; - static readonly apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings"; - - subjects?: Subject[]; - roleRef: RoleRef; - - constructor({ subjects, roleRef, ...rest }: RoleBindingData) { - super(rest); - this.subjects = subjects; - this.roleRef = roleRef; - } - - getSubjects() { - return this.subjects || []; - } - - getSubjectNames(): string { - return this.getSubjects().map(subject => subject.name).join(", "); - } -} - -export class RoleBindingApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: RoleBinding, - }); - } -} diff --git a/src/common/k8s-api/endpoints/role.api.injectable.ts b/src/common/k8s-api/endpoints/role.api.injectable.ts deleted file mode 100644 index d91d6a1fb8..0000000000 --- a/src/common/k8s-api/endpoints/role.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { RoleApi } from "./role.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const roleApiInjectable = getInjectable({ - id: "role-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleApi is only available in certain environments"); - - return new RoleApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default roleApiInjectable; diff --git a/src/common/k8s-api/endpoints/role.api.ts b/src/common/k8s-api/endpoints/role.api.ts deleted file mode 100644 index d6bd0e79d3..0000000000 --- a/src/common/k8s-api/endpoints/role.api.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { PolicyRule } from "./types/policy-rule"; - -export interface RoleData extends KubeJsonApiData, void, void> { - rules?: PolicyRule[]; -} - -export class Role extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static readonly kind = "Role"; - static readonly namespaced = true; - static readonly apiBase = "/apis/rbac.authorization.k8s.io/v1/roles"; - rules?: PolicyRule[]; - - constructor({ rules, ...rest }: RoleData) { - super(rest); - this.rules = rules; - } - - getRules() { - return this.rules || []; - } -} - -export class RoleApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: Role, - }); - } -} diff --git a/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts b/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts deleted file mode 100644 index 3fbeab9a09..0000000000 --- a/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { RuntimeClassApi } from "./runtime-class.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const runtimeClassApiInjectable = getInjectable({ - id: "runtime-class-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "RuntimeClassApi is only available in certain environments"); - - return new RuntimeClassApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default runtimeClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/runtime-class.api.ts b/src/common/k8s-api/endpoints/runtime-class.api.ts deleted file mode 100644 index 16e3cefab5..0000000000 --- a/src/common/k8s-api/endpoints/runtime-class.api.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; -import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope, Toleration } from "../kube-object"; -import { KubeObject } from "../kube-object"; - -export interface RuntimeClassData extends KubeJsonApiData, void, void> { - handler: string; - overhead?: RuntimeClassOverhead; - scheduling?: RuntimeClassScheduling; -} - -export interface RuntimeClassOverhead { - podFixed?: string; -} - -export interface RuntimeClassScheduling { - nodeSelector?: Partial>; - tolerations?: Toleration[]; -} - -export class RuntimeClass extends KubeObject< - ClusterScopedMetadata, - void, - void -> { - static readonly kind = "RuntimeClass"; - static readonly namespaced = false; - static readonly apiBase = "/apis/node.k8s.io/v1/runtimeclasses"; - - handler: string; - overhead?: RuntimeClassOverhead; - scheduling?: RuntimeClassScheduling; - - constructor({ handler, overhead, scheduling, ...rest }: RuntimeClassData) { - super(rest); - this.handler = handler; - this.overhead = overhead; - this.scheduling = scheduling; - } - - getHandler() { - return this.handler; - } - - getPodFixed() { - return this.overhead?.podFixed ?? ""; - } - - getNodeSelectors(): string[] { - return Object.entries(this.scheduling?.nodeSelector ?? {}) - .map(values => values.join(": ")); - } - - getTolerations() { - return this.scheduling?.tolerations ?? []; - } -} - -export class RuntimeClassApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - objectConstructor: RuntimeClass, - ...opts, - }); - } -} diff --git a/src/common/k8s-api/endpoints/secret.api.injectable.ts b/src/common/k8s-api/endpoints/secret.api.injectable.ts deleted file mode 100644 index c4173987da..0000000000 --- a/src/common/k8s-api/endpoints/secret.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { SecretApi } from "./secret.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const secretApiInjectable = getInjectable({ - id: "secret-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "secretApi is only available in certain environments"); - - return new SecretApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default secretApiInjectable; diff --git a/src/common/k8s-api/endpoints/secret.api.ts b/src/common/k8s-api/endpoints/secret.api.ts deleted file mode 100644 index a5a3b5eb1c..0000000000 --- a/src/common/k8s-api/endpoints/secret.api.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { KubeJsonApiData } from "../kube-json-api"; -import { autoBind } from "../../utils"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export enum SecretType { - Opaque = "Opaque", - ServiceAccountToken = "kubernetes.io/service-account-token", - Dockercfg = "kubernetes.io/dockercfg", - DockerConfigJson = "kubernetes.io/dockerconfigjson", - BasicAuth = "kubernetes.io/basic-auth", - SSHAuth = "kubernetes.io/ssh-auth", - TLS = "kubernetes.io/tls", - BootstrapToken = "bootstrap.kubernetes.io/token", -} - -export const reverseSecretTypeMap = { - [SecretType.Opaque]: "Opaque", - [SecretType.ServiceAccountToken]: "ServiceAccountToken", - [SecretType.Dockercfg]: "Dockercfg", - [SecretType.DockerConfigJson]: "DockerConfigJson", - [SecretType.BasicAuth]: "BasicAuth", - [SecretType.SSHAuth]: "SSHAuth", - [SecretType.TLS]: "TLS", - [SecretType.BootstrapToken]: "BootstrapToken", -}; - -export interface SecretReference { - name: string; - namespace?: string; -} - -export interface SecretData extends KubeJsonApiData, void, void> { - type: SecretType; - data?: Partial>; -} - -export class Secret extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static readonly kind = "Secret"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/secrets"; - - type: SecretType; - data: Partial>; - - constructor({ data = {}, type, ...rest }: SecretData) { - super(rest); - autoBind(this); - - this.data = data; - this.type = type; - } - - getKeys(): string[] { - return Object.keys(this.data); - } - - getToken() { - return this.data.token; - } -} - -export class SecretApi extends KubeApi { - constructor(deps: KubeApiDependencies, options: DerivedKubeApiOptions = {}) { - super(deps, { - ...options, - objectConstructor: Secret, - }); - } -} diff --git a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts deleted file mode 100644 index 8034b9d47f..0000000000 --- a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { SelfSubjectRulesReviewApi } from "./self-subject-rules-reviews.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const selfSubjectRulesReviewApiInjectable = getInjectable({ - id: "self-subject-rules-review-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "selfSubjectRulesReviewApi is only available in certain environments"); - - return new SelfSubjectRulesReviewApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default selfSubjectRulesReviewApiInjectable; diff --git a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts b/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts deleted file mode 100644 index 2c73d89e77..0000000000 --- a/src/common/k8s-api/endpoints/self-subject-rules-reviews.api.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export class SelfSubjectRulesReviewApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: SelfSubjectRulesReview, - }); - } - - create({ namespace = "default" }) { - return super.create({}, { - spec: { - namespace, - }, - }); - } -} - -export interface ISelfSubjectReviewRule { - verbs: string[]; - apiGroups?: string[]; - resources?: string[]; - resourceNames?: string[]; - nonResourceURLs?: string[]; -} - -export interface SelfSubjectRulesReview { - spec: { - namespace?: string; - }; - status: { - resourceRules: ISelfSubjectReviewRule[]; - nonResourceRules: ISelfSubjectReviewRule[]; - incomplete: boolean; - }; -} - -export class SelfSubjectRulesReview extends KubeObject { - static kind = "SelfSubjectRulesReview"; - static namespaced = false; - static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews"; - - getResourceRules() { - const rules = this.status && this.status.resourceRules || []; - - return rules.map(rule => this.normalize(rule)); - } - - getNonResourceRules() { - const rules = this.status && this.status.nonResourceRules || []; - - return rules.map(rule => this.normalize(rule)); - } - - protected normalize(rule: ISelfSubjectReviewRule): ISelfSubjectReviewRule { - const { apiGroups = [], resourceNames = [], verbs = [], nonResourceURLs = [], resources = [] } = rule; - - return { - apiGroups, - nonResourceURLs, - resourceNames, - verbs, - resources: resources.map((resource, index) => { - const apiGroup = apiGroups.length >= index + 1 ? apiGroups[index] : apiGroups.slice(-1)[0]; - const separator = apiGroup == "" ? "" : "."; - - return resource + separator + apiGroup; - }), - }; - } -} - diff --git a/src/common/k8s-api/endpoints/service-account.api.injectable.ts b/src/common/k8s-api/endpoints/service-account.api.injectable.ts deleted file mode 100644 index 70a477ac54..0000000000 --- a/src/common/k8s-api/endpoints/service-account.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ServiceAccountApi } from "./service-account.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const serviceAccountApiInjectable = getInjectable({ - id: "service-account-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceAccountApi is only available in certain environments"); - - return new ServiceAccountApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default serviceAccountApiInjectable; diff --git a/src/common/k8s-api/endpoints/service-account.api.ts b/src/common/k8s-api/endpoints/service-account.api.ts deleted file mode 100644 index e92ea667d9..0000000000 --- a/src/common/k8s-api/endpoints/service-account.api.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectMetadata, KubeObjectScope, LocalObjectReference, NamespaceScopedMetadata, ObjectReference } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; - -export interface ServiceAccountData extends KubeJsonApiData, void, void> { - automountServiceAccountToken?: boolean; - imagePullSecrets?: LocalObjectReference[]; - secrets?: ObjectReference[]; -} - -export class ServiceAccount extends KubeObject< - NamespaceScopedMetadata, - void, - void -> { - static readonly kind = "ServiceAccount"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/serviceaccounts"; - - automountServiceAccountToken?: boolean; - imagePullSecrets?: LocalObjectReference[]; - secrets?: ObjectReference[]; - - constructor({ - automountServiceAccountToken, - imagePullSecrets, - secrets, - ...rest - }: ServiceAccountData) { - super(rest); - this.automountServiceAccountToken = automountServiceAccountToken; - this.imagePullSecrets = imagePullSecrets; - this.secrets = secrets; - } - - getSecrets() { - return this.secrets || []; - } - - getImagePullSecrets() { - return this.imagePullSecrets || []; - } -} - -export class ServiceAccountApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: ServiceAccount, - }); - } -} diff --git a/src/common/k8s-api/endpoints/service.api.injectable.ts b/src/common/k8s-api/endpoints/service.api.injectable.ts deleted file mode 100644 index 78a0f744de..0000000000 --- a/src/common/k8s-api/endpoints/service.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { ServiceApi } from "./service.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const serviceApiInjectable = getInjectable({ - id: "service-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceApi is only available in certain environments"); - - return new ServiceApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default serviceApiInjectable; diff --git a/src/common/k8s-api/endpoints/service.api.ts b/src/common/k8s-api/endpoints/service.api.ts deleted file mode 100644 index f574bea53d..0000000000 --- a/src/common/k8s-api/endpoints/service.api.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; - -export interface ServicePort { - name?: string; - protocol: string; - port: number; - targetPort: number; - nodePort?: number; -} - -export class ServicePort { - constructor(data: ServicePort) { - Object.assign(this, data); - } - - toString() { - if (this.nodePort) { - return `${this.port}:${this.nodePort}/${this.protocol}`; - } else { - return `${this.port}${this.port === this.targetPort ? "" : `:${this.targetPort}`}/${this.protocol}`; - } - } -} - -export interface ServiceSpec { - type: string; - clusterIP: string; - clusterIPs?: string[]; - externalTrafficPolicy?: string; - externalName?: string; - loadBalancerIP?: string; - loadBalancerSourceRanges?: string[]; - sessionAffinity: string; - selector: Partial>; - ports: ServicePort[]; - healthCheckNodePort?: number; - externalIPs?: string[]; // https://kubernetes.io/docs/concepts/services-networking/service/#external-ips - topologyKeys?: string[]; - ipFamilies?: string[]; - ipFamilyPolicy?: string; - allocateLoadBalancerNodePorts?: boolean; - loadBalancerClass?: string; - internalTrafficPolicy?: string; -} - -export interface ServiceStatus { - loadBalancer?: { - ingress?: { - ip?: string; - hostname?: string; - }[]; - }; -} - -export class Service extends KubeObject< - NamespaceScopedMetadata, - ServiceStatus, - ServiceSpec -> { - static readonly kind = "Service"; - static readonly namespaced = true; - static readonly apiBase = "/api/v1/services"; - - getClusterIp() { - return this.spec.clusterIP; - } - - getClusterIps() { - return this.spec.clusterIPs || []; - } - - getExternalIps() { - const lb = this.getLoadBalancer(); - - if (lb?.ingress) { - return lb.ingress.map(val => val.ip || val.hostname); - } - - if (Array.isArray(this.spec?.externalIPs)) { - return this.spec.externalIPs; - } - - return []; - } - - getType() { - return this.spec.type || "-"; - } - - getSelector(): string[] { - if (!this.spec.selector) return []; - - return Object.entries(this.spec.selector).map(val => val.join("=")); - } - - getPorts(): ServicePort[] { - const ports = this.spec.ports || []; - - return ports.map(p => new ServicePort(p)); - } - - getLoadBalancer() { - return this.status?.loadBalancer; - } - - isActive() { - return this.getType() !== "LoadBalancer" || this.getExternalIps().length > 0; - } - - getStatus() { - return this.isActive() ? "Active" : "Pending"; - } - - getIpFamilies() { - return this.spec.ipFamilies || []; - } - - getIpFamilyPolicy() { - return this.spec.ipFamilyPolicy || ""; - } -} - -export class ServiceApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: Service, - }); - } -} diff --git a/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts b/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts deleted file mode 100644 index 1dd58fde3c..0000000000 --- a/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { StatefulSetApi } from "./stateful-set.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const statefulSetApiInjectable = getInjectable({ - id: "stateful-set-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "statefulSetApi is only available in certain environments"); - - return new StatefulSetApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default statefulSetApiInjectable; diff --git a/src/common/k8s-api/endpoints/stateful-set.api.ts b/src/common/k8s-api/endpoints/stateful-set.api.ts deleted file mode 100644 index e91a856262..0000000000 --- a/src/common/k8s-api/endpoints/stateful-set.api.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; - -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { LabelSelector, NamespaceScopedMetadata } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { PodTemplateSpec } from "./types/pod-template-spec"; -import type { PersistentVolumeClaimTemplateSpec } from "./types/persistent-volume-claim-template-spec"; - -export class StatefulSetApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { - super(deps, { - ...opts ?? {}, - objectConstructor: StatefulSet, - }); - } - - protected getScaleApiUrl(params: { namespace: string; name: string }) { - return `${this.getUrl(params)}/scale`; - } - - getReplicas(params: { namespace: string; name: string }): Promise { - return this.request - .get(this.getScaleApiUrl(params)) - .then(({ status }: any) => status?.replicas); - } - - scale(params: { namespace: string; name: string }, replicas: number) { - return this.request.patch(this.getScaleApiUrl(params), { - data: { - spec: { - replicas, - }, - }, - }, - { - headers: { - "content-type": "application/merge-patch+json", - }, - }); - } - - restart(params: { namespace: string; name: string }) { - return this.request.patch(this.getUrl(params), { - data: { - spec: { - template: { - metadata: { - annotations: { "kubectl.kubernetes.io/restartedAt" : moment.utc().format() }, - }, - }, - }, - }, - }, - { - headers: { - "content-type": "application/strategic-merge-patch+json", - }, - }); - } -} - -export interface StatefulSetSpec { - serviceName: string; - replicas: number; - selector: LabelSelector; - template: PodTemplateSpec; - volumeClaimTemplates: PersistentVolumeClaimTemplateSpec[]; -} - -export interface StatefulSetStatus { - observedGeneration: number; - replicas: number; - currentReplicas: number; - readyReplicas: number; - currentRevision: string; - updateRevision: string; - collisionCount: number; -} - -export class StatefulSet extends KubeObject< - NamespaceScopedMetadata, - StatefulSetStatus, - StatefulSetSpec -> { - static readonly kind = "StatefulSet"; - static readonly namespaced = true; - static readonly apiBase = "/apis/apps/v1/statefulsets"; - - getSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.selector.matchLabels); - } - - getNodeSelectors(): string[] { - return KubeObject.stringifyLabels(this.spec.template.spec?.nodeSelector); - } - - getTemplateLabels(): string[] { - return KubeObject.stringifyLabels(this.spec.template.metadata?.labels); - } - - getTolerations() { - return this.spec.template.spec?.tolerations ?? []; - } - - getAffinity() { - return this.spec.template.spec?.affinity ?? {}; - } - - getAffinityNumber() { - return Object.keys(this.getAffinity()).length; - } - - getReplicas() { - return this.spec.replicas || 0; - } - - getImages() { - const containers = this.spec.template?.spec?.containers ?? []; - - return containers.map(container => container.image); - } -} diff --git a/src/common/k8s-api/endpoints/storage-class.api.injectable.ts b/src/common/k8s-api/endpoints/storage-class.api.injectable.ts deleted file mode 100644 index e65247aa8d..0000000000 --- a/src/common/k8s-api/endpoints/storage-class.api.injectable.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import { StorageClassApi } from "./storage-class.api"; -import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; -import loggerInjectable from "../../logger.injectable"; -import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; - -const storageClassApiInjectable = getInjectable({ - id: "storage-class-api", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "storageClassApi is only available in certain environments"); - - return new StorageClassApi({ - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }); - }, - - injectionToken: kubeApiInjectionToken, -}); - -export default storageClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/storage-class.api.ts b/src/common/k8s-api/endpoints/storage-class.api.ts deleted file mode 100644 index bf121e47db..0000000000 --- a/src/common/k8s-api/endpoints/storage-class.api.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { autoBind } from "../../utils"; -import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; -import { KubeObject } from "../kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; -import { KubeApi } from "../kube-api"; -import type { KubeJsonApiData } from "../kube-json-api"; - -export interface TopologySelectorLabelRequirement { - key: string; - values: string[]; -} - -export interface TopologySelectorTerm { - matchLabelExpressions?: TopologySelectorLabelRequirement[]; -} - -export interface StorageClassData extends KubeJsonApiData, void, void> { - allowVolumeExpansion?: boolean; - allowedTopologies?: TopologySelectorTerm[]; - mountOptions?: string[]; - parameters?: Partial>; - provisioner: string; - reclaimPolicy?: string; - volumeBindingMode?: string; -} - -export class StorageClass extends KubeObject< - ClusterScopedMetadata, - void, - void -> { - static readonly kind = "StorageClass"; - static readonly namespaced = false; - static readonly apiBase = "/apis/storage.k8s.io/v1/storageclasses"; - - allowVolumeExpansion?: boolean; - allowedTopologies: TopologySelectorTerm[]; - mountOptions: string[]; - parameters: Partial>; - provisioner: string; - reclaimPolicy: string; - volumeBindingMode?: string; - - constructor({ - allowVolumeExpansion, - allowedTopologies = [], - mountOptions = [], - parameters = {}, - provisioner, - reclaimPolicy = "Delete", - volumeBindingMode, - ...rest - }: StorageClassData) { - super(rest); - autoBind(this); - this.allowVolumeExpansion = allowVolumeExpansion; - this.allowedTopologies = allowedTopologies; - this.mountOptions = mountOptions; - this.parameters = parameters; - this.provisioner = provisioner; - this.reclaimPolicy = reclaimPolicy; - this.volumeBindingMode = volumeBindingMode; - } - - isDefault() { - const annotations = this.metadata.annotations || {}; - - return ( - annotations["storageclass.kubernetes.io/is-default-class"] === "true" || - annotations["storageclass.beta.kubernetes.io/is-default-class"] === "true" - ); - } - - getVolumeBindingMode() { - return this.volumeBindingMode || "-"; - } - - getReclaimPolicy() { - return this.reclaimPolicy || "-"; - } -} - -export class StorageClassApi extends KubeApi { - constructor(deps: KubeApiDependencies, opts: DerivedKubeApiOptions = {}) { - super(deps, { - ...opts, - objectConstructor: StorageClass, - }); - } -} diff --git a/src/common/k8s-api/endpoints/types/aggregation-rule.ts b/src/common/k8s-api/endpoints/types/aggregation-rule.ts deleted file mode 100644 index 57ebe32264..0000000000 --- a/src/common/k8s-api/endpoints/types/aggregation-rule.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LabelSelector } from "../../kube-object"; - -export interface AggregationRule { - clusterRoleSelectors?: LabelSelector; -} diff --git a/src/common/k8s-api/endpoints/types/capabilities.ts b/src/common/k8s-api/endpoints/types/capabilities.ts deleted file mode 100644 index baea13c688..0000000000 --- a/src/common/k8s-api/endpoints/types/capabilities.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Adds and removes POSIX capabilities from running containers. - */ -export interface Capabilities { - /** - * Added capabilities - */ - add?: string[]; - - /** - * Removed capabilities - */ - drop?: string[]; -} diff --git a/src/common/k8s-api/endpoints/types/container-port.ts b/src/common/k8s-api/endpoints/types/container-port.ts deleted file mode 100644 index 9ccb635e98..0000000000 --- a/src/common/k8s-api/endpoints/types/container-port.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface ContainerPort { - containerPort: number; - hostIP?: string; - hostPort?: number; - name?: string; - protocol?: "UDP" | "TCP" | "SCTP"; -} diff --git a/src/common/k8s-api/endpoints/types/container.ts b/src/common/k8s-api/endpoints/types/container.ts deleted file mode 100644 index bccf45eb64..0000000000 --- a/src/common/k8s-api/endpoints/types/container.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Lifecycle } from "./lifecycle"; -import type { ResourceRequirements } from "./resource-requirements"; -import type { SecurityContext } from "./security-context"; -import type { Probe } from "./probe"; -import type { VolumeDevice } from "./volume-device"; -import type { VolumeMount } from "./volume-mount"; -import type { ContainerPort } from "./container-port"; -import type { EnvFromSource } from "./env-from-source"; -import type { EnvVar } from "./env-var"; - -/** - * A single application container that you want to run within a pod. - */ -export interface Container { - /** - * Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable - * references `$(VAR_NAME)` are expanded using the container's environment. - * - * If a variable cannot be resolved, the reference in the input string will be unchanged. - * Double `$$` are reduced to a single `$`, which allows for escaping the `$(VAR_NAME)` syntax: - * i.e. `"$$(VAR_NAME)"` will produce the string literal `"$(VAR_NAME)`". - * - * Escaped references will never be expanded, regardless of whether the variable exists or not. - * Cannot be updated. - * - * More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell - */ - args?: string[]; - - /** - * Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this - * is not provided. Variable references `$(VAR_NAME)` are expanded using the container's - * environment. - * - * If a variable cannot be resolved, the reference in the input string will be unchanged. - * Double `$$` are reduced to a single `$`, which allows for escaping the `$(VAR_NAME)` syntax: - * i.e. `"$$(VAR_NAME)"` will produce the string literal `"$(VAR_NAME)`". - * - * Escaped references will never be expanded, regardless of whether the variable exists or not. - * Cannot be updated. - * - * More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell - */ - command?: string[]; - - /** - * List of environment variables to set in the container. Cannot be updated. - */ - env?: EnvVar[]; - - /** - * List of sources to populate environment variables in the container. The keys defined within a - * source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the - * container is starting. - * - * When a key exists in multiple sources, the value associated with the last source will take - * precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be - * updated. - */ - envFrom?: EnvFromSource[]; - - /** - * Docker image name. - * - * More info: https://kubernetes.io/docs/concepts/containers/images - */ - image?: string; - - /** - * Image pull policy. Defaults to `"Always"` if :latest tag is specified, or `"IfNotPresent"` - * otherwise. Cannot be updated. - * - * More info: https://kubernetes.io/docs/concepts/containers/images#updating-images - */ - imagePullPolicy?: "Always" | "Never" | "IfNotPresent"; - - lifecycle?: Lifecycle; - livenessProbe?: Probe; - - /** - * Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique - * name. Cannot be updated. - */ - name: string; - - /** - * List of ports to expose from the container. Exposing a port here gives the system additional - * information about the network connections a container uses, but is primarily informational. - * Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is - * listening on the default `"0.0.0.0"` address inside a container will be accessible from the - * network. Cannot be updated. - */ - ports?: ContainerPort[]; - - readinessProbe?: Probe; - resources?: ResourceRequirements; - securityContext?: SecurityContext; - startupProbe?: Probe; - - /** - * Whether this container should allocate a buffer for stdin in the container runtime. If this is - * not set, reads from stdin in the container will always result in EOF. - * - * @default false - */ - stdin?: boolean; - - /** - * Whether the container runtime should close the stdin channel after it has been opened by a - * single attach. When stdin is true the stdin stream will remain open across multiple attach - * sessions. - * - * If stdinOnce is set to true, stdin is opened on container start, is empty until the first - * client attaches to stdin, and then remains open and accepts data until the client disconnects, - * at which time stdin is closed and remains closed until the container is restarted. - * - * If this flag is false, a container processes that reads from stdin will never receive an EOF. - * - * @default false - */ - stdinOnce?: boolean; - - /** - * Path at which the file to which the container's termination message will be written - * is mounted into the container's filesystem. Message written is intended to be brief final - * status, such as an assertion failure message. - * - * Will be truncated by the node if greater than 4096 bytes. - * The total message length across all containers will be limited to 12kb. Cannot be updated. - * - * @default "/dev/termination-log" - */ - terminationMessagePath?: string; - - /** - * Indicate how the termination message should be populated. - * - * - `File`: will use the contents of {@link terminationMessagePath} to populate the container - * status message on both success and failure. - * - * - `FallbackToLogsOnError`: will use the last chunk of container log output if the - * termination message file is empty and the container exited with an error. - * - * The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Cannot be updated. - * - * @default "File" - */ - terminationMessagePolicy?: "File" | "FallbackToLogsOnError"; - - /** - * Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. - * - * @default false - */ - tty?: boolean; - - /** - * volumeDevices is the list of block devices to be used by the container. - */ - volumeDevices?: VolumeDevice[]; - - /** - * Pod volumes to mount into the container's filesystem. Cannot be updated. - */ - volumeMounts?: VolumeMount[]; - - /** - * Container's working directory. If not specified, the container runtime's default will be used, - * which might be configured in the container image. Cannot be updated. - */ - workingDir?: string; -} diff --git a/src/common/k8s-api/endpoints/types/env-from-source.ts b/src/common/k8s-api/endpoints/types/env-from-source.ts deleted file mode 100644 index fa87b2fac8..0000000000 --- a/src/common/k8s-api/endpoints/types/env-from-source.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { EnvSource } from "./env-source"; - -export interface EnvFromSource { - configMapRef?: EnvSource; - /** - * An identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. - */ - prefix?: string; - secretRef?: EnvSource; -} diff --git a/src/common/k8s-api/endpoints/types/env-source.ts b/src/common/k8s-api/endpoints/types/env-source.ts deleted file mode 100644 index 2a16ee2ada..0000000000 --- a/src/common/k8s-api/endpoints/types/env-source.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LocalObjectReference } from "../../kube-object"; - -export interface EnvSource extends LocalObjectReference { - /** - * Whether the object must be defined - */ - optional?: boolean; -} diff --git a/src/common/k8s-api/endpoints/types/env-var-key-selector.ts b/src/common/k8s-api/endpoints/types/env-var-key-selector.ts deleted file mode 100644 index 3aca6d79bd..0000000000 --- a/src/common/k8s-api/endpoints/types/env-var-key-selector.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface EnvVarKeySelector { - key: string; - name?: string; - optional?: boolean; -} diff --git a/src/common/k8s-api/endpoints/types/env-var-source.ts b/src/common/k8s-api/endpoints/types/env-var-source.ts deleted file mode 100644 index c47c839d18..0000000000 --- a/src/common/k8s-api/endpoints/types/env-var-source.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { EnvVarKeySelector } from "./env-var-key-selector"; -import type { ObjectFieldSelector } from "./object-field-selector"; -import type { ResourceFieldSelector } from "./resource-field-selector"; - -export interface EnvVarSource { - configMapKeyRef?: EnvVarKeySelector; - fieldRef?: ObjectFieldSelector; - resourceFieldRef?: ResourceFieldSelector; - secretKeyRef?: EnvVarKeySelector; -} diff --git a/src/common/k8s-api/endpoints/types/env-var.ts b/src/common/k8s-api/endpoints/types/env-var.ts deleted file mode 100644 index 4a60b69129..0000000000 --- a/src/common/k8s-api/endpoints/types/env-var.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { EnvVarSource } from "./env-var-source"; - -export interface EnvVar { - name: string; - value?: string; - valueFrom?: EnvVarSource; -} diff --git a/src/common/k8s-api/endpoints/types/exec-action.ts b/src/common/k8s-api/endpoints/types/exec-action.ts deleted file mode 100644 index abfd1947cb..0000000000 --- a/src/common/k8s-api/endpoints/types/exec-action.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * ExecAction describes a "run in container" action. - */ -export interface ExecAction { - /** - * Command is the command line to execute inside the container, the working directory for the - * command is root ('\\') in the container's filesystem. The command is simply exec'd, it is not - * run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, - * you need to explicitly call out to that shell. - * - * Exit status of 0 is treated as live/healthy and non-zero is unhealthy. - */ - command?: string[]; -} diff --git a/src/common/k8s-api/endpoints/types/external-documentation.ts b/src/common/k8s-api/endpoints/types/external-documentation.ts deleted file mode 100644 index b433785323..0000000000 --- a/src/common/k8s-api/endpoints/types/external-documentation.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface ExternalDocumentation { - description?: string; - url?: string; -} diff --git a/src/common/k8s-api/endpoints/types/handler.ts b/src/common/k8s-api/endpoints/types/handler.ts deleted file mode 100644 index d30e6cd181..0000000000 --- a/src/common/k8s-api/endpoints/types/handler.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ExecAction } from "./exec-action"; -import type { HttpGetAction } from "./http-get-action"; -import type { TcpSocketAction } from "./tcp-socket-action"; - -/** - * Handler defines a specific action that should be taken. - */ -export interface Handler { - exec?: ExecAction; - httpGet?: HttpGetAction; - tcpSocket?: TcpSocketAction; -} diff --git a/src/common/k8s-api/endpoints/types/http-get-action.ts b/src/common/k8s-api/endpoints/types/http-get-action.ts deleted file mode 100644 index 1077ea13e1..0000000000 --- a/src/common/k8s-api/endpoints/types/http-get-action.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { HttpHeader } from "./http-header"; - -/** - * An action based on HTTP Get requests. - */ -export interface HttpGetAction { - /** - * Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead. - */ - host?: string; - - /** - * Custom headers to set in the request. HTTP allows repeated headers. - */ - httpHeaders?: HttpHeader[]; - - /** - * Path to access on the HTTP server. - */ - path?: string; - - /** - * The PORT to request from. - */ - port: string | number; - - /** - * Scheme to use for connecting to the host. - * - * @default "HTTP" - */ - scheme?: string; -} diff --git a/src/common/k8s-api/endpoints/types/http-header.ts b/src/common/k8s-api/endpoints/types/http-header.ts deleted file mode 100644 index d70b42afdb..0000000000 --- a/src/common/k8s-api/endpoints/types/http-header.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * A custom header to be used in HTTP probes and get actions - */ -export interface HttpHeader { - /** - * Field name - */ - name: string; - - /** - * The value of the field - */ - value: string; -} diff --git a/src/common/k8s-api/endpoints/types/index.ts b/src/common/k8s-api/endpoints/types/index.ts deleted file mode 100644 index 6fb52e7403..0000000000 --- a/src/common/k8s-api/endpoints/types/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./aggregation-rule"; -export * from "./capabilities"; -export * from "./container"; -export * from "./container-port"; -export * from "./env-from-source"; -export * from "./env-source"; -export * from "./env-var-key-selector"; -export * from "./env-var-source"; -export * from "./env-var"; -export * from "./exec-action"; -export * from "./handler"; -export * from "./http-get-action"; -export * from "./http-header"; -export * from "./job-template-spec"; -export * from "./lifecycle"; -export * from "./object-field-selector"; -export * from "./persistent-volume-claim-template-spec"; -export * from "./pod-security-context"; -export * from "./pod-template-spec"; -export * from "./policy-rule"; -export * from "./probe"; -export * from "./resource-field-selector"; -export * from "./resource-requirements"; -export * from "./role-ref"; -export * from "./se-linux-options"; -export * from "./seccomp-profile"; -export * from "./subject"; -export * from "./tcp-socket-action"; -export * from "./volume-device"; -export * from "./volume-mount"; -export * from "./windows-security-context-options"; diff --git a/src/common/k8s-api/endpoints/types/job-template-spec.ts b/src/common/k8s-api/endpoints/types/job-template-spec.ts deleted file mode 100644 index b3ae5cceca..0000000000 --- a/src/common/k8s-api/endpoints/types/job-template-spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectScope, KubeTemplateObjectMetadata } from "../../kube-object"; -import type { JobSpec } from "../job.api"; - -export interface JobTemplateSpec { - metadata?: KubeTemplateObjectMetadata; - spec?: JobSpec; -} diff --git a/src/common/k8s-api/endpoints/types/json-schema-props.ts b/src/common/k8s-api/endpoints/types/json-schema-props.ts deleted file mode 100644 index 1c0f18a7d2..0000000000 --- a/src/common/k8s-api/endpoints/types/json-schema-props.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { JsonValue } from "type-fest"; -import type { ExternalDocumentation } from "./external-documentation"; - -export interface JSONSchemaProps { - $ref?: string; - $schema?: string; - additionalItems?: JSONSchemaProps | boolean; - additionalProperties?: JSONSchemaProps | boolean; - allOf?: JSONSchemaProps[]; - anyOf?: JSONSchemaProps[]; - - /** - * default is a default value for undefined object fields. - * Defaulting is a beta feature under the CustomResourceDefaulting feature gate. - * Defaulting requires spec.preserveUnknownFields to be false. - */ - _default?: object; - - definitions?: Partial>; - dependencies?: Partial>; - description?: string; - _enum?: object[]; - example?: JsonValue; - - exclusiveMaximum?: boolean; - exclusiveMinimum?: boolean; - externalDocs?: ExternalDocumentation; - - /** - * format is an OpenAPI v3 format string. - * Unknown formats are ignored. - * - * The following formats are validated: - * - bsonobjectid: a bson object ID, i.e. a 24 characters hex string - * - uri: an URI as parsed by Golang net/url.ParseRequestURI - * - email: an email address as parsed by Golang net/mail.ParseAddress - * - hostname: a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. - * - ipv4: an IPv4 IP as parsed by Golang net.ParseIP - * - ipv6: an IPv6 IP as parsed by Golang net.ParseIP - * - cidr: a CIDR as parsed by Golang net.ParseCIDR - * - mac: a MAC address as parsed by Golang net.ParseMAC - * - uuid: an UUID that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$ - * - uuid3: an UUID3 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$ - * - uuid4: an UUID4 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$ - * - uuid5: an UUID5 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$ - * - isbn: an ISBN10 or ISBN13 number string like "0321751043" or "978-0321751041" - * - isbn10: an ISBN10 number string like "0321751043" - * - isbn13: an ISBN13 number string like "978-0321751041" - * - creditcard: a credit card number defined by the regex ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$ with any non digit characters mixed in - * - ssn: a U.S. social security number following the regex ^\\d{3}[- ]?\\d{2}[- ]?\\d{4}$ - * - hexcolor: an hexadecimal color code like "#FFFFFF: following the regex ^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ - * - rgbcolor: an RGB color code like rgb like "rgb(255,255,2559" - * - byte: base64 encoded binary data - * - password: any kind of string - * - date: a date string like "2006-01-02" as defined by full-date in RFC3339 - * - duration: a duration string like "22 ns" as parsed by Golang time.ParseDuration or compatible with Scala duration format - * - datetime: a date time string like "2014-12-15T19:30:20.000Z" as defined by date-time in RFC3339. - */ - format?: string; - - id?: string; - items?: JSONSchemaProps | JSONSchemaProps[]; - maxItems?: number; - maxLength?: number; - maxProperties?: number; - maximum?: number; - minItems?: number; - minLength?: number; - minProperties?: number; - minimum?: number; - multipleOf?: number; - not?: JSONSchemaProps; - nullable?: boolean; - oneOf?: JSONSchemaProps[]; - pattern?: string; - patternProperties?: Partial>; - properties?: Partial>; - required?: Array; - title?: string; - type?: string; - uniqueItems?: boolean; - x_kubernetes_embedded_resource?: boolean; - x_kubernetes_int_or_string?: boolean; - x_kubernetes_list_map_keys?: string[]; - x_kubernetes_list_type?: string; - x_kubernetes_map_type?: string; - x_kubernetes_preserve_unknown_fields?: boolean; -} diff --git a/src/common/k8s-api/endpoints/types/lifecycle.ts b/src/common/k8s-api/endpoints/types/lifecycle.ts deleted file mode 100644 index 2e459958c4..0000000000 --- a/src/common/k8s-api/endpoints/types/lifecycle.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Handler } from "./handler"; - -/** - * Lifecycle describes actions that the management system should take in response to container - * lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container - * blocks until the action is complete, unless the container process fails, in which case the - * handler is aborted. - */ -export interface Lifecycle { - postStart?: Handler; - preStop?: Handler; -} diff --git a/src/common/k8s-api/endpoints/types/object-field-selector.ts b/src/common/k8s-api/endpoints/types/object-field-selector.ts deleted file mode 100644 index 8593456ff1..0000000000 --- a/src/common/k8s-api/endpoints/types/object-field-selector.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface ObjectFieldSelector { - apiVersion?: string; - fieldPath: string; -} diff --git a/src/common/k8s-api/endpoints/types/persistent-volume-claim-template-spec.ts b/src/common/k8s-api/endpoints/types/persistent-volume-claim-template-spec.ts deleted file mode 100644 index 5461c81807..0000000000 --- a/src/common/k8s-api/endpoints/types/persistent-volume-claim-template-spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectScope, KubeTemplateObjectMetadata } from "../../kube-object"; -import type { PersistentVolumeSpec } from "../persistent-volume.api"; - -export interface PersistentVolumeClaimTemplateSpec { - metadata?: KubeTemplateObjectMetadata; - spec?: PersistentVolumeSpec; -} diff --git a/src/common/k8s-api/endpoints/types/pod-security-context.ts b/src/common/k8s-api/endpoints/types/pod-security-context.ts deleted file mode 100644 index 983ae86262..0000000000 --- a/src/common/k8s-api/endpoints/types/pod-security-context.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { SeLinuxOptions } from "./se-linux-options"; -import type { SeccompProfile } from "./seccomp-profile"; -import type { WindowsSecurityContextOptions } from "./windows-security-context-options"; -import type { Sysctl } from "../pod.api"; - - -export interface PodSecurityContext { - fsGroup?: number; - fsGroupChangePolicy?: string; - runAsGroup?: number; - runAsNonRoot?: boolean; - runAsUser?: number; - seLinuxOptions?: SeLinuxOptions; - seccompProfile?: SeccompProfile; - supplementalGroups?: number[]; - sysctls?: Sysctl; - windowsOptions?: WindowsSecurityContextOptions; -} diff --git a/src/common/k8s-api/endpoints/types/pod-template-spec.ts b/src/common/k8s-api/endpoints/types/pod-template-spec.ts deleted file mode 100644 index ba0df4aec7..0000000000 --- a/src/common/k8s-api/endpoints/types/pod-template-spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectScope, KubeTemplateObjectMetadata } from "../../kube-object"; -import type { PodSpec } from "../pod.api"; - -export interface PodTemplateSpec { - metadata?: KubeTemplateObjectMetadata; - spec?: PodSpec; -} diff --git a/src/common/k8s-api/endpoints/types/policy-rule.ts b/src/common/k8s-api/endpoints/types/policy-rule.ts deleted file mode 100644 index b84e556223..0000000000 --- a/src/common/k8s-api/endpoints/types/policy-rule.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface PolicyRule { - verbs: string[]; - apiGroups?: string[]; - resources?: string[]; - resourceNames?: string[]; - nonResourceURLs?: string[]; -} diff --git a/src/common/k8s-api/endpoints/types/preemption-policy.ts b/src/common/k8s-api/endpoints/types/preemption-policy.ts deleted file mode 100644 index 32d37b93a3..0000000000 --- a/src/common/k8s-api/endpoints/types/preemption-policy.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type PreemptionPolicy = "Never" | "PreemptLowerPriority"; diff --git a/src/common/k8s-api/endpoints/types/probe.ts b/src/common/k8s-api/endpoints/types/probe.ts deleted file mode 100644 index 4251be5e2c..0000000000 --- a/src/common/k8s-api/endpoints/types/probe.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ExecAction } from "./exec-action"; -import type { HttpGetAction } from "./http-get-action"; -import type { TcpSocketAction } from "./tcp-socket-action"; - -/** - * Describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic. - */ -export interface Probe { - exec?: ExecAction; - - /** - * Minimum consecutive failures for the probe to be considered failed after having succeeded. - * - * @default 3 - * @minimum 1 - */ - failureThreshold?: number; - - httpGet?: HttpGetAction; - - /** - * Duration after the container has started before liveness probes are initiated. - * - * More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - */ - initialDelaySeconds?: number; - - /** - * How often to perform the probe. - * - * @default 10 - * @minimum 1 - */ - periodSeconds?: number; - - /** - * Minimum consecutive successes for the probe to be considered successful after having failed. - * - * Must be 1 for liveness and startup. - * - * @default 1 - * @minimum 1 - */ - successThreshold?: number; - - tcpSocket?: TcpSocketAction; - - /** - * Duration the pod needs to terminate gracefully upon probe failure. - * - * The grace period is the duration in seconds after the processes running in the pod are sent a - * termination signal and the time when the processes are forcibly halted with a kill signal. - * - * Set this value longer than the expected cleanup time for your process. - * - * If this value is not set, the pod's terminationGracePeriodSeconds will be used. Otherwise, - * this value overrides the value provided by the pod spec. Value must be non-negative integer. - * The value zero indicates stop immediately via the kill signal (no opportunity to shut down). - * - * This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. - * - * @minimum 1 - */ - terminationGracePeriodSeconds?: number; - - /** - * Duration after which the probe times out. - * - * More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - * - * @default 1 - * @minimum 1 - */ - timeoutSeconds?: number; -} diff --git a/src/common/k8s-api/endpoints/types/resource-field-selector.ts b/src/common/k8s-api/endpoints/types/resource-field-selector.ts deleted file mode 100644 index 9727506183..0000000000 --- a/src/common/k8s-api/endpoints/types/resource-field-selector.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface ResourceFieldSelector { - containerName?: string; - divisor?: string; - resource: string; -} diff --git a/src/common/k8s-api/endpoints/types/resource-requirements.ts b/src/common/k8s-api/endpoints/types/resource-requirements.ts deleted file mode 100644 index eb3b6caffa..0000000000 --- a/src/common/k8s-api/endpoints/types/resource-requirements.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * ResourceRequirements describes the compute resource requirements. - */ -export interface ResourceRequirements { - /** - * Limits describes the maximum amount of compute resources allowed. - * - * More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - */ - limits?: Partial>; - - /** - * Requests describes the minimum amount of compute resources required. If Requests is omitted - * for a container, it defaults to Limits if that is explicitly specified, otherwise to an - * implementation-defined value. - * - * More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - */ - requests?: Partial>; -} diff --git a/src/common/k8s-api/endpoints/types/role-ref.ts b/src/common/k8s-api/endpoints/types/role-ref.ts deleted file mode 100644 index 60e9bdda77..0000000000 --- a/src/common/k8s-api/endpoints/types/role-ref.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface RoleRef { - apiGroup: string; - kind: string; - name: string; -} diff --git a/src/common/k8s-api/endpoints/types/se-linux-options.ts b/src/common/k8s-api/endpoints/types/se-linux-options.ts deleted file mode 100644 index 9e3c629192..0000000000 --- a/src/common/k8s-api/endpoints/types/se-linux-options.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * SELinuxOptions are the labels to be applied to the container - */ -export interface SeLinuxOptions { - /** - * The SELinux `level` label that applies to the container. - */ - level?: string; - - /** - * The SELinux `role` label that applies to the container. - */ - role?: string; - - /** - * The SELinux `type` label that applies to the container. - */ - type?: string; - - /** - * The SELinux `user` label that applies to the container. - */ - user?: string; -} diff --git a/src/common/k8s-api/endpoints/types/seccomp-profile.ts b/src/common/k8s-api/endpoints/types/seccomp-profile.ts deleted file mode 100644 index 20eddbf94d..0000000000 --- a/src/common/k8s-api/endpoints/types/seccomp-profile.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Defines a pod's or a container's seccomp profile settings. Only one profile source may be set. - */ -export interface SeccompProfile { - /** - * Indicates a profile defined in a file on the node should be used. The profile must be - * preconfigured on the node to work. Must be a descending path, relative to the kubelet's - * configured seccomp profile location. Must only be set if type is "Localhost". - */ - localhostProfile?: string; - - /** - * Indicates which kind of seccomp profile will be applied. - * - * Options: - * - * | Value | Description | - * |--|--| - * | `Localhost` | A profile defined in a file on the node should be used. | - * | `RuntimeDefault` | The container runtime default profile should be used. | - * | `Unconfined` | No profile should be applied. | - */ - type: "Localhost" | "RuntimeDefault" | "Unconfined"; -} diff --git a/src/common/k8s-api/endpoints/types/security-context.ts b/src/common/k8s-api/endpoints/types/security-context.ts deleted file mode 100644 index ce5cf60e40..0000000000 --- a/src/common/k8s-api/endpoints/types/security-context.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Capabilities } from "./capabilities"; -import type { SeLinuxOptions } from "./se-linux-options"; -import type { SeccompProfile } from "./seccomp-profile"; -import type { WindowsSecurityContextOptions } from "./windows-security-context-options"; - -/** - * SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence. - */ -export interface SecurityContext { - /** - * AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN - */ - allowPrivilegeEscalation?: boolean; - - capabilities?: Capabilities; - - /** - * Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. - */ - privileged?: boolean; - - /** - * procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. - */ - procMount?: string; - - /** - * Whether this container has a read-only root filesystem. Default is false. - */ - readOnlyRootFilesystem?: boolean; - - /** - * The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - */ - runAsGroup?: number; - - /** - * Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - */ - runAsNonRoot?: boolean; - - /** - * The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - */ - runAsUser?: number; - - seLinuxOptions?: SeLinuxOptions; - seccompProfile?: SeccompProfile; - windowsOptions?: WindowsSecurityContextOptions; -} diff --git a/src/common/k8s-api/endpoints/types/subject.ts b/src/common/k8s-api/endpoints/types/subject.ts deleted file mode 100644 index ba4e37f1a4..0000000000 --- a/src/common/k8s-api/endpoints/types/subject.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type SubjectKind = "Group" | "ServiceAccount" | "User"; - -export interface Subject { - apiGroup?: string; - kind: SubjectKind; - name: string; - namespace?: string; -} diff --git a/src/common/k8s-api/endpoints/types/tcp-socket-action.ts b/src/common/k8s-api/endpoints/types/tcp-socket-action.ts deleted file mode 100644 index 02f1805823..0000000000 --- a/src/common/k8s-api/endpoints/types/tcp-socket-action.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * An action based on opening a socket - */ -export interface TcpSocketAction { - /** - * Host name to connect to, defaults to the pod IP. - */ - host?: string; - - /** - * Port to connect to - */ - port: number | string; -} diff --git a/src/common/k8s-api/endpoints/types/volume-device.ts b/src/common/k8s-api/endpoints/types/volume-device.ts deleted file mode 100644 index 1dacaafe21..0000000000 --- a/src/common/k8s-api/endpoints/types/volume-device.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * A mapping of a raw block device within a container. - */ -export interface VolumeDevice { - /** - * The path inside of the container that the device will be mapped to. - */ - devicePath: string; - - /** - * Must match the name of a persistentVolumeClaim in the pod - */ - name: string; -} diff --git a/src/common/k8s-api/endpoints/types/volume-mount.ts b/src/common/k8s-api/endpoints/types/volume-mount.ts deleted file mode 100644 index 0066d8896d..0000000000 --- a/src/common/k8s-api/endpoints/types/volume-mount.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface VolumeMount { - name: string; - readOnly?: boolean; - mountPath: string; - mountPropagation?: string; - subPath?: string; - subPathExpr?: string; -} diff --git a/src/common/k8s-api/endpoints/types/windows-security-context-options.ts b/src/common/k8s-api/endpoints/types/windows-security-context-options.ts deleted file mode 100644 index 07daf48731..0000000000 --- a/src/common/k8s-api/endpoints/types/windows-security-context-options.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Windows-specific options and credentials. - */ -export interface WindowsSecurityContextOptions { - /** - * The location of the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - * inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. - */ - gmsaCredentialSpec?: string; - - /** - * The name of the GMSA credential spec to use. - */ - gmsaCredentialSpecName?: string; - - /** - * Determines if a container should be run as a 'Host Process' container. - * - * This field is alpha-level and will only be honored by components that enable the - * WindowsHostProcessContainers feature flag. - * - * Setting this field without the feature flag will result in errors when validating the Pod. - * - * All of a Pod's containers must have the same effective HostProcess value (it is not allowed to - * have a mix of HostProcess containers and non-HostProcess containers). - * - * In addition, if HostProcess is true then HostNetwork must also be set to true. - */ - hostProcess?: boolean; - - /** - * The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - */ - runAsUserName?: string; -} diff --git a/src/common/k8s-api/json-api.ts b/src/common/k8s-api/json-api.ts deleted file mode 100644 index 988378641c..0000000000 --- a/src/common/k8s-api/json-api.ts +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Base http-service / json-api class - -import { Agent as HttpAgent } from "http"; -import { Agent as HttpsAgent } from "https"; -import { merge } from "lodash"; -import type { Response, RequestInit } from "node-fetch"; -import { stringify } from "querystring"; -import type { Patch } from "rfc6902"; -import type { PartialDeep, ValueOf } from "type-fest"; -import { EventEmitter } from "../../common/event-emitter"; -import type { Logger } from "../../common/logger"; -import type { Fetch } from "../fetch/fetch.injectable"; -import type { Defaulted } from "../utils"; -import { json } from "../utils"; - -export interface JsonApiData {} - -export interface JsonApiError { - code?: number; - message?: string; - errors?: { id: string; title: string; status?: number }[]; -} - -export interface JsonApiParams { - data?: PartialDeep; // request body -} - -export interface JsonApiLog { - method: string; - reqUrl: string; - reqInit: RequestInit; - data?: any; - error?: any; -} - -export type GetRequestOptions = () => Promise; - -export interface JsonApiConfig { - apiBase: string; - serverAddress: string; - debug?: boolean; - getRequestOptions?: GetRequestOptions; -} - -const httpAgent = new HttpAgent({ keepAlive: true }); -const httpsAgent = new HttpsAgent({ keepAlive: true }); - -export type QueryParam = string | number | boolean | null | undefined | readonly string[] | readonly number[] | readonly boolean[]; -export type QueryParams = Partial>; - -export type ParamsAndQuery = ( - ValueOf extends QueryParam - ? Params & { query?: Query } - : Params & { query?: undefined } -); - -export interface JsonApiDependencies { - fetch: Fetch; - readonly logger: Logger; -} - -export class JsonApi = JsonApiParams> { - static readonly reqInitDefault = { - headers: { - "content-type": "application/json", - }, - }; - protected readonly reqInit: Defaulted; - - static readonly configDefault: Partial = { - debug: false, - }; - - constructor(protected readonly dependencies: JsonApiDependencies, public readonly config: JsonApiConfig, reqInit?: RequestInit) { - this.config = Object.assign({}, JsonApi.configDefault, config); - this.reqInit = merge({}, JsonApi.reqInitDefault, reqInit); - this.parseResponse = this.parseResponse.bind(this); - this.getRequestOptions = config.getRequestOptions ?? (() => Promise.resolve({})); - } - - public readonly onData = new EventEmitter<[Data, Response]>(); - public readonly onError = new EventEmitter<[JsonApiErrorParsed, Response]>(); - private readonly getRequestOptions: GetRequestOptions; - - async getResponse( - path: string, - params?: ParamsAndQuery, - init: RequestInit = {}, - ): Promise { - let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`; - const reqInit = merge( - { - method: "get", - agent: reqUrl.startsWith("https:") ? httpsAgent : httpAgent, - }, - this.reqInit, - await this.getRequestOptions(), - init, - ); - const { query } = params ?? {}; - - if (query && Object.keys(query).length > 0) { - const queryString = stringify(query as unknown as QueryParams); - - reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; - } - - return this.dependencies.fetch(reqUrl, reqInit); - } - - get( - path: string, - params?: ParamsAndQuery, - reqInit: RequestInit = {}, - ) { - return this.request(path, params, { ...reqInit, method: "get" }); - } - - post( - path: string, - params?: ParamsAndQuery, - reqInit: RequestInit = {}, - ) { - return this.request(path, params, { ...reqInit, method: "post" }); - } - - put( - path: string, - params?: ParamsAndQuery, - reqInit: RequestInit = {}, - ) { - return this.request(path, params, { ...reqInit, method: "put" }); - } - - patch( - path: string, - params?: (ParamsAndQuery, Query> & { data?: Patch | PartialDeep }), - reqInit: RequestInit = {}, - ) { - return this.request(path, params, { ...reqInit, method: "patch" }); - } - - del( - path: string, - params?: ParamsAndQuery, - reqInit: RequestInit = {}, - ) { - return this.request(path, params, { ...reqInit, method: "delete" }); - } - - protected async request( - path: string, - params: (ParamsAndQuery, Query> & { data?: unknown }) | undefined, - init: Defaulted, - ) { - let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`; - const reqInit = merge( - {}, - this.reqInit, - await this.getRequestOptions(), - init, - ); - const { data, query } = params || {}; - - if (data && !reqInit.body) { - reqInit.body = JSON.stringify(data); - } - - if (query && Object.keys(query).length > 0) { - const queryString = stringify(query as unknown as QueryParams); - - reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; - } - const infoLog: JsonApiLog = { - method: reqInit.method.toUpperCase(), - reqUrl, - reqInit, - }; - - const res = await this.dependencies.fetch(reqUrl, reqInit); - - return this.parseResponse(res, infoLog); - } - - protected async parseResponse(res: Response, log: JsonApiLog): Promise { - const { status } = res; - - const text = await res.text(); - let data: any; - - try { - data = text ? json.parse(text) : ""; // DELETE-requests might not have response-body - } catch (e) { - data = text; - } - - if (status >= 200 && status < 300) { - this.onData.emit(data, res); - this.writeLog({ ...log, data }); - - return data; - } - - if (log.method === "GET" && res.status === 403) { - this.writeLog({ ...log, error: data }); - throw data; - } - - const error = new JsonApiErrorParsed(data, this.parseError(data, res)); - - this.onError.emit(error, res); - this.writeLog({ ...log, error }); - - throw error; - } - - protected parseError(error: JsonApiError | string, res: Response): string[] { - if (typeof error === "string") { - return [error]; - } - - if (Array.isArray(error.errors)) { - return error.errors.map(error => error.title); - } - - if (error.message) { - return [error.message]; - } - - return [res.statusText || "Error!"]; - } - - protected writeLog(log: JsonApiLog) { - const { method, reqUrl, ...params } = log; - - this.dependencies.logger.debug(`[JSON-API] request ${method} ${reqUrl}`, params); - } -} - -export class JsonApiErrorParsed { - isUsedForNotification = false; - - constructor(private error: JsonApiError | DOMException, private messages: string[]) { - } - - get isAborted() { - return this.error.code === DOMException.ABORT_ERR; - } - - toString() { - return this.messages.join("\n"); - } -} diff --git a/src/common/k8s-api/kube-api-parse.ts b/src/common/k8s-api/kube-api-parse.ts deleted file mode 100644 index cb5315b50c..0000000000 --- a/src/common/k8s-api/kube-api-parse.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Parse kube-api path and get api-version, group, etc. - -import { splitArray } from "../utils"; - -export interface IKubeApiLinkRef { - apiPrefix?: string; - apiVersion: string; - resource: string; - name?: string; - namespace?: string; -} - -export interface IKubeApiParsed extends IKubeApiLinkRef { - apiBase: string; - apiPrefix: string; - apiGroup: string; - apiVersionWithGroup: string; -} - -export function parseKubeApi(path: string): IKubeApiParsed { - const apiPath = new URL(path, "https://localhost").pathname; - const [, prefix, ...parts] = apiPath.split("/"); - const apiPrefix = `/${prefix}`; - const [left, right, namespaced] = splitArray(parts, "namespaces"); - let apiGroup!: string; - let apiVersion!: string; - let namespace!: string; - let resource!: string; - let name!: string; - - if (namespaced) { - switch (right.length) { - case 1: - name = right[0]; - // fallthrough - case 0: - resource = "namespaces"; // special case this due to `split` removing namespaces - break; - default: - [namespace, resource, name] = right; - break; - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - apiVersion = left.pop()!; - apiGroup = left.join("/"); - } else { - switch (left.length) { - case 0: - throw new Error(`invalid apiPath: ${apiPath}`); - case 4: - [apiGroup, apiVersion, resource, name] = left; - break; - case 2: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - resource = left.pop()!; - // fallthrough - case 1: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - apiVersion = left.pop()!; - apiGroup = ""; - break; - default: - /** - * Given that - * - `apiVersion` is `GROUP/VERSION` and - * - `VERSION` is `DNS_LABEL` which is /^[a-z0-9]((-[a-z0-9])|[a-z0-9])*$/i - * where length <= 63 - * - `GROUP` is /^D(\.D)*$/ where D is `DNS_LABEL` and length <= 253 - * - * There is no well defined selection from an array of items that were - * separated by '/' - * - * Solution is to create a heuristic. Namely: - * 1. if '.' in left[0] then apiGroup <- left[0] - * 2. if left[1] matches /^v[0-9]/ then apiGroup, apiVersion <- left[0], left[1] - * 3. otherwise assume apiVersion <- left[0] - * 4. always resource, name <- left[(0 or 1)+1..] - */ - if (left[0].includes(".") || left[1].match(/^v[0-9]/)) { - [apiGroup, apiVersion] = left; - resource = left.slice(2).join("/"); - } else { - apiGroup = ""; - apiVersion = left[0]; - [resource, name] = left.slice(1); - } - break; - } - } - - const apiVersionWithGroup = [apiGroup, apiVersion].filter(v => v).join("/"); - const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/"); - - if (!apiBase) { - throw new Error(`invalid apiPath: ${apiPath}`); - } - - return { - apiBase, - apiPrefix, apiGroup, - apiVersion, apiVersionWithGroup, - namespace, resource, name, - }; -} - -export function createKubeApiURL({ apiPrefix = "/apis", resource, apiVersion, name, namespace }: IKubeApiLinkRef): string { - const parts = [apiPrefix, apiVersion]; - - if (namespace) { - parts.push("namespaces", namespace); - } - - parts.push(resource); - - if (name) { - parts.push(name); - } - - return parts.join("/"); -} diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts deleted file mode 100644 index 8e6e86d7da..0000000000 --- a/src/common/k8s-api/kube-api.ts +++ /dev/null @@ -1,721 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Base class for building all kubernetes apis - -import { merge } from "lodash"; -import { stringify } from "querystring"; -import { createKubeApiURL, parseKubeApi } from "./kube-api-parse"; -import type { KubeObjectConstructor, KubeJsonApiDataFor, KubeObjectMetadata } from "./kube-object"; -import { KubeObject, KubeStatus, isKubeStatusData } from "./kube-object"; -import byline from "byline"; -import type { IKubeWatchEvent } from "./kube-watch-event"; -import type { KubeJsonApiData, KubeJsonApi } from "./kube-json-api"; -import type { Disposer } from "../utils"; -import { isDefined, noop, WrappedAbortController } from "../utils"; -import type { RequestInit, Response } from "node-fetch"; -import type { Patch } from "rfc6902"; -import assert from "assert"; -import type { PartialDeep } from "type-fest"; -import type { Logger } from "../logger"; -import type AbortController from "abort-controller"; -import { matches } from "lodash/fp"; -import { makeObservable, observable } from "mobx"; - -/** - * The options used for creating a `KubeApi` - */ -export interface KubeApiOptions = KubeJsonApiDataFor> extends DerivedKubeApiOptions { - /** - * base api-path for listing all resources, e.g. "/api/v1/pods" - * - * Must be provided either here or under `objectConstructor.apiBase` - * @deprecated should be specified by `objectConstructor` - */ - apiBase?: string; - - /** - * The constructor for the kube objects returned from the API - */ - objectConstructor: KubeObjectConstructor; - - /** - * Must be provided either here or under `objectConstructor.namespaced` - * @deprecated should be specified by `objectConstructor` - */ - isNamespaced?: boolean; - - /** - * Must be provided either here or under `objectConstructor.kind` - * @deprecated should be specified by `objectConstructor` - */ - kind?: string; -} - -export interface DerivedKubeApiOptions { - /** - * If the API uses a different API endpoint (e.g. apiBase) depending on the cluster version, - * fallback API bases can be listed individually. - * The first (existing) API base is used in the requests, if apiBase is not found. - * This option only has effect if checkPreferredVersion is true. - */ - fallbackApiBases?: string[]; - - /** - * If `true` then will check all declared apiBases against the kube api server - * for the first accepted one. - */ - checkPreferredVersion?: boolean; - - /** - * The api instance to use for making requests - * - * @default apiKube - */ - request?: KubeJsonApi; -} - -export interface KubeApiQueryParams { - watch?: boolean | number; - resourceVersion?: string; - timeoutSeconds?: number; - limit?: number; // doesn't work with ?watch - continue?: string; // might be used with ?limit from second request - labelSelector?: string | string[]; // restrict list of objects by their labels, e.g. labelSelector: ["label=value"] - fieldSelector?: string | string[]; // restrict list of objects by their fields, e.g. fieldSelector: "field=name" -} - -export interface KubeApiListOptions { - namespace?: string; - reqInit?: RequestInit; -} - -export interface IKubePreferredVersion { - preferredVersion?: { - version: string; - }; -} - -export interface KubeApiResource { - categories?: string[]; - group?: string; - kind: string; - name: string; - namespaced: boolean; - shortNames?: string[]; - singularName: string; - storageVersionHash?: string; - verbs: string[]; - version?: string; -} - -export interface KubeApiResourceList { - apiVersion?: string; - groupVersion?: string; - kind?: string; - resources: KubeApiResource[]; -} - -export interface KubeApiResourceVersion { - groupVersion: string; - version: string; -} - -export interface KubeApiResourceVersionList { - apiVersion: string; - kind: string; - name: string; - preferredVersion: KubeApiResourceVersion; - versions: KubeApiResourceVersion[]; -} - -const not = (fn: (val: T) => boolean) => (val: T) => !(fn(val)); - -const getOrderedVersions = (list: KubeApiResourceVersionList): KubeApiResourceVersion[] => [ - list.preferredVersion, - ...list.versions.filter(not(matches(list.preferredVersion))), -]; - -export type PropagationPolicy = undefined | "Orphan" | "Foreground" | "Background"; - -export type KubeApiWatchCallback = (data: IKubeWatchEvent | null, error: KubeStatus | Response | null | any) => void; - -export interface KubeApiWatchOptions> { - /** - * If the resource is namespaced then the default is `"default"` - */ - namespace?: string; - - /** - * This will be called when either an error occurs or some data is received - */ - callback?: KubeApiWatchCallback; - - /** - * This is a way of aborting the request - */ - abortController?: AbortController; - - /** - * The ID used for tracking within logs - */ - watchId?: string; - - /** - * @default false - */ - retry?: boolean; - - /** - * timeout in seconds - */ - timeout?: number; -} - -export type KubeApiPatchType = "merge" | "json" | "strategic"; - -const patchTypeHeaders: Record = { - "merge": "application/merge-patch+json", - "json": "application/json-patch+json", - "strategic": "application/strategic-merge-patch+json", -}; - -export interface ResourceDescriptor { - /** - * The name of the kubernetes resource - */ - name: string; - - /** - * The namespace that the resource lives in (if the resource is namespaced) - * - * Note: if not provided and the resource kind is namespaced, then this defaults to `"default"` - */ - namespace?: string; -} - -export interface DeleteResourceDescriptor extends ResourceDescriptor { - /** - * This determinines how child resources should be handled by kubernetes - * - * @default "Background" - */ - propagationPolicy?: PropagationPolicy; -} - -export interface KubeApiDependencies { - readonly logger: Logger; - readonly maybeKubeApi: KubeJsonApi | undefined; -} - -export class KubeApi< - Object extends KubeObject = KubeObject, - Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, -> { - readonly kind: string; - readonly apiVersion: string; - - @observable apiBase: string; - - apiPrefix: string; - apiGroup: string; - apiVersionPreferred: string | undefined; - readonly apiResource: string; - readonly isNamespaced: boolean; - - public readonly objectConstructor: KubeObjectConstructor; - protected readonly request: KubeJsonApi; - protected readonly resourceVersions = new Map(); - protected readonly watchDisposer: Disposer | undefined; - private watchId = 1; - protected readonly doCheckPreferredVersion: boolean; - protected readonly fullApiPathname: string; - protected readonly fallbackApiBases: string[] | undefined; - - constructor(protected readonly dependencies: KubeApiDependencies, opts: KubeApiOptions) { - const { - objectConstructor, - request = this.dependencies.maybeKubeApi, - kind = objectConstructor.kind, - isNamespaced, - apiBase: fullApiPathname = objectConstructor.apiBase, - checkPreferredVersion: doCheckPreferredVersion = false, - fallbackApiBases, - } = opts; - - assert(fullApiPathname, "apiBase MUST be provied either via KubeApiOptions.apiBase or KubeApiOptions.objectConstructor.apiBase"); - assert(request, "request MUST be provided if not in a cluster page frame context"); - - const { apiBase, apiPrefix, apiGroup, apiVersion, resource } = parseKubeApi(fullApiPathname); - - assert(kind, "kind MUST be provied either via KubeApiOptions.kind or KubeApiOptions.objectConstructor.kind"); - assert(apiPrefix, "apiBase MUST be parsable as a kubeApi selfLink style string"); - - this.doCheckPreferredVersion = doCheckPreferredVersion; - this.fallbackApiBases = fallbackApiBases; - this.fullApiPathname = fullApiPathname; - this.kind = kind; - this.isNamespaced = isNamespaced ?? objectConstructor.namespaced ?? false; - this.apiBase = apiBase; - this.apiPrefix = apiPrefix; - this.apiGroup = apiGroup; - this.apiVersion = apiVersion; - this.apiResource = resource; - this.request = request; - this.objectConstructor = objectConstructor; - makeObservable(this); - } - - get apiVersionWithGroup() { - return [this.apiGroup, this.apiVersionPreferred ?? this.apiVersion] - .filter(Boolean) - .join("/"); - } - - /** - * Returns the latest API prefix/group that contains the required resource. - * First tries fullApiPathname, then urls in order from fallbackApiBases. - */ - private async getLatestApiPrefixGroup() { - // Note that this.fullApiPathname is the "full" url, whereas this.apiBase is parsed - const rawApiBases = [ - this.fullApiPathname, - this.objectConstructor.apiBase, - ...this.fallbackApiBases ?? [], - ].filter(isDefined); - const apiBases = new Set(rawApiBases); - - for (const apiUrl of apiBases) { - try { - const { apiPrefix, apiGroup, resource } = parseKubeApi(apiUrl); - const list = await this.request.get(`${apiPrefix}/${apiGroup}`) as KubeApiResourceVersionList; - const resourceVersions = getOrderedVersions(list); - - for (const resourceVersion of resourceVersions) { - const { resources } = await this.request.get(`${apiPrefix}/${resourceVersion.groupVersion}`) as KubeApiResourceList; - - if (resources.some(({ name }) => name === resource)) { - return { - apiPrefix, - apiGroup, - apiVersionPreferred: resourceVersion.version, - }; - } - } - } catch (error) { - // Exception is ignored as we can try the next url - } - } - - throw new Error(`Can't find working API for the Kubernetes resource ${this.apiResource}`); - } - - protected async checkPreferredVersion() { - if (this.fallbackApiBases && !this.doCheckPreferredVersion) { - throw new Error("checkPreferredVersion must be enabled if fallbackApiBases is set in KubeApi"); - } - - if (this.doCheckPreferredVersion && this.apiVersionPreferred === undefined) { - const { apiPrefix, apiGroup, apiVersionPreferred } = await this.getLatestApiPrefixGroup(); - - this.apiPrefix = apiPrefix; - this.apiGroup = apiGroup; - this.apiVersionPreferred = apiVersionPreferred; - this.apiBase = this.computeApiBase(); - } - } - - setResourceVersion(namespace = "", newVersion: string) { - this.resourceVersions.set(namespace, newVersion); - } - - getResourceVersion(namespace = "") { - return this.resourceVersions.get(namespace); - } - - async refreshResourceVersion(params?: KubeApiListOptions) { - return this.list(params, { limit: 1 }); - } - - private computeApiBase(): string { - return createKubeApiURL({ - apiPrefix: this.apiPrefix, - apiVersion: this.apiVersionWithGroup, - resource: this.apiResource, - }); - } - - /** - * This method differs from {@link formatUrlForNotListing} because this treats `""` as "all namespaces" - * NOTE: This is also useful for watching - * @param namespace The namespace to list in or `""` for all namespaces - */ - formatUrlForListing(namespace: string | undefined, query?: Partial) { - const resourcePath = createKubeApiURL({ - apiPrefix: this.apiPrefix, - apiVersion: this.apiVersionWithGroup, - resource: this.apiResource, - namespace: this.isNamespaced - ? namespace ?? "default" - : undefined, - }); - - return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : ""); - } - - /** - * Format a URL pathname and query for acting upon a specific resource. - */ - formatUrlForNotListing({ name, namespace }: Partial = {}) { - return createKubeApiURL({ - apiPrefix: this.apiPrefix, - apiVersion: this.apiVersionWithGroup, - resource: this.apiResource, - namespace: this.isNamespaced - ? namespace || "default" - : undefined, - name, - }); - } - - /** - * @deprecated use {@link formatUrlForNotListing} or {@link formatUrlForListing} instead - */ - getUrl(resource?: Partial, query?: Partial) { - if (query) { - return this.formatUrlForListing(resource?.namespace, query); - } - - return this.formatUrlForNotListing(resource); - } - - protected normalizeQuery(query: Partial = {}) { - if (query.labelSelector) { - query.labelSelector = [query.labelSelector].flat().join(","); - } - - if (query.fieldSelector) { - query.fieldSelector = [query.fieldSelector].flat().join(","); - } - - return query; - } - - protected parseResponse(data: unknown, namespace?: string): Object | Object[] | null { - if (!data) { - return null; - } - - const KubeObjectConstructor = this.objectConstructor; - - // process items list response, check before single item since there is overlap - if (KubeObject.isJsonApiDataList(data, KubeObject.isPartialJsonApiData)) { - const { apiVersion, items, metadata } = data; - - this.setResourceVersion(namespace, metadata.resourceVersion); - this.setResourceVersion("", metadata.resourceVersion); - - return items - .map((item) => { - if (item.metadata) { - this.ensureMetadataSelfLink(item.metadata); - } else { - return undefined; - } - - const object = new KubeObjectConstructor({ - ...(item as Data), - kind: this.kind, - apiVersion, - }); - - return object; - }) - .filter(isDefined); - } - - // process a single item - if (KubeObject.isJsonApiData(data)) { - this.ensureMetadataSelfLink(data.metadata); - - return new KubeObjectConstructor(data as never); - } - - // custom apis might return array for list response, e.g. users, groups, etc. - if (Array.isArray(data)) { - return data.map(data => { - this.ensureMetadataSelfLink(data.metadata); - - return new KubeObjectConstructor(data); - }); - } - - return null; - } - - private ensureMetadataSelfLink(metadata: T): asserts metadata is T & { selfLink: string } { - metadata.selfLink ||= createKubeApiURL({ - apiPrefix: this.apiPrefix, - apiVersion: this.apiVersionWithGroup, - resource: this.apiResource, - namespace: metadata.namespace, - name: metadata.name, - }); - } - - async list({ namespace = "", reqInit }: KubeApiListOptions = {}, query?: KubeApiQueryParams): Promise { - await this.checkPreferredVersion(); - - const url = this.formatUrlForListing(namespace); - const res = await this.request.get(url, { query }, reqInit); - const parsed = this.parseResponse(res, namespace); - - if (Array.isArray(parsed)) { - return parsed; - } - - if (!parsed) { - return null; - } - - throw new Error(`GET multiple request to ${url} returned not an array: ${JSON.stringify(parsed)}`); - } - - async get(desc: ResourceDescriptor, query?: KubeApiQueryParams): Promise { - await this.checkPreferredVersion(); - - const url = this.formatUrlForNotListing(desc); - const res = await this.request.get(url, { query }); - const parsed = this.parseResponse(res); - - if (Array.isArray(parsed)) { - throw new Error(`GET single request to ${url} returned an array: ${JSON.stringify(parsed)}`); - } - - return parsed; - } - - async create({ name, namespace }: Partial, partialData?: PartialDeep): Promise { - await this.checkPreferredVersion(); - - const apiUrl = this.formatUrlForNotListing({ namespace }); - const data = merge(partialData, { - kind: this.kind, - apiVersion: this.apiVersionWithGroup, - metadata: { - name, - namespace, - }, - }); - const res = await this.request.post(apiUrl, { data }); - const parsed = this.parseResponse(res); - - if (Array.isArray(parsed)) { - throw new Error(`POST request to ${apiUrl} returned an array: ${JSON.stringify(parsed)}`); - } - - return parsed; - } - - async update({ name, namespace }: ResourceDescriptor, data: PartialDeep): Promise { - await this.checkPreferredVersion(); - const apiUrl = this.formatUrlForNotListing({ namespace, name }); - - const res = await this.request.put(apiUrl, { - data: merge(data, { - metadata: { - name, - namespace, - }, - }), - }); - const parsed = this.parseResponse(res); - - if (Array.isArray(parsed)) { - throw new Error(`PUT request to ${apiUrl} returned an array: ${JSON.stringify(parsed)}`); - } - - return parsed; - } - - async patch(desc: ResourceDescriptor, data: PartialDeep): Promise; - async patch(desc: ResourceDescriptor, data: PartialDeep, strategy: "strategic" | "merge"): Promise; - async patch(desc: ResourceDescriptor, data: Patch, strategy: "json"): Promise; - async patch(desc: ResourceDescriptor, data: PartialDeep | Patch, strategy: KubeApiPatchType): Promise; - async patch(desc: ResourceDescriptor, data: PartialDeep | Patch, strategy: KubeApiPatchType = "strategic"): Promise { - await this.checkPreferredVersion(); - const apiUrl = this.formatUrlForNotListing(desc); - - const res = await this.request.patch(apiUrl, { data }, { - headers: { - "content-type": patchTypeHeaders[strategy], - }, - }); - const parsed = this.parseResponse(res); - - if (Array.isArray(parsed)) { - throw new Error(`PATCH request to ${apiUrl} returned an array: ${JSON.stringify(parsed)}`); - } - - return parsed; - } - - async delete({ propagationPolicy = "Background", ...desc }: DeleteResourceDescriptor) { - await this.checkPreferredVersion(); - const apiUrl = this.formatUrlForNotListing(desc); - - return this.request.del(apiUrl, { - query: { - propagationPolicy, - }, - }); - } - - getWatchUrl(namespace?: string, query: KubeApiQueryParams = {}) { - return this.formatUrlForListing(namespace, { - watch: 1, - resourceVersion: this.getResourceVersion(namespace), - ...query, - }); - } - - watch(opts?: KubeApiWatchOptions): Disposer { - let errorReceived = false; - let timedRetry: NodeJS.Timeout; - const { - namespace, - callback = noop as KubeApiWatchCallback, - retry = false, - timeout = 600, - watchId = `${this.kind.toLowerCase()}-${this.watchId++}`, - } = opts ?? {}; - - // Create AbortController for this request - const abortController = new WrappedAbortController(opts?.abortController); - - abortController.signal.addEventListener("abort", () => { - this.dependencies.logger.info(`[KUBE-API] watch (${watchId}) aborted ${watchUrl}`); - clearTimeout(timedRetry); - }); - - const requestParams = timeout ? { query: { timeoutSeconds: timeout }} : {}; - const watchUrl = this.getWatchUrl(namespace); - const responsePromise = this.request.getResponse(watchUrl, requestParams, { - signal: abortController.signal, - }); - - this.dependencies.logger.info(`[KUBE-API] watch (${watchId}) ${retry === true ? "retried" : "started"} ${watchUrl}`); - - responsePromise - .then(response => { - // True if the current watch request was retried - let requestRetried = false; - - if (!response.ok) { - this.dependencies.logger.warn(`[KUBE-API] watch (${watchId}) error response ${watchUrl}`, { status: response.status }); - - return callback(null, response); - } - - // Add mechanism to retry in case timeoutSeconds is set but the watch wasn't timed out. - // This can happen if e.g. network is offline and AWS NLB is used. - if (timeout) { - setTimeout(() => { - // We only retry if we haven't retried, haven't aborted and haven't received k8s error - if (requestRetried || abortController.signal.aborted || errorReceived) { - return; - } - - // Close current request - abortController.abort(); - - this.dependencies.logger.info(`[KUBE-API] Watch timeout set, but not retried, retrying now`); - - requestRetried = true; - - // Clearing out any possible timeout, although we don't expect this to be set - clearTimeout(timedRetry); - this.watch({ ...opts, namespace, callback, watchId, retry: true }); - // We wait longer than the timeout, as we expect the request to be retried with timeoutSeconds - }, timeout * 1000 * 1.1); - } - - if (!response.body || !response.body.readable) { - if (!response.body) { - this.dependencies.logger.warn(`[KUBE-API]: watch (${watchId}) did not return a body`); - } - requestRetried = true; - - clearTimeout(timedRetry); - timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry - this.watch({ ...opts, namespace, callback, watchId, retry: true }); - }, 1000); - - return; - } - - for (const eventName of ["end", "close", "error"]) { - response.body.on(eventName, () => { - // We only retry if we haven't retried, haven't aborted and haven't received k8s error - // kubernetes errors (=errorReceived set) should be handled in a callback - if (requestRetried || abortController.signal.aborted || errorReceived) { - return; - } - - this.dependencies.logger.info(`[KUBE-API] watch (${watchId}) ${eventName} ${watchUrl}`); - - requestRetried = true; - - clearTimeout(timedRetry); - timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry - this.watch({ ...opts, namespace, callback, watchId, retry: true }); - }, 1000); - }); - } - - byline(response.body).on("data", (line) => { - try { - const event = JSON.parse(line) as IKubeWatchEvent; - - if (event.type === "ERROR" && isKubeStatusData(event.object)) { - errorReceived = true; - - return callback(null, new KubeStatus(event.object)); - } - - this.modifyWatchEvent(event); - callback(event, null); - } catch (ignore) { - // ignore parse errors - } - }); - }) - .catch(error => { - if (!abortController.signal.aborted) { - this.dependencies.logger.error(`[KUBE-API] watch (${watchId}) threw ${watchUrl}`, error); - } - callback(null, error); - }); - - return () => { - abortController.abort(); - }; - } - - protected modifyWatchEvent(event: IKubeWatchEvent>) { - if (event.type === "ERROR") { - return; - } - - this.ensureMetadataSelfLink(event.object.metadata); - - const { namespace, resourceVersion } = event.object.metadata; - - assert(resourceVersion, "watch events failed to return resourceVersion from kube api"); - - this.setResourceVersion(namespace, resourceVersion); - this.setResourceVersion("", resourceVersion); - } -} diff --git a/src/common/k8s-api/kube-api/get-kube-api-from-path.injectable.ts b/src/common/k8s-api/kube-api/get-kube-api-from-path.injectable.ts deleted file mode 100644 index 669185684f..0000000000 --- a/src/common/k8s-api/kube-api/get-kube-api-from-path.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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 { parseKubeApi } from "../kube-api-parse"; -import { kubeApiInjectionToken } from "./kube-api-injection-token"; -import type { KubeApi } from "../kube-api"; - -const getKubeApiFromPathInjectable = getInjectable({ - id: "get-kube-api-from-path", - - instantiate: (di) => { - const kubeApis = di.injectMany(kubeApiInjectionToken); - - return (apiPath: string) => { - const parsed = parseKubeApi(apiPath); - - const kubeApi = kubeApis.find((api) => api.apiBase === parsed.apiBase); - - return (kubeApi as KubeApi) || undefined; - }; - }, -}); - -export default getKubeApiFromPathInjectable; diff --git a/src/common/k8s-api/kube-api/kube-api-injection-token.ts b/src/common/k8s-api/kube-api/kube-api-injection-token.ts deleted file mode 100644 index 92b6636274..0000000000 --- a/src/common/k8s-api/kube-api/kube-api-injection-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { KubeApi } from "../kube-api"; - -export const kubeApiInjectionToken = getInjectionToken>({ - id: "kube-api-injection-token", -}); diff --git a/src/common/k8s-api/kube-json-api.ts b/src/common/k8s-api/kube-json-api.ts deleted file mode 100644 index 16ca5cda70..0000000000 --- a/src/common/k8s-api/kube-json-api.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { JsonApiData, JsonApiError } from "./json-api"; -import { JsonApi } from "./json-api"; -import type { Response } from "node-fetch"; -import type { KubeJsonApiObjectMetadata } from "./kube-object"; - -export interface KubeJsonApiListMetadata { - resourceVersion: string; - selfLink?: string; -} - -export interface KubeJsonApiDataList { - kind: string; - apiVersion: string; - items: T[]; - metadata: KubeJsonApiListMetadata; -} - -export interface KubeJsonApiData< - Metadata extends KubeJsonApiObjectMetadata = KubeJsonApiObjectMetadata, - Status = unknown, - Spec = unknown, -> extends JsonApiData { - kind: string; - apiVersion: string; - metadata: Metadata; - status?: Status; - spec?: Spec; - [otherKeys: string]: unknown; -} - -export interface KubeJsonApiError extends JsonApiError { - code: number; - status: string; - message?: string; - reason: string; - details: { - name: string; - kind: string; - }; -} - -export class KubeJsonApi extends JsonApi { - protected parseError(error: KubeJsonApiError | string, res: Response): string[] { - if (typeof error === "string") { - return [error]; - } - - const { status, reason, message } = error; - - if (status && reason) { - return [message || `${status}: ${reason}`]; - } - - return super.parseError(error, res); - } -} diff --git a/src/common/k8s-api/kube-object-status.ts b/src/common/k8s-api/kube-object-status.ts deleted file mode 100644 index 200bf35b5d..0000000000 --- a/src/common/k8s-api/kube-object-status.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface KubeObjectStatus { - level: KubeObjectStatusLevel; - text: string; - timestamp?: string; -} - -export enum KubeObjectStatusLevel { - INFO = 1, - WARNING = 2, - CRITICAL = 3, -} diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts deleted file mode 100644 index ee6a32265e..0000000000 --- a/src/common/k8s-api/kube-object.store.ts +++ /dev/null @@ -1,539 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, computed, makeObservable, observable, reaction } from "mobx"; -import type { Disposer } from "../utils"; -import { waitUntilDefined, autoBind, includes, rejectPromiseBy } from "../utils"; -import type { KubeJsonApiDataFor, KubeObject } from "./kube-object"; -import { KubeStatus } from "./kube-object"; -import type { IKubeWatchEvent } from "./kube-watch-event"; -import { ItemStore } from "../item.store"; -import type { KubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-api"; -import { parseKubeApi } from "./kube-api-parse"; -import type { RequestInit } from "node-fetch"; -import type { Patch } from "rfc6902"; -import type { Logger } from "../logger"; -import assert from "assert"; -import type { PartialDeep } from "type-fest"; -import { entries } from "../utils/objects"; -import AbortController from "abort-controller"; -import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context"; - -export type OnLoadFailure = (error: unknown) => void; - -export interface KubeObjectStoreLoadingParams { - namespaces: string[]; - reqInit?: RequestInit; - - /** - * A function that is called when listing fails. If set then blocks errors - * being rejected with - */ - onLoadFailure?: OnLoadFailure; -} - -export interface KubeObjectStoreLoadAllParams { - namespaces?: string[]; - merge?: boolean; - reqInit?: RequestInit; - - /** - * A function that is called when listing fails. If set then blocks errors - * being rejected with - */ - onLoadFailure?: OnLoadFailure; -} - -export interface KubeObjectStoreSubscribeParams { - /** - * A function that is called when listing fails. If set then blocks errors - * being rejected with - */ - onLoadFailure?: OnLoadFailure; - - /** - * An optional parent abort controller - */ - abortController?: AbortController; -} - -export interface MergeItemsOptions { - merge?: boolean; - updateStore?: boolean; - sort?: boolean; - filter?: boolean; - namespaces: string[]; -} - -export interface StatusProvider { - getStatuses(items: K[]): Record; -} - -export interface KubeObjectStoreOptions { - limit?: number; - bufferSize?: number; -} - -export type KubeApiDataFrom = A extends KubeApi - ? D extends KubeJsonApiDataFor - ? D - : never - : never; - -export type JsonPatch = Patch; - -export interface KubeObjectStoreDependencies { - readonly context: ClusterContext; - readonly logger: Logger; -} - -export abstract class KubeObjectStore< - K extends KubeObject = KubeObject, - A extends KubeApi = KubeApi>, - D extends KubeJsonApiDataFor = KubeApiDataFrom, -> extends ItemStore { - public readonly limit: number | undefined; - public readonly bufferSize: number; - - private readonly loadedNamespaces = observable.box(); - - constructor( - protected readonly dependencies: KubeObjectStoreDependencies, - public readonly api: A, - opts?: KubeObjectStoreOptions, - ) { - super(); - this.limit = opts?.limit; - this.bufferSize = opts?.bufferSize ?? 50_000; - - makeObservable(this); - autoBind(this); - this.bindWatchEventsUpdater(); - } - - // TODO: Circular dependency: KubeObjectStore -> ClusterFrameContext -> NamespaceStore -> KubeObjectStore - @computed get contextItems(): K[] { - const namespaces = this.dependencies.context.contextNamespaces; - - return this.items.filter(item => { - const itemNamespace = item.getNs(); - - return !itemNamespace /* cluster-wide */ || namespaces.includes(itemNamespace); - }); - } - - getTotalCount(): number { - return this.contextItems.length; - } - - get query(): KubeApiQueryParams { - const { limit } = this; - - if (!limit) { - return {}; - } - - return { limit }; - } - - getAllByNs(namespace: string | string[], strict = false): K[] { - const namespaces = [namespace].flat(); - - if (namespaces.length) { - return this.items.filter(item => includes(namespaces, item.getNs())); - } - - if (!strict) { - return this.items; - } - - return []; - } - - getById(id: string): K | undefined { - return this.items.find(item => item.getId() === id); - } - - getByName(name: string, namespace?: string): K | undefined { - return this.items.find(item => { - return item.getName() === name && ( - namespace ? item.getNs() === namespace : true - ); - }); - } - - getByPath(path: string): K | undefined { - return this.items.find(item => item.selfLink === path); - } - - getByLabel(labels: string[] | Partial>): K[] { - if (Array.isArray(labels)) { - return this.items.filter((item: K) => { - const itemLabels = item.getLabels(); - - return labels.every(label => itemLabels.includes(label)); - }); - } else { - return this.items.filter((item: K) => { - const itemLabels = item.metadata.labels || {}; - - return entries(labels) - .every(([key, value]) => itemLabels[key] === value); - }); - } - } - - protected async loadItems({ namespaces, reqInit, onLoadFailure }: KubeObjectStoreLoadingParams): Promise { - const isLoadingAll = this.dependencies.context.isLoadingAll(namespaces); - - if (!this.api.isNamespaced || isLoadingAll) { - if (this.api.isNamespaced) { - this.loadedNamespaces.set([]); - } - - const res = this.api.list({ reqInit }, this.query); - - if (onLoadFailure) { - try { - return await res ?? []; - } catch (error) { - onLoadFailure(new Error(`Failed to load ${this.api.apiBase}`, { cause: error })); - - // reset the store because we are loading all, so that nothing is displayed - this.items.clear(); - this.selectedItemsIds.clear(); - - return []; - } - } - - return await res ?? []; - } - - this.loadedNamespaces.set(namespaces); - - const results = await Promise.allSettled( - namespaces.map(namespace => this.api.list({ namespace, reqInit }, this.query)), - ); - const res: K[] = []; - - for (const result of results) { - switch (result.status) { - case "fulfilled": - res.push(...result.value ?? []); - break; - - case "rejected": - if (onLoadFailure) { - onLoadFailure(new Error(`Failed to load ${this.api.apiBase}`, { cause: result.reason })); - } else { - // if onLoadFailure is not provided then preserve old behaviour - throw result.reason; - } - } - } - - return res; - } - - protected filterItemsOnLoad(items: K[]) { - return items; - } - - @action - async loadAll({ namespaces, merge = true, reqInit, onLoadFailure }: KubeObjectStoreLoadAllParams = {}): Promise { - namespaces ??= this.dependencies.context.contextNamespaces; - this.isLoading = true; - - try { - const items = await this.loadItems({ namespaces, reqInit, onLoadFailure }); - - this.mergeItems(items, { merge, namespaces }); - - this.isLoaded = true; - this.failedLoading = false; - - return items; - } catch (error) { - console.warn("[KubeObjectStore] loadAll failed", this.api.apiBase, error); - this.resetOnError(error); - this.failedLoading = true; - } finally { - this.isLoading = false; - } - - return undefined; - } - - @action - async reloadAll(opts: { force?: boolean; namespaces?: string[]; merge?: boolean } = {}): Promise { - const { force = false, ...loadingOptions } = opts; - - if (this.isLoading || (this.isLoaded && !force)) { - return undefined; - } - - return this.loadAll(loadingOptions); - } - - @action - protected mergeItems(partialItems: K[], { merge = true, updateStore = true, sort = true, filter = true, namespaces }: MergeItemsOptions): K[] { - let items = partialItems; - - // update existing items - if (merge && this.api.isNamespaced) { - const ns = new Set(namespaces); - - items = [ - ...this.items.filter(item => !ns.has(item.getNs() as string)), - ...partialItems, - ]; - } - - if (filter) items = this.filterItemsOnLoad(items); - if (sort) items = this.sortItems(items); - if (updateStore) this.items.replace(items); - - return items; - } - - protected resetOnError(error: any) { - if (error) this.reset(); - } - - protected async loadItem(params: { name: string; namespace?: string }): Promise { - return this.api.get(params); - } - - @action - async load(params: { name: string; namespace?: string }): Promise { - const { name, namespace } = params; - let item: K | null | undefined = this.getByName(name, namespace); - - if (!item) { - item = await this.loadItem(params); - assert(item, "Failed to load item from kube"); - const newItems = this.sortItems([...this.items, item]); - - this.items.replace(newItems); - } - - return item; - } - - @action - async loadFromPath(resourcePath: string) { - const { namespace, name } = parseKubeApi(resourcePath); - - assert(name, "name must be part of resourcePath"); - - return this.load({ name, namespace }); - } - - protected async createItem(params: { name: string; namespace?: string }, data?: PartialDeep): Promise { - return this.api.create(params, data); - } - - async create(params: { name: string; namespace?: string }, data?: PartialDeep): Promise { - const newItem = await this.createItem(params, data); - - assert(newItem, "Failed to create item from kube"); - const items = this.sortItems([...this.items, newItem]); - - this.items.replace(items); - - return newItem; - } - - private postUpdate(newItem: K): K { - const index = this.items.findIndex(item => item.getId() === newItem.getId()); - - if (index < 0) { - this.items.push(newItem); - } else { - this.items[index] = newItem; - } - - return newItem; - } - - async patch(item: K, patch: JsonPatch): Promise { - const rawItem = await this.api.patch( - { - name: item.getName(), namespace: item.getNs(), - }, - patch, - "json", - ); - - assert(rawItem, `Failed to patch ${item.getScopedName()} of ${item.kind} ${item.apiVersion}`); - - return this.postUpdate(rawItem); - } - - async update(item: K, data: PartialDeep): Promise { - const rawItem = await this.api.update( - { - name: item.getName(), - namespace: item.getNs(), - }, - data, - ); - - assert(rawItem, `Failed to update ${item.getScopedName()} of ${item.kind} ${item.apiVersion}`); - - return this.postUpdate(rawItem); - } - - async remove(item: K) { - await this.api.delete({ name: item.getName(), namespace: item.getNs() }); - this.selectedItemsIds.delete(item.getId()); - } - - async removeSelectedItems() { - await Promise.all(this.selectedItems.map(this.remove)); - } - - async removeItems(items: K[]) { - await Promise.all(items.map(this.remove)); - } - - // collect items from watch-api events to avoid UI blowing up with huge streams of data - protected readonly eventsBuffer = observable.array>([], { deep: false }); - - protected bindWatchEventsUpdater(delay = 1000) { - reaction(() => [...this.eventsBuffer], this.updateFromEventsBuffer, { - delay, - }); - } - - subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}): Disposer { - if (this.api.isNamespaced) { - void (async () => { - try { - const loadedNamespaces = await Promise.race([ - rejectPromiseBy(abortController.signal), - waitUntilDefined(() => this.loadedNamespaces.get()), - ]); - - if (this.dependencies.context.isGlobalWatchEnabled() && loadedNamespaces.length === 0) { - this.watchNamespace("", abortController, { onLoadFailure }); - } else { - for (const namespace of loadedNamespaces) { - this.watchNamespace(namespace, abortController, { onLoadFailure }); - } - } - } catch (error) { - console.error(`[KUBE-OBJECT-STORE]: failed to subscribe to ${this.api.apiBase}`, error); - } - })(); - } else { - this.watchNamespace("", abortController, { onLoadFailure }); - } - - return () => abortController.abort(); - } - - private watchNamespace(namespace: string, abortController: AbortController, opts: KubeObjectStoreSubscribeParams) { - if (!this.api.getResourceVersion(namespace)) { - return; - } - - let timedRetry: NodeJS.Timeout; - const startNewWatch = () => this.api.watch({ - namespace, - abortController, - callback, - }); - - const signal = abortController.signal; - - const callback: KubeApiWatchCallback = (data, error) => { - if (!this.isLoaded || error?.type === "aborted") return; - - if (error instanceof Response) { - if (error.status === 404 || error.status === 401) { - // api has gone, or credentials are not permitted, let's not retry - return; - } - - // not sure what to do, best to retry - clearTimeout(timedRetry); - timedRetry = setTimeout(startNewWatch, 5000); - } else if (error instanceof KubeStatus && error.code === 410) { - clearTimeout(timedRetry); - // resourceVersion has gone, let's try to reload - timedRetry = setTimeout(() => { - ( - namespace - ? this.loadAll({ namespaces: [namespace], reqInit: { signal }, ...opts }) - : this.loadAll({ merge: false, reqInit: { signal }, ...opts }) - ).then(startNewWatch); - }, 1000); - } else if (error) { // not sure what to do, best to retry - clearTimeout(timedRetry); - timedRetry = setTimeout(startNewWatch, 5000); - } - - if (data) { - this.eventsBuffer.push(data); - } - }; - - signal.addEventListener("abort", () => clearTimeout(timedRetry)); - startNewWatch(); - } - - @action - protected updateFromEventsBuffer() { - const items = this.getItems(); - - for (const event of this.eventsBuffer.clear()) { - if (event.type === "ERROR") { - continue; - } - - try { - const { type, object } = event; - - if (!object.metadata?.uid) { - this.dependencies.logger.warn("[KUBE-STORE]: watch event did not have defined .metadata.uid, skipping", { event }); - // Other parts of the code will break if this happens - continue; - } - - const index = items.findIndex(item => item.getId() === object.metadata.uid); - const item = items[index]; - - switch (type) { - case "ADDED": - - // fallthrough - case "MODIFIED": { - const newItem = new this.api.objectConstructor(object); - - if (!item) { - items.push(newItem); - } else { - items[index] = newItem; - } - - break; - } - case "DELETED": - if (item) { - items.splice(index, 1); - } - break; - } - } catch (error) { - this.dependencies.logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); - } - } - - // update items - this.items.replace(this.sortItems(items.slice(-this.bufferSize))); - } -} diff --git a/src/common/k8s-api/kube-object.ts b/src/common/k8s-api/kube-object.ts deleted file mode 100644 index d88c19db89..0000000000 --- a/src/common/k8s-api/kube-object.ts +++ /dev/null @@ -1,694 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Base class for all kubernetes objects - -import moment from "moment"; -import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata } from "./kube-json-api"; -import { autoBind, formatDuration, hasOptionalTypedProperty, hasTypedProperty, isObject, isString, isNumber, bindPredicate, isTypedArray, isRecord, json } from "../utils"; -import type { ItemObject } from "../item.store"; -import type { Patch } from "rfc6902"; -import assert from "assert"; -import type { JsonObject } from "type-fest"; -import requestKubeObjectPatchInjectable from "./endpoints/resource-applier.api/request-patch.injectable"; -import { apiKubeInjectionToken } from "./api-kube"; -import requestKubeObjectCreationInjectable from "./endpoints/resource-applier.api/request-update.injectable"; -import { dump } from "js-yaml"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; - -export type KubeJsonApiDataFor = K extends KubeObject - ? KubeJsonApiData - : never; - -export interface KubeObjectConstructorData { - readonly kind?: string; - readonly namespaced?: boolean; - readonly apiBase?: string; -} - -export type KubeObjectConstructor = (new (data: Data) => K) & KubeObjectConstructorData; - -export interface OwnerReference { - apiVersion: string; - kind: string; - name: string; - uid: string; - controller?: boolean; - blockOwnerDeletion?: boolean; -} - -export type KubeTemplateObjectMetadata = Pick, "annotations" | "finalizers" | "generateName" | "labels" | "ownerReferences"> & { - name?: string; - namespace?: ScopedNamespace; -}; - -export interface BaseKubeJsonApiObjectMetadata { - /** - * Annotations is an unstructured key value map stored with a resource that may be set by - * external tools to store and retrieve arbitrary metadata. They are not queryable and should be - * preserved when modifying objects. - * - * More info: http://kubernetes.io/docs/user-guide/annotations - */ - annotations?: Partial>; - - /** - * The name of the cluster which the object belongs to. This is used to distinguish resources - * with same name and namespace in different clusters. This field is not set anywhere right now - * and apiserver is going to ignore it if set in create or update request. - */ - clusterName?: string; - - /** - * CreationTimestamp is a timestamp representing the server time when this object was created. It - * is not guaranteed to be set in happens-before order across separate operations. Clients may - * not set this value. It is represented in RFC3339 form and is in UTC. Populated by the system. - * - * More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata - */ - readonly creationTimestamp?: string; - - /** - * Number of seconds allowed for this object to gracefully terminate before it will be removed - * from the system. Only set when deletionTimestamp is also set. May only be shortened. - */ - readonly deletionGracePeriodSeconds?: number; - - /** - * DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field - * is set by the server when a graceful deletion is requested by the user, and is not directly - * settable by a client. The resource is expected to be deleted (no longer visible from resource - * lists, and not reachable by name) after the time in this field, once the finalizers list is - * empty. As long as the finalizers list contains items, deletion is blocked. Once the - * `deletionTimestamp` is set, this value may not be unset or be set further into the future, - * although it may be shortened or the resource may be deleted prior to this time. For example, - * a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a - * graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet - * will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the - * pod from the API. In the presence of network partitions, this object may still exist after - * this timestamp, until an administrator or automated process can determine the resource is - * fully terminated. If not set, graceful deletion of the object has not been requested. - * Populated by the system when a graceful deletion is requested. - * - * More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata - */ - readonly deletionTimestamp?: string; - - /** - * Must be empty before the object is deleted from the registry. Each entry is an identifier for - * the responsible component that will remove the entry from the list. If the deletionTimestamp - * of the object is non-nil, entries in this list can only be removed. Finalizers may be - * processed and removed in any order. Order is NOT enforced because it introduces significant - * risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder - * it. If the finalizer list is processed in order, then this can lead to a situation in which - * the component responsible for the first finalizer in the list is waiting for a signal (field - * value, external system, or other) produced by a component responsible for a finalizer later in - * the list, resulting in a deadlock. Without enforced ordering finalizers are free to order - * amongst themselves and are not vulnerable to ordering changes in the list. - */ - finalizers?: string[]; - - /** - * GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the - * Name field has not been provided. If this field is used, the name returned to the client will - * be different than the name passed. This value will also be combined with a unique suffix. The - * provided value has the same validation rules as the Name field, and may be truncated by the - * length of the suffix required to make the value unique on the server. If this field is - * specified and the generated name exists, the server will NOT return a 409 - instead, it will - * either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not - * be found in the time allotted, and the client should retry (optionally after the time indicated - * in the Retry-After header). Applied only if Name is not specified. - * - * More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency - */ - generateName?: string; - - /** - * A sequence number representing a specific generation of the desired state. Populated by the - * system. - */ - readonly generation?: number; - - /** - * Map of string keys and values that can be used to organize and categorize (scope and select) - * objects. May match selectors of replication controllers and services. - * - * More info: http://kubernetes.io/docs/user-guide/labels - */ - labels?: Partial>; - - /** - * ManagedFields maps workflow-id and version to the set of fields that are managed by that - * workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set - * or understand this field. A workflow can be the user's name, a controller's name, or the name - * of a specific apply path like "ci-cd". The set of fields is always in the version that the - * workflow used when modifying the object. - */ - managedFields?: unknown[]; - - /** - * Name must be unique within a namespace. Is required when creating resources, although some - * resources may allow a client to request the generation of an appropriate name automatically. - * Name is primarily intended for creation idempotence and configuration definition. - * - * More info: http://kubernetes.io/docs/user-guide/identifiers#names - */ - readonly name: string; - - /** - * Namespace defines the space within which each name must be unique. An empty namespace is - * equivalent to the "default" namespace, but "default" is the canonical representation. Not all - * objects are required to be scoped to a namespace - the value of this field for those objects - * will be empty. Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces - */ - readonly namespace?: ScopedNamespace; - - /** - * List of objects depended by this object. If ALL objects in the list have been deleted, this - * object will be garbage collected. If this object is managed by a controller, then an entry in - * this list will point to this controller, with the controller field set to true. There cannot - * be more than one managing controller. - */ - ownerReferences?: OwnerReference[]; - - /** - * An opaque value that represents the internal version of this object that can be used by - * clients to determine when objects have changed. May be used for optimistic concurrency, change - * detection, and the watch operation on a resource or set of resources. Clients must treat these - * values as opaque and passed unmodified back to the server. They may only be valid for a - * particular resource or set of resources. Populated by the system. Value must be treated as - * opaque by clients. - * - * More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency - */ - readonly resourceVersion?: string; - - /** - * SelfLink is a URL representing this object. Populated by the system. - */ - readonly selfLink?: string; - - /** - * UID is the unique in time and space value for this object. It is typically generated by the - * server on successful creation of a resource and is not allowed to change on PUT operations. - * Populated by the system. - * - * More info: http://kubernetes.io/docs/user-guide/identifiers#uids - */ - readonly uid?: string; - - [key: string]: unknown; -} - -export type KubeJsonApiObjectMetadata = BaseKubeJsonApiObjectMetadata & ( - Namespaced extends KubeObjectScope.Namespace - ? { readonly namespace: string } - : {} -); - -export type KubeObjectMetadata = KubeJsonApiObjectMetadata & { - readonly selfLink: string; - readonly uid: string; - readonly name: string; - readonly resourceVersion: string; -}; - -export type NamespaceScopedMetadata = KubeObjectMetadata; -export type ClusterScopedMetadata = KubeObjectMetadata; - -export interface KubeStatusData { - kind: string; - apiVersion: string; - code: number; - message?: string; - reason?: string; -} - -export function isKubeStatusData(object: unknown): object is KubeStatusData { - return isObject(object) - && hasTypedProperty(object, "kind", isString) - && hasTypedProperty(object, "apiVersion", isString) - && hasTypedProperty(object, "code", isNumber) - && hasOptionalTypedProperty(object, "message", isString) - && hasOptionalTypedProperty(object, "reason", isString) - && object.kind === "Status"; -} - -export class KubeStatus { - public readonly kind = "Status"; - public readonly apiVersion: string; - public readonly code: number; - public readonly message: string; - public readonly reason: string; - - constructor(data: KubeStatusData) { - this.apiVersion = data.apiVersion; - this.code = data.code; - this.message = data.message || ""; - this.reason = data.reason || ""; - } -} - -export interface BaseKubeObjectCondition { - /** - * Last time the condition transit from one status to another. - * - * @type Date - */ - lastTransitionTime?: string; - /** - * A human readable message indicating details about last transition. - */ - message?: string; - /** - * brief (usually one word) readon for the condition's last transition. - */ - reason?: string; - /** - * Status of the condition - */ - status: "True" | "False" | "Unknown"; - /** - * Type of the condition - */ - type: string; -} - -export interface KubeObjectStatus { - conditions?: BaseKubeObjectCondition[]; -} - -export type KubeMetaField = keyof KubeJsonApiObjectMetadata; - -export class KubeCreationError extends Error { - constructor(message: string, public data: any) { - super(message); - } -} - -export type LabelMatchExpression = { - /** - * The label key that the selector applies to. - */ - key: string; -} & ( - { - /** - * This represents the key's relationship to a set of values. - */ - operator: "Exists" | "DoesNotExist"; - values?: undefined; - } - | - { - operator: "In" | "NotIn"; - /** - * The set of values for to match according to the operator for the label. - */ - values: string[]; - } -); - -export interface Toleration { - key?: string; - operator?: string; - effect?: string; - value?: string; - tolerationSeconds?: number; -} - -export interface ObjectReference { - apiVersion?: string; - fieldPath?: string; - kind?: string; - name: string; - namespace?: string; - resourceVersion?: string; - uid?: string; -} - -export interface LocalObjectReference { - name: string; -} - -export interface TypedLocalObjectReference { - apiGroup?: string; - kind: string; - name: string; -} - -export interface NodeAffinity { - nodeSelectorTerms?: LabelSelector[]; - weight: number; - preference: LabelSelector; -} - -export interface PodAffinity { - labelSelector: LabelSelector; - topologyKey: string; -} - -export interface SpecificAffinity { - requiredDuringSchedulingIgnoredDuringExecution?: T[]; - preferredDuringSchedulingIgnoredDuringExecution?: T[]; -} - -export interface Affinity { - nodeAffinity?: SpecificAffinity; - podAffinity?: SpecificAffinity; - podAntiAffinity?: SpecificAffinity; -} - -export interface LabelSelector { - matchLabels?: Partial>; - matchExpressions?: LabelMatchExpression[]; -} - -export enum KubeObjectScope { - Namespace, - Cluster, -} -export type ScopedNamespace = ( - Namespaced extends KubeObjectScope.Namespace - ? string - : Namespaced extends KubeObjectScope.Cluster - ? undefined - : string | undefined -); - -const resourceApplierAnnotationsForFiltering = [ - "kubectl.kubernetes.io/last-applied-configuration", -]; - -const filterOutResourceApplierAnnotations = (label: string) => !resourceApplierAnnotationsForFiltering.some(key => label.startsWith(key)); - -export class KubeObject< - Metadata extends KubeObjectMetadata = KubeObjectMetadata, - Status = unknown, - Spec = unknown, -> implements ItemObject { - static readonly kind?: string; - static readonly namespaced?: boolean; - static readonly apiBase?: string; - - apiVersion!: string; - kind!: string; - metadata!: Metadata; - status?: Status; - spec!: Spec; - - static create< - Metadata extends KubeObjectMetadata = KubeObjectMetadata, - Status = unknown, - Spec = unknown, - >(data: KubeJsonApiData) { - return new KubeObject(data); - } - - static isNonSystem(item: KubeJsonApiData | KubeObject, unknown, unknown>) { - return !item.metadata.name?.startsWith("system:"); - } - - static isJsonApiData(object: unknown): object is KubeJsonApiData { - return ( - isObject(object) - && hasTypedProperty(object, "kind", isString) - && hasTypedProperty(object, "apiVersion", isString) - && hasTypedProperty(object, "metadata", KubeObject.isKubeJsonApiMetadata) - ); - } - - static isKubeJsonApiListMetadata(object: unknown): object is KubeJsonApiListMetadata { - return ( - isObject(object) - && hasOptionalTypedProperty(object, "resourceVersion", isString) - && hasOptionalTypedProperty(object, "selfLink", isString) - ); - } - - static isKubeJsonApiMetadata(object: unknown): object is KubeJsonApiObjectMetadata { - return ( - isObject(object) - && hasTypedProperty(object, "uid", isString) - && hasTypedProperty(object, "name", isString) - && hasTypedProperty(object, "resourceVersion", isString) - && hasOptionalTypedProperty(object, "selfLink", isString) - && hasOptionalTypedProperty(object, "namespace", isString) - && hasOptionalTypedProperty(object, "creationTimestamp", isString) - && hasOptionalTypedProperty(object, "continue", isString) - && hasOptionalTypedProperty(object, "finalizers", bindPredicate(isTypedArray, isString)) - && hasOptionalTypedProperty(object, "labels", bindPredicate(isRecord, isString, isString)) - && hasOptionalTypedProperty(object, "annotations", bindPredicate(isRecord, isString, isString)) - ); - } - - static isPartialJsonApiMetadata(object: unknown): object is Partial { - return ( - isObject(object) - && hasOptionalTypedProperty(object, "uid", isString) - && hasOptionalTypedProperty(object, "name", isString) - && hasOptionalTypedProperty(object, "resourceVersion", isString) - && hasOptionalTypedProperty(object, "selfLink", isString) - && hasOptionalTypedProperty(object, "namespace", isString) - && hasOptionalTypedProperty(object, "creationTimestamp", isString) - && hasOptionalTypedProperty(object, "continue", isString) - && hasOptionalTypedProperty(object, "finalizers", bindPredicate(isTypedArray, isString)) - && hasOptionalTypedProperty(object, "labels", bindPredicate(isRecord, isString, isString)) - && hasOptionalTypedProperty(object, "annotations", bindPredicate(isRecord, isString, isString)) - ); - } - - static isPartialJsonApiData(object: unknown): object is Partial { - return ( - isObject(object) - && hasOptionalTypedProperty(object, "kind", isString) - && hasOptionalTypedProperty(object, "apiVersion", isString) - && hasOptionalTypedProperty(object, "metadata", KubeObject.isPartialJsonApiMetadata) - ); - } - - static isJsonApiDataList(object: unknown, verifyItem: (val: unknown) => val is T): object is KubeJsonApiDataList { - return ( - isObject(object) - && hasTypedProperty(object, "kind", isString) - && hasTypedProperty(object, "apiVersion", isString) - && hasTypedProperty(object, "metadata", KubeObject.isKubeJsonApiListMetadata) - && hasTypedProperty(object, "items", bindPredicate(isTypedArray, verifyItem)) - ); - } - - static stringifyLabels(labels?: Partial>): string[] { - if (!labels) return []; - - return Object.entries(labels).map(([name, value]) => `${name}=${value}`); - } - - /** - * These must be RFC6902 compliant paths - */ - private static readonly nonEditablePathPrefixes = [ - "/metadata/managedFields", - "/status", - ]; - private static readonly nonEditablePaths = new Set([ - "/apiVersion", - "/kind", - "/metadata/name", - "/metadata/selfLink", - "/metadata/resourceVersion", - "/metadata/uid", - ...KubeObject.nonEditablePathPrefixes, - ]); - - constructor(data: KubeJsonApiData) { - if (typeof data !== "object") { - throw new TypeError(`Cannot create a KubeObject from ${typeof data}`); - } - - if (!data.metadata || typeof data.metadata !== "object") { - throw new KubeCreationError(`Cannot create a KubeObject from an object without metadata`, data); - } - - Object.assign(this, data); - autoBind(this); - } - - get selfLink() { - return this.metadata.selfLink; - } - - getId() { - return this.metadata.uid; - } - - getResourceVersion() { - return this.metadata.resourceVersion; - } - - getScopedName() { - const ns = this.getNs(); - const res = ns ? `${ns}/` : ""; - - return res + this.getName(); - } - - getName() { - return this.metadata.name; - } - - getNs(): Metadata["namespace"] { - // avoid "null" serialization via JSON.stringify when post data - return (this.metadata.namespace || undefined) as never; - } - - /** - * This function computes the number of milliseconds from the UNIX EPOCH to the - * creation timestamp of this object. - */ - getCreationTimestamp() { - if (!this.metadata.creationTimestamp) { - return Date.now(); - } - - return new Date(this.metadata.creationTimestamp).getTime(); - } - - /** - * @deprecated This function computes a new "now" on every call which might cause subtle issues if called multiple times - * - * NOTE: Generally you can use `getCreationTimestamp` instead. - */ - getTimeDiffFromNow(): number { - if (!this.metadata.creationTimestamp) { - return 0; - } - - return Date.now() - new Date(this.metadata.creationTimestamp).getTime(); - } - - /** - * @deprecated This function computes a new "now" on every call might cause subtle issues if called multiple times - * - * NOTE: this function also is not reactive to updates in the current time so it should not be used for renderering - */ - getAge(humanize = true, compact = true, fromNow = false): string | number { - if (fromNow) { - return moment(this.metadata.creationTimestamp).fromNow(); // "string", getTimeDiffFromNow() cannot be used - } - const diff = this.getTimeDiffFromNow(); - - if (humanize) { - return formatDuration(diff, compact); - } - - return diff; - } - - getFinalizers(): string[] { - return this.metadata.finalizers || []; - } - - getLabels(): string[] { - return KubeObject.stringifyLabels(this.metadata.labels); - } - - getAnnotations(filter = false): string[] { - const labels = KubeObject.stringifyLabels(this.metadata.annotations); - - if (!filter) { - return labels; - } - - return labels.filter(filterOutResourceApplierAnnotations); - } - - getOwnerRefs() { - const refs = this.metadata.ownerReferences || []; - const namespace = this.getNs(); - - return refs.map(ownerRef => ({ ...ownerRef, namespace })); - } - - getSearchFields() { - const { getName, getId, getNs, getAnnotations, getLabels } = this; - - return [ - getName(), - getNs(), - getId(), - ...getLabels(), - ...getAnnotations(true), - ]; - } - - toPlainObject() { - return json.parse(JSON.stringify(this)) as JsonObject; - } - - /** - * @deprecated use KubeApi.patch instead - */ - async patch(patch: Patch): Promise { - for (const op of patch) { - if (KubeObject.nonEditablePaths.has(op.path)) { - throw new Error(`Failed to update ${this.kind}: JSON pointer ${op.path} has been modified`); - } - - for (const pathPrefix of KubeObject.nonEditablePathPrefixes) { - if (op.path.startsWith(`${pathPrefix}/`)) { - throw new Error(`Failed to update ${this.kind}: Child JSON pointer of ${op.path} has been modified`); - } - } - } - - const di = getLegacyGlobalDiForExtensionApi(); - const requestKubeObjectPatch = di.inject(requestKubeObjectPatchInjectable); - const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch); - - if (!result.callWasSuccessful) { - throw new Error(result.error); - } - - return result.response; - } - - /** - * Perform a full update (or more specifically a replace) - * - * Note: this is brittle if `data` is not actually partial (but instead whole). - * As fields such as `resourceVersion` will probably out of date. This is a - * common race condition. - * - * @deprecated use KubeApi.update instead - */ - async update(data: Partial): Promise { - const di = getLegacyGlobalDiForExtensionApi(); - const requestKubeObjectCreation = di.inject(requestKubeObjectCreationInjectable); - const descriptor = dump({ - ...this.toPlainObject(), - ...data, - }); - - const result = await requestKubeObjectCreation(descriptor); - - if (!result.callWasSuccessful) { - throw new Error(result.error); - } - - return result.response; - } - - /** - * @deprecated use KubeApi.delete instead - */ - delete(params?: object) { - assert(this.selfLink, "selfLink must be present to delete self"); - - const di = getLegacyGlobalDiForExtensionApi(); - const apiKube = di.inject(apiKubeInjectionToken); - - return apiKube.del(this.selfLink, params); - } -} diff --git a/src/common/k8s-api/kube-watch-event.ts b/src/common/k8s-api/kube-watch-event.ts deleted file mode 100644 index 0f42bf4054..0000000000 --- a/src/common/k8s-api/kube-watch-event.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeStatusData } from "./kube-object"; - -export type IKubeWatchEvent = { - readonly type: "ADDED" | "MODIFIED" | "DELETED"; - readonly object: T; -} | { - readonly type: "ERROR"; - readonly object?: KubeStatusData; -}; - diff --git a/src/common/k8s-api/maybe-kube-api.injectable.ts b/src/common/k8s-api/maybe-kube-api.injectable.ts deleted file mode 100644 index 6717c00881..0000000000 --- a/src/common/k8s-api/maybe-kube-api.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 { apiKubeInjectionToken } from "./api-kube"; - -const maybeKubeApiInjectable = getInjectable({ - id: "maybe-kube-api", - instantiate: (di) => { - try { - return di.inject(apiKubeInjectionToken); - } catch { - return undefined; - } - }, -}); - -export default maybeKubeApiInjectable; diff --git a/src/common/k8s-api/selected-filter-namespaces.injectable.ts b/src/common/k8s-api/selected-filter-namespaces.injectable.ts deleted file mode 100644 index 6c70a665a4..0000000000 --- a/src/common/k8s-api/selected-filter-namespaces.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 namespaceStoreInjectable from "../../renderer/components/+namespaces/store.injectable"; -import { storesAndApisCanBeCreatedInjectionToken } from "./stores-apis-can-be-created.token"; - -const selectedFilterNamespacesInjectable = getInjectable({ - id: "selected-filter-namespaces", - instantiate: (di) => { - if (!di.inject(storesAndApisCanBeCreatedInjectionToken)) { - // Dummy value so that this works in all environments - return computed(() => []); - } - - const store = di.inject(namespaceStoreInjectable); - - return computed(() => [...store.contextNamespaces]); - }, -}); - -export default selectedFilterNamespacesInjectable; diff --git a/src/common/k8s-api/stores-apis-can-be-created.token.ts b/src/common/k8s-api/stores-apis-can-be-created.token.ts deleted file mode 100644 index 746f822c0c..0000000000 --- a/src/common/k8s-api/stores-apis-can-be-created.token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const storesAndApisCanBeCreatedInjectionToken = getInjectionToken({ - id: "stores-and-apis-can-be-created-token", -}); diff --git a/src/common/k8s-api/window-location.global-override-for-injectable.ts b/src/common/k8s-api/window-location.global-override-for-injectable.ts deleted file mode 100644 index 616e110c88..0000000000 --- a/src/common/k8s-api/window-location.global-override-for-injectable.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import windowLocationInjectable from "./window-location.injectable"; - -export default getGlobalOverride(windowLocationInjectable, () => ({ - host: "localhost", - port: "12345", -})); diff --git a/src/common/k8s-api/window-location.injectable.ts b/src/common/k8s-api/window-location.injectable.ts deleted file mode 100644 index 80bcd44be6..0000000000 --- a/src/common/k8s-api/window-location.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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"; - -const windowLocationInjectable = getInjectable({ - id: "window-location", - instantiate: () => { - const { host, port } = window.location; - - return { host, port }; - }, - causesSideEffects: true, -}); - -export default windowLocationInjectable; diff --git a/src/common/k8s/create-resource-stack.injectable.ts b/src/common/k8s/create-resource-stack.injectable.ts deleted file mode 100644 index 083d240b3a..0000000000 --- a/src/common/k8s/create-resource-stack.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 { KubernetesCluster } from "../catalog-entities"; -import readDirectoryInjectable from "../fs/read-directory.injectable"; -import readFileInjectable from "../fs/read-file.injectable"; -import { kubectlApplyAllInjectionToken, kubectlDeleteAllInjectionToken } from "../kube-helpers/channels"; -import loggerInjectable from "../logger.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; -import type { ResourceApplyingStack, ResourceStackDependencies } from "./resource-stack"; -import { ResourceStack } from "./resource-stack"; - -export type CreateResourceStack = (cluster: KubernetesCluster, name: string) => ResourceApplyingStack; - -const createResourceStackInjectable = getInjectable({ - id: "create-resource-stack", - instantiate: (di): CreateResourceStack => { - const deps: ResourceStackDependencies = { - joinPaths: di.inject(joinPathsInjectable), - kubectlApplyAll: di.inject(kubectlApplyAllInjectionToken), - kubectlDeleteAll: di.inject(kubectlDeleteAllInjectionToken), - logger: di.inject(loggerInjectable), - readDirectory: di.inject(readDirectoryInjectable), - readFile: di.inject(readFileInjectable), - }; - - return (cluster, name) => new ResourceStack(deps, cluster, name); - }, -}); - -export default createResourceStackInjectable; diff --git a/src/common/k8s/resource-stack.ts b/src/common/k8s/resource-stack.ts deleted file mode 100644 index 771b48b413..0000000000 --- a/src/common/k8s/resource-stack.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import hb from "handlebars"; -import type { KubernetesCluster } from "../catalog-entities"; -import yaml from "js-yaml"; -import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import productNameInjectable from "../vars/product-name.injectable"; -import type { AsyncResult } from "../utils/async-result"; -import type { Logger } from "../logger"; -import type { KubectlApplyAll, KubectlDeleteAll } from "../kube-helpers/channels"; -import type { ReadDirectory } from "../fs/read-directory.injectable"; -import type { JoinPaths } from "../path/join-paths.injectable"; -import type { ReadFile } from "../fs/read-file.injectable"; -import { hasTypedProperty, isObject } from "../utils"; - -export interface ResourceApplyingStack { - kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise; - kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise; -} - -export interface ResourceStackDependencies { - readonly logger: Logger; - kubectlApplyAll: KubectlApplyAll; - kubectlDeleteAll: KubectlDeleteAll; - readDirectory: ReadDirectory; - joinPaths: JoinPaths; - readFile: ReadFile; -} - -export class ResourceStack { - constructor( - protected readonly dependencies: ResourceStackDependencies, - protected readonly cluster: KubernetesCluster, - protected readonly name: string, - ) {} - - /** - * - * @param folderPath folder path that is searched for files defining kubernetes resources. - * @param templateContext sets the template parameters that are to be applied to any templated kubernetes resources that are to be applied. - */ - async kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise { - const resources = await this.renderTemplates(folderPath, templateContext); - const result = await this.applyResources(resources, extraArgs); - - if (result.callWasSuccessful) { - return result.response; - } - - this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to apply resources: ${result.error}`); - - return ""; - } - - /** - * - * @param folderPath folder path that is searched for files defining kubernetes resources. - * @param templateContext sets the template parameters that are to be applied to any templated kubernetes resources that are to be applied. - */ - async kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise { - const resources = await this.renderTemplates(folderPath, templateContext); - const result = await this.deleteResources(resources, extraArgs); - - if (result.callWasSuccessful) { - return result.response; - } - - this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to delete resources: ${result.error}`); - - return ""; - } - - protected async applyResources(resources: string[], extraArgs: string[] = []): Promise> { - const kubectlArgs = [...extraArgs, ...this.getAdditionalArgs(extraArgs)]; - - return this.dependencies.kubectlApplyAll({ - clusterId: this.cluster.getId(), - resources, - extraArgs: kubectlArgs, - }); - } - - protected async deleteResources(resources: string[], extraArgs: string[] = []): Promise> { - const kubectlArgs = [...extraArgs, ...this.getAdditionalArgs(extraArgs)]; - - return this.dependencies.kubectlDeleteAll({ - clusterId: this.cluster.getId(), - resources, - extraArgs: kubectlArgs, - }); - } - - protected getAdditionalArgs(kubectlArgs: string[]): string[] { - if (!kubectlArgs.includes("-l") && !kubectlArgs.includes("--label")) { - return ["-l", `app.kubernetes.io/name=${this.name}`]; - } - - return []; - } - - protected async renderTemplates(folderPath: string, templateContext: any): Promise { - const resources: string[] = []; - const di = getLegacyGlobalDiForExtensionApi(); - const productName = di.inject(productNameInjectable); - - this.dependencies.logger.info(`[RESOURCE-STACK]: render templates from ${folderPath}`); - const files = await this.dependencies.readDirectory(folderPath); - - for (const filename of files) { - const file = this.dependencies.joinPaths(folderPath, filename); - const raw = await this.dependencies.readFile(file); - const data = ( - filename.endsWith(".hb") - ? hb.compile(raw)(templateContext) - : raw - ).trim(); - - if (!data) { - continue; - } - - for (const entry of yaml.loadAll(data)) { - if (typeof entry !== "object" || !entry) { - continue; - } - - if (hasTypedProperty(entry, "metadata", isObject)) { - const labels = (entry.metadata.labels ??= {}) as Partial>; - - labels["app.kubernetes.io/name"] = this.name; - labels["app.kubernetes.io/managed-by"] = productName; - labels["app.kubernetes.io/created-by"] = "resource-stack"; - } - - resources.push(yaml.dump(entry)); - } - } - - return resources; - } -} diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts deleted file mode 100644 index c439c29d16..0000000000 --- a/src/common/kube-helpers.ts +++ /dev/null @@ -1,266 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeConfig } from "@kubernetes/client-node"; -import yaml from "js-yaml"; -import type { Cluster, Context, User } from "@kubernetes/client-node/dist/config_types"; -import { newClusters, newContexts, newUsers } from "@kubernetes/client-node/dist/config_types"; -import { isDefined } from "./utils"; -import Joi from "joi"; -import type { PartialDeep } from "type-fest"; - -const clusterSchema = Joi.object({ - name: Joi - .string() - .min(1) - .required(), - cluster: Joi - .object({ - server: Joi - .string() - .min(1) - .required(), - }) - .required(), -}); - -const userSchema = Joi.object({ - name: Joi.string() - .min(1) - .required(), -}); - -const contextSchema = Joi.object({ - name: Joi.string() - .min(1) - .required(), - context: Joi.object({ - cluster: Joi.string() - .min(1) - .required(), - user: Joi.string() - .min(1) - .required(), - }), -}); - -const kubeConfigSchema = Joi.object({ - users: Joi - .array() - .items(userSchema) - .optional(), - clusters: Joi - .array() - .items(clusterSchema) - .optional(), - contexts: Joi - .array() - .items(contextSchema) - .optional(), - "current-context": Joi - .string() - .min(1) - .optional(), -}) - .required(); - -interface KubeConfigOptions { - clusters: Cluster[]; - users: User[]; - contexts: Context[]; - currentContext?: string; -} - -interface OptionsResult { - options: KubeConfigOptions; - error: Joi.ValidationError | undefined; -} - -function loadToOptions(rawYaml: string): OptionsResult { - const parsed = yaml.load(rawYaml); - const { error } = kubeConfigSchema.validate(parsed, { - abortEarly: false, - allowUnknown: true, - }); - const { value } = kubeConfigSchema.validate(parsed, { - abortEarly: false, - allowUnknown: true, - stripUnknown: { - arrays: true, - }, - }); - const { - clusters: rawClusters, - users: rawUsers, - contexts: rawContexts, - "current-context": currentContext, - } = value ?? {}; - const clusters = newClusters(rawClusters); - const users = newUsers(rawUsers); - const contexts = newContexts(rawContexts); - - return { - options: { clusters, users, contexts, currentContext }, - error, - }; -} - -export function loadFromOptions(options: KubeConfigOptions): KubeConfig { - const kc = new KubeConfig(); - - // need to load using the kubernetes client to generate a kubeconfig object - kc.loadFromOptions(options); - - return kc; -} - -export interface ConfigResult { - config: KubeConfig; - error: Joi.ValidationError | undefined; -} - -export function loadConfigFromString(content: string): ConfigResult { - const { options, error } = loadToOptions(content); - - return { - config: loadFromOptions(options), - error, - }; -} - -export interface SplitConfigEntry { - config: KubeConfig; - validationResult: ValidateKubeConfigResult; -} - -/** - * Breaks kube config into several configs. Each context as it own KubeConfig object - */ -export function splitConfig(kubeConfig: KubeConfig): SplitConfigEntry[] { - return kubeConfig.getContexts().map(ctx => { - const config = new KubeConfig(); - const cluster = kubeConfig.getCluster(ctx.cluster); - const user = kubeConfig.getUser(ctx.user); - const context = kubeConfig.getContextObject(ctx.name); - - if (cluster) { - config.addCluster(cluster); - } - - if (user) { - config.addUser(user); - } - - if (context) { - config.addContext(context); - } - - config.setCurrentContext(ctx.name); - - return { - config, - validationResult: validateKubeConfig(config, ctx.name), - }; - }); -} - -/** - * Pretty format the object as human readable yaml, such as would be on the filesystem - * @param kubeConfig The kubeconfig object to format as pretty yaml - * @returns The yaml representation of the kubeconfig object - */ -export function dumpConfigYaml(kubeConfig: PartialDeep): string { - const clusters = kubeConfig.clusters - ?.filter(isDefined) - .map(cluster => ({ - name: cluster.name, - cluster: { - "certificate-authority-data": cluster.caData, - "certificate-authority": cluster.caFile, - server: cluster.server, - "insecure-skip-tls-verify": cluster.skipTLSVerify, - }, - })); - const contexts = kubeConfig.contexts - ?.filter(isDefined) - .map(context => ({ - name: context.name, - context: { - cluster: context.cluster, - user: context.user, - namespace: context.namespace, - }, - })); - const users = kubeConfig.users - ?.filter(isDefined) - .map(user => ({ - name: user.name, - user: { - "client-certificate-data": user.certData, - "client-certificate": user.certFile, - "client-key-data": user.keyData, - "client-key": user.keyFile, - "auth-provider": user.authProvider, - exec: user.exec, - token: user.token, - username: user.username, - password: user.password, - }, - })); - const config = { - apiVersion: "v1", - kind: "Config", - preferences: {}, - "current-context": kubeConfig.currentContext, - clusters, - contexts, - users, - }; - - // skipInvalid: true makes dump ignore undefined values - return yaml.dump(config, { skipInvalid: true }); -} - -export type ValidateKubeConfigResult = { - error: Error; -} | { - error?: undefined; - context: Context; - cluster: Cluster; - user: User; -}; - -/** - * Checks if `config` has valid `Context`, `User`, `Cluster`, and `exec` fields (if present when required) - * - * Note: This function returns an error instead of throwing it, returning `undefined` if the validation passes - */ -export function validateKubeConfig(config: KubeConfig, contextName: string): ValidateKubeConfigResult { - const context = config.getContextObject(contextName); - - if (!context) { - return { - error: new Error(`No valid context object provided in kubeconfig for context '${contextName}'`), - }; - } - - const cluster = config.getCluster(context.cluster); - - if (!cluster) { - return { - error: new Error(`No valid cluster object provided in kubeconfig for context '${contextName}'`), - }; - } - - const user = config.getUser(context.user); - - if (!user) { - return { - error: new Error(`No valid user object provided in kubeconfig for context '${contextName}'`), - }; - } - - return { cluster, user, context }; -} diff --git a/src/common/kube-helpers/channels.ts b/src/common/kube-helpers/channels.ts deleted file mode 100644 index 4782f64367..0000000000 --- a/src/common/kube-helpers/channels.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { Asyncify } from "type-fest"; -import type { RequestChannelHandler } from "../../main/utils/channel/channel-listeners/listener-tokens"; -import type { ClusterId } from "../cluster-types"; -import type { AsyncResult } from "../utils/async-result"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; - -export interface KubectlApplyAllArgs { - clusterId: ClusterId; - resources: string[]; - extraArgs: string[]; -} - -export const kubectlApplyAllChannel: RequestChannel> = { - id: "kubectl-apply-all", -}; - -export type KubectlApplyAll = Asyncify>; - -export const kubectlApplyAllInjectionToken = getInjectionToken({ - id: "kubectl-apply-all", -}); - -export interface KubectlDeleteAllArgs { - clusterId: ClusterId; - resources: string[]; - extraArgs: string[]; -} - -export const kubectlDeleteAllChannel: RequestChannel> = { - id: "kubectl-delete-all", -}; - -export type KubectlDeleteAll = Asyncify>; - -export const kubectlDeleteAllInjectionToken = getInjectionToken({ - id: "kubectl-delete-all", -}); diff --git a/src/common/kube-helpers/load-config-from-file.injectable.ts b/src/common/kube-helpers/load-config-from-file.injectable.ts deleted file mode 100644 index afa9d3c070..0000000000 --- a/src/common/kube-helpers/load-config-from-file.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 readFileInjectable from "../fs/read-file.injectable"; -import type { ConfigResult } from "../kube-helpers"; -import { loadConfigFromString } from "../kube-helpers"; -import resolveTildeInjectable from "../path/resolve-tilde.injectable"; - -export type LoadConfigfromFile = (filePath: string) => Promise; - -const loadConfigfromFileInjectable = getInjectable({ - id: "load-configfrom-file", - instantiate: (di): LoadConfigfromFile => { - const readFile = di.inject(readFileInjectable); - const resolveTilde = di.inject(resolveTildeInjectable); - - return async (filePath) => loadConfigFromString(await readFile(resolveTilde(filePath))); - }, -}); - -export default loadConfigfromFileInjectable; diff --git a/src/common/library.ts b/src/common/library.ts deleted file mode 100644 index 960cb47ca2..0000000000 --- a/src/common/library.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import applicationInformationToken from "./vars/application-information-token"; -import type { ApplicationInformation } from "./vars/application-information-token"; -import { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token"; - -// @experimental -export { - applicationInformationToken, - ApplicationInformation, - bundledExtensionInjectionToken, -}; diff --git a/src/common/log-error.global-override-for-injectable.ts b/src/common/log-error.global-override-for-injectable.ts deleted file mode 100644 index e3a03c2802..0000000000 --- a/src/common/log-error.global-override-for-injectable.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getGlobalOverrideForFunction } from "./test-utils/get-global-override-for-function"; -import logErrorInjectable from "./log-error.injectable"; - -// Note: this should remain as it is, and throw if called. Logging error is something -// that cannot happen without a unit test explicitly causing it. It cannot be allowed -// to happen without author of unit test knowing it. -export default getGlobalOverrideForFunction(logErrorInjectable); diff --git a/src/common/log-error.injectable.ts b/src/common/log-error.injectable.ts deleted file mode 100644 index 4fab2cd546..0000000000 --- a/src/common/log-error.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 loggerInjectable from "./logger.injectable"; - -const logErrorInjectable = getInjectable({ - id: "log-error", - instantiate: (di) => di.inject(loggerInjectable).error, -}); - -export default logErrorInjectable; diff --git a/src/common/logger.global-override-for-injectable.ts b/src/common/logger.global-override-for-injectable.ts deleted file mode 100644 index cad548cd22..0000000000 --- a/src/common/logger.global-override-for-injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import loggerInjectable from "./logger.injectable"; -import { getGlobalOverride } from "./test-utils/get-global-override"; -import { noop } from "./utils"; - -export default getGlobalOverride(loggerInjectable, () => ({ - warn: noop, - debug: noop, - error: noop, - info: noop, - silly: noop, -})); diff --git a/src/common/logger.injectable.ts b/src/common/logger.injectable.ts deleted file mode 100644 index 8e9dd2a6a7..0000000000 --- a/src/common/logger.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 { createLogger, format } from "winston"; -import type { Logger } from "./logger"; -import { loggerTransportInjectionToken } from "./logger/transports"; - -const loggerInjectable = getInjectable({ - id: "logger", - instantiate: (di): Logger => { - const baseLogger = createLogger({ - format: format.combine( - format.splat(), - format.simple(), - ), - transports: di.injectMany(loggerTransportInjectionToken), - }); - - return { - debug: (message, ...data) => baseLogger.debug(message, ...data), - info: (message, ...data) => baseLogger.info(message, ...data), - warn: (message, ...data) => baseLogger.warn(message, ...data), - error: (message, ...data) => baseLogger.error(message, ...data), - silly: (message, ...data) => baseLogger.silly(message, ...data), - }; - }, -}); - -export default loggerInjectable; diff --git a/src/common/logger.ts b/src/common/logger.ts deleted file mode 100644 index ad81271bfa..0000000000 --- a/src/common/logger.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -export interface Logger { - info: (message: string, ...args: any) => void; - error: (message: string, ...args: any) => void; - debug: (message: string, ...args: any) => void; - warn: (message: string, ...args: any) => void; - silly: (message: string, ...args: any) => void; -} diff --git a/src/common/logger/prefixed-logger.injectable.ts b/src/common/logger/prefixed-logger.injectable.ts deleted file mode 100644 index 36f86c532f..0000000000 --- a/src/common/logger/prefixed-logger.injectable.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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 type { Logger } from "../logger"; -import loggerInjectable from "../logger.injectable"; - -const prefixedLoggerInjectable = getInjectable({ - id: "prefixed-logger", - instantiate: (di, prefix): Logger => { - const logger = di.inject(loggerInjectable); - - return { - debug: (message, ...args) => { - logger.debug(`[${prefix}]: ${message}`, ...args); - }, - error: (message, ...args) => { - logger.error(`[${prefix}]: ${message}`, ...args); - }, - info: (message, ...args) => { - logger.info(`[${prefix}]: ${message}`, ...args); - }, - silly: (message, ...args) => { - logger.silly(`[${prefix}]: ${message}`, ...args); - }, - warn: (message, ...args) => { - logger.warn(`[${prefix}]: ${message}`, ...args); - }, - }; - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, prefix: string) => prefix, - }), -}); - -export default prefixedLoggerInjectable; diff --git a/src/common/logger/transports.ts b/src/common/logger/transports.ts deleted file mode 100644 index 1407eb91b8..0000000000 --- a/src/common/logger/transports.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type TransportStream from "winston-transport"; - -export const loggerTransportInjectionToken = getInjectionToken({ - id: "logger-transport", -}); diff --git a/src/common/os/home-directory-path.injectable.ts b/src/common/os/home-directory-path.injectable.ts deleted file mode 100644 index 83b4b0cdff..0000000000 --- a/src/common/os/home-directory-path.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 userInfoInjectable from "../user-store/user-info.injectable"; - -const homeDirectoryPathInjectable = getInjectable({ - id: "home-directory-path", - instantiate: (di) => di.inject(userInfoInjectable).homedir, -}); - -export default homeDirectoryPathInjectable; diff --git a/src/common/os/temp-directory-path.global-override-for-injectable.ts b/src/common/os/temp-directory-path.global-override-for-injectable.ts deleted file mode 100644 index 05615644f9..0000000000 --- a/src/common/os/temp-directory-path.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import tempDirectoryPathInjectable from "./temp-directory-path.injectable"; - -export default getGlobalOverride(tempDirectoryPathInjectable, () => "/some-temp-directory"); diff --git a/src/common/os/temp-directory-path.injectable.ts b/src/common/os/temp-directory-path.injectable.ts deleted file mode 100644 index 46fc5db67d..0000000000 --- a/src/common/os/temp-directory-path.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { tmpdir } from "os"; - -const tempDirectoryPathInjectable = getInjectable({ - id: "temp-directory-path", - instantiate: () => tmpdir(), - causesSideEffects: true, -}); - -export default tempDirectoryPathInjectable; diff --git a/src/common/path/get-absolute-path.global-override-for-injectable.ts b/src/common/path/get-absolute-path.global-override-for-injectable.ts deleted file mode 100644 index 15f377cb2c..0000000000 --- a/src/common/path/get-absolute-path.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getAbsolutePathInjectable from "./get-absolute-path.injectable"; - -export default getGlobalOverride(getAbsolutePathInjectable, () => path.posix.resolve); diff --git a/src/common/path/get-absolute-path.injectable.ts b/src/common/path/get-absolute-path.injectable.ts deleted file mode 100644 index 8919605942..0000000000 --- a/src/common/path/get-absolute-path.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 path from "path"; - -export type GetAbsolutePath = (...args: string[]) => string; - -const getAbsolutePathInjectable = getInjectable({ - id: "get-absolute-path", - - instantiate: (): GetAbsolutePath => path.resolve, - - // This causes side effect e.g. Windows creates different kinds of - // absolute paths than linux - causesSideEffects: true, -}); - -export default getAbsolutePathInjectable; diff --git a/src/common/path/get-basename.global-override-for-injectable.ts b/src/common/path/get-basename.global-override-for-injectable.ts deleted file mode 100644 index 913ec9c5c2..0000000000 --- a/src/common/path/get-basename.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getBasenameOfPathInjectable from "./get-basename.injectable"; - -export default getGlobalOverride(getBasenameOfPathInjectable, () => path.posix.basename); diff --git a/src/common/path/get-basename.injectable.ts b/src/common/path/get-basename.injectable.ts deleted file mode 100644 index be92bde7f5..0000000000 --- a/src/common/path/get-basename.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 path from "path"; - -export type GetBasenameOfPath = (path: string) => string; - -const getBasenameOfPathInjectable = getInjectable({ - id: "get-basename-of-path", - instantiate: (): GetBasenameOfPath => path.basename, - causesSideEffects: true, -}); - -export default getBasenameOfPathInjectable; diff --git a/src/common/path/get-dirname.global-override-for-injectable.ts b/src/common/path/get-dirname.global-override-for-injectable.ts deleted file mode 100644 index ed694de182..0000000000 --- a/src/common/path/get-dirname.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getDirnameOfPathInjectable from "./get-dirname.injectable"; - -export default getGlobalOverride(getDirnameOfPathInjectable, () => path.posix.dirname); diff --git a/src/common/path/get-dirname.injectable.ts b/src/common/path/get-dirname.injectable.ts deleted file mode 100644 index 93b4496767..0000000000 --- a/src/common/path/get-dirname.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 path from "path"; - -export type GetDirnameOfPath = (path: string) => string; - -const getDirnameOfPathInjectable = getInjectable({ - id: "get-dirname-of-path", - instantiate: (): GetDirnameOfPath => path.dirname, - causesSideEffects: true, -}); - -export default getDirnameOfPathInjectable; diff --git a/src/common/path/get-relative-path.global-override-for-injectable.ts b/src/common/path/get-relative-path.global-override-for-injectable.ts deleted file mode 100644 index 9e96b70301..0000000000 --- a/src/common/path/get-relative-path.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getRelativePathInjectable from "./get-relative-path.injectable"; - -export default getGlobalOverride(getRelativePathInjectable, () => path.posix.relative); diff --git a/src/common/path/get-relative-path.injectable.ts b/src/common/path/get-relative-path.injectable.ts deleted file mode 100644 index 18b5d832de..0000000000 --- a/src/common/path/get-relative-path.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 path from "path"; - -export type GetRelativePath = (from: string, to: string) => string; - -const getRelativePathInjectable = getInjectable({ - id: "get-relative-path", - instantiate: (): GetRelativePath => path.relative, - causesSideEffects: true, -}); - -export default getRelativePathInjectable; diff --git a/src/common/path/is-logical-child-path.injectable.ts b/src/common/path/is-logical-child-path.injectable.ts deleted file mode 100644 index 1a52b66c0f..0000000000 --- a/src/common/path/is-logical-child-path.injectable.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 getAbsolutePathInjectable from "./get-absolute-path.injectable"; -import getDirnameOfPathInjectable from "./get-dirname.injectable"; - -/** - * Checks if `testPath` represents a potential filesystem entry that would be - * logically "within" the `parentPath` directory. - * - * This function will return `true` in the above case, and `false` otherwise. - * It will return `false` if the two paths are the same (after resolving them). - * - * The function makes no FS calls and is platform dependant. Meaning that the - * results are only guaranteed to be correct for the platform you are running - * on. - * @param parentPath The known path of a directory - * @param testPath The path that is to be tested - */ -export type IsLogicalChildPath = (parentPath: string, testPath: string) => boolean; - -const isLogicalChildPathInjectable = getInjectable({ - id: "is-logical-child-path", - instantiate: (di): IsLogicalChildPath => { - const getAbsolutePath = di.inject(getAbsolutePathInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - - return (parentPath, testPath) => { - const resolvedParentPath = getAbsolutePath(parentPath); - let resolvedTestPath = getAbsolutePath(testPath); - - if (resolvedParentPath === resolvedTestPath) { - return false; - } - - while (resolvedTestPath.length >= resolvedParentPath.length) { - if (resolvedTestPath === resolvedParentPath) { - return true; - } - - resolvedTestPath = getDirnameOfPath(resolvedTestPath); - } - - return false; - }; - }, -}); - -export default isLogicalChildPathInjectable; diff --git a/src/common/path/join-paths.global-override-for-injectable.ts b/src/common/path/join-paths.global-override-for-injectable.ts deleted file mode 100644 index d3e9d5e4c2..0000000000 --- a/src/common/path/join-paths.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import joinPathsInjectable from "./join-paths.injectable"; - -export default getGlobalOverride(joinPathsInjectable, () => path.posix.join); diff --git a/src/common/path/join-paths.injectable.ts b/src/common/path/join-paths.injectable.ts deleted file mode 100644 index dc63b48307..0000000000 --- a/src/common/path/join-paths.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 path from "path"; - -export type JoinPaths = (...args: string[]) => string; - -const joinPathsInjectable = getInjectable({ - id: "join-paths", - instantiate: (): JoinPaths => path.join, - - // This causes side effect e.g. Windows uses different separator than e.g. linux - causesSideEffects: true, -}); - -export default joinPathsInjectable; diff --git a/src/common/path/parse.global-override-for-injectable.ts b/src/common/path/parse.global-override-for-injectable.ts deleted file mode 100644 index fad97db696..0000000000 --- a/src/common/path/parse.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import parsePathInjectable from "./parse.injectable"; - -export default getGlobalOverride(parsePathInjectable, () => path.posix.parse); diff --git a/src/common/path/parse.injectable.ts b/src/common/path/parse.injectable.ts deleted file mode 100644 index a32dfb3fa5..0000000000 --- a/src/common/path/parse.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 path from "path"; - -const parsePathInjectable = getInjectable({ - id: "parse-path", - instantiate: () => path.parse, - causesSideEffects: true, -}); - -export default parsePathInjectable; diff --git a/src/common/path/resolve-path.injectable.ts b/src/common/path/resolve-path.injectable.ts deleted file mode 100644 index 75a1e98c59..0000000000 --- a/src/common/path/resolve-path.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 getAbsolutePathInjectable from "./get-absolute-path.injectable"; -import resolveTildeInjectable from "./resolve-tilde.injectable"; - -export type ResolvePath = (path: string) => string; - -const resolvePathInjectable = getInjectable({ - id: "resolve-path", - instantiate: (di): ResolvePath => { - const getAbsolutePath = di.inject(getAbsolutePathInjectable); - const resolveTilde = di.inject(resolveTildeInjectable); - - return (filePath) => getAbsolutePath(resolveTilde(filePath)); - }, -}); - -export default resolvePathInjectable; diff --git a/src/common/path/resolve-tilde.injectable.ts b/src/common/path/resolve-tilde.injectable.ts deleted file mode 100644 index 86d267aa4f..0000000000 --- a/src/common/path/resolve-tilde.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 homeDirectoryPathInjectable from "../os/home-directory-path.injectable"; -import fileSystemSeparatorInjectable from "./separator.injectable"; - -export type ResolveTilde = (path: string) => string; - -const resolveTildeInjectable = getInjectable({ - id: "resolve-tilde", - instantiate: (di): ResolveTilde => { - const homeDirectoryPath = di.inject(homeDirectoryPathInjectable); - const fileSystemSeparator = di.inject(fileSystemSeparatorInjectable); - - return (filePath) => { - if (filePath === "~") { - return homeDirectoryPath; - } - - if (filePath === `~${fileSystemSeparator}`) { - return `${homeDirectoryPath}${filePath.slice(1)}`; - } - - return filePath; - }; - }, -}); - -export default resolveTildeInjectable; diff --git a/src/common/path/separator.global-override-for-injectable.ts b/src/common/path/separator.global-override-for-injectable.ts deleted file mode 100644 index 655f8908b0..0000000000 --- a/src/common/path/separator.global-override-for-injectable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { getGlobalOverride } from "../test-utils/get-global-override"; -import fileSystemSeparatorInjectable from "./separator.injectable"; - -export default getGlobalOverride(fileSystemSeparatorInjectable, () => path.posix.sep); diff --git a/src/common/path/separator.injectable.ts b/src/common/path/separator.injectable.ts deleted file mode 100644 index 5b0413b56f..0000000000 --- a/src/common/path/separator.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 path from "path"; - -const fileSystemSeparatorInjectable = getInjectable({ - id: "file-system-separator", - instantiate: () => path.sep, - causesSideEffects: true, -}); - -export default fileSystemSeparatorInjectable; diff --git a/src/common/protocol-handler/error.ts b/src/common/protocol-handler/error.ts deleted file mode 100644 index 707c8d420b..0000000000 --- a/src/common/protocol-handler/error.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type Url from "url-parse"; - -export enum RoutingErrorType { - INVALID_PROTOCOL = "invalid-protocol", - INVALID_HOST = "invalid-host", - INVALID_PATHNAME = "invalid-pathname", - NO_HANDLER = "no-handler", - NO_EXTENSION_ID = "no-ext-id", - MISSING_EXTENSION = "missing-ext", -} - -export class RoutingError extends Error { - /** - * Will be set if the routing error originated in an extension route table - */ - public extensionName?: string; - - constructor(public type: RoutingErrorType, public url: Url) { - super("routing error"); - } - - toString(): string { - switch (this.type) { - case RoutingErrorType.INVALID_HOST: - return "invalid host"; - case RoutingErrorType.INVALID_PROTOCOL: - return "invalid protocol"; - case RoutingErrorType.INVALID_PATHNAME: - return "invalid pathname"; - case RoutingErrorType.NO_EXTENSION_ID: - return "no extension ID"; - case RoutingErrorType.MISSING_EXTENSION: - return "extension not found"; - default: - return `unknown error: ${this.type}`; - } - } -} diff --git a/src/common/protocol-handler/index.ts b/src/common/protocol-handler/index.ts deleted file mode 100644 index b329c09046..0000000000 --- a/src/common/protocol-handler/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./error"; -export * from "./router"; diff --git a/src/common/protocol-handler/registration.ts b/src/common/protocol-handler/registration.ts deleted file mode 100644 index 9fdf390bee..0000000000 --- a/src/common/protocol-handler/registration.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * ProtocolHandlerRegistration is the data required for an extension to register - * a handler to a specific path or dynamic path. - */ -export interface ProtocolHandlerRegistration { - pathSchema: string; - handler: RouteHandler; -} - -/** - * The collection of the dynamic parts of a URI which initiated a `lens://` - * protocol request - */ -export interface RouteParams { - /** - * the parts of the URI query string - */ - search: Record; - - /** - * the matching parts of the path. The dynamic parts of the URI path. - */ - pathname: Record; - - /** - * if the most specific path schema that is matched does not cover the whole - * of the URI's path. Then this field will be set to the remaining path - * segments. - * - * Example: - * - * If the path schema `/landing/:type` is the matched schema for the URI - * `/landing/soft/easy` then this field will be set to `"/easy"`. - */ - tail?: string; -} - -/** - * RouteHandler represents the function signature of the handler function for - * `lens://` protocol routing. - */ -export interface RouteHandler { - (params: RouteParams): void; -} diff --git a/src/common/protocol-handler/router.ts b/src/common/protocol-handler/router.ts deleted file mode 100644 index 8c9915b287..0000000000 --- a/src/common/protocol-handler/router.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { match } from "react-router"; -import { matchPath } from "react-router"; -import { countBy } from "lodash"; -import { isDefined, iter } from "../utils"; -import { pathToRegexp } from "path-to-regexp"; -import type Url from "url-parse"; -import { RoutingError, RoutingErrorType } from "./error"; -import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store"; -import type { ExtensionLoader } from "../../extensions/extension-loader"; -import type { LensExtension } from "../../extensions/lens-extension"; -import type { RouteHandler, RouteParams } from "./registration"; -import { when } from "mobx"; -import { ipcRenderer } from "electron"; -import type { Logger } from "../logger"; - -// IPC channel for protocol actions. Main broadcasts the open-url events to this channel. -export const ProtocolHandlerIpcPrefix = "protocol-handler"; - -export const ProtocolHandlerInternal = `${ProtocolHandlerIpcPrefix}:internal`; -export const ProtocolHandlerExtension = `${ProtocolHandlerIpcPrefix}:extension`; -export const ProtocolHandlerInvalid = `${ProtocolHandlerIpcPrefix}:invalid`; - -/** - * These two names are long and cumbersome by design so as to decrease the chances - * of an extension using the same names. - * - * Though under the current (2021/01/18) implementation, these are never matched - * against in the final matching so their names are less of a concern. - */ -export const EXTENSION_PUBLISHER_MATCH = "LENS_INTERNAL_EXTENSION_PUBLISHER_MATCH"; -export const EXTENSION_NAME_MATCH = "LENS_INTERNAL_EXTENSION_NAME_MATCH"; - -/** - * Returned from routing attempts - */ -export enum RouteAttempt { - /** - * A handler was found in the set of registered routes - */ - MATCHED = "matched", - /** - * A handler was not found within the set of registered routes - */ - MISSING = "missing", - /** - * The extension that was matched in the route was not activated - */ - MISSING_EXTENSION = "no-extension", -} - -export function foldAttemptResults(mainAttempt: RouteAttempt, rendererAttempt: RouteAttempt): RouteAttempt { - switch (mainAttempt) { - case RouteAttempt.MATCHED: - return RouteAttempt.MATCHED; - case RouteAttempt.MISSING: - case RouteAttempt.MISSING_EXTENSION: - return rendererAttempt; - } -} - -export interface LensProtocolRouterDependencies { - readonly extensionLoader: ExtensionLoader; - readonly extensionsStore: ExtensionsStore; - readonly logger: Logger; -} - -export abstract class LensProtocolRouter { - // Map between path schemas and the handlers - protected internalRoutes = new Map(); - - public static readonly LoggingPrefix = "[PROTOCOL ROUTER]"; - - static readonly ExtensionUrlSchema = `/:${EXTENSION_PUBLISHER_MATCH}(@[A-Za-z0-9_]+)?/:${EXTENSION_NAME_MATCH}`; - - constructor(protected readonly dependencies: LensProtocolRouterDependencies) {} - - /** - * Attempts to route the given URL to all internal routes that have been registered - * @param url the parsed URL that initiated the `lens://` protocol - * @returns true if a route has been found - */ - protected _routeToInternal(url: Url>): RouteAttempt { - return this._route(this.internalRoutes.entries(), url); - } - - /** - * match against all matched URIs, returning either the first exact match or - * the most specific match if none are exact. - * @param routes the array of path schemas, handler pairs to match against - * @param url the url (in its current state) - */ - protected _findMatchingRoute(routes: Iterable<[string, RouteHandler]>, url: Url>): null | [match>, RouteHandler] { - const matches: [match>, RouteHandler][] = []; - - for (const [schema, handler] of routes) { - const match = matchPath(url.pathname, { path: schema }); - - if (!match) { - continue; - } - - // prefer an exact match - if (match.isExact) { - return [match, handler]; - } - - matches.push([match, handler]); - } - - // if no exact match pick the one that is the most specific - return matches.sort(([a], [b]) => { - if (a.path === "/") { - return 1; - } - - if (b.path === "/") { - return -1; - } - - return countBy(b.path)["/"] - countBy(a.path)["/"]; - })[0] ?? null; - } - - /** - * find the most specific matching handler and call it - * @param routes the array of (path schemas, handler) pairs to match against - * @param url the url (in its current state) - */ - protected _route(routes: Iterable<[string, RouteHandler]>, url: Url>, extensionName?: string): RouteAttempt { - const route = this._findMatchingRoute(routes, url); - - if (!route) { - const data: Record = { url: url.toString() }; - - if (extensionName) { - data.extensionName = extensionName; - } - - this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: No handler found`, data); - - return RouteAttempt.MISSING; - } - - const [match, handler] = route; - - const params: RouteParams = { - pathname: match.params, - search: url.query, - }; - - if (!match.isExact) { - params.tail = url.pathname.slice(match.url.length); - } - - handler(params); - - return RouteAttempt.MATCHED; - } - - /** - * Tries to find the matching LensExtension instance - * - * Note: this needs to be async so that `main`'s overloaded version can also be async - * @param url the protocol request URI that was "open"-ed - * @returns either the found name or the instance of `LensExtension` - */ - protected async _findMatchingExtensionByName(url: Url>): Promise { - interface ExtensionUrlMatch { - [EXTENSION_PUBLISHER_MATCH]: string; - [EXTENSION_NAME_MATCH]: string; - } - - const match = matchPath(url.pathname, LensProtocolRouter.ExtensionUrlSchema); - - if (!match) { - throw new RoutingError(RoutingErrorType.NO_EXTENSION_ID, url); - } - - const { [EXTENSION_PUBLISHER_MATCH]: publisher, [EXTENSION_NAME_MATCH]: partialName } = match.params; - const name = [publisher, partialName].filter(isDefined).join("/"); - - const extensionLoader = this.dependencies.extensionLoader; - - try { - /** - * Note, if `getInstanceByName` returns `null` that means we won't be getting an instance - */ - await when(() => extensionLoader.getInstanceByName(name) !== void 0, { - timeout: 5_000, - }); - } catch (error) { - this.dependencies.logger.info( - `${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not installed (${error})`, - ); - - return name; - } - - const extension = extensionLoader.getInstanceByName(name); - - if (!extension) { - this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but does not have a class for ${ipcRenderer ? "renderer" : "main"}`); - - return name; - } - - if (!this.dependencies.extensionsStore.isEnabled(extension)) { - this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`); - - return name; - } - - this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched`); - - return extension; - } - - /** - * Find a matching extension by the first one or two path segments of `url` and then try to `_route` - * its correspondingly registered handlers. - * - * If no handlers are found or the extension is not enabled then `_missingHandlers` is called before - * checking if more handlers have been added. - * - * Note: this function modifies its argument, do not reuse - * @param url the protocol request URI that was "open"-ed - */ - protected async _routeToExtension(url: Url>): Promise { - const extension = await this._findMatchingExtensionByName(url); - - if (typeof extension === "string") { - // failed to find an extension, it returned its name - return RouteAttempt.MISSING_EXTENSION; - } - - // remove the extension name from the path name so we don't need to match on it anymore - url.set("pathname", url.pathname.slice(extension.name.length + 1)); - - try { - const handlers = iter.map(extension.protocolHandlers, ({ pathSchema, handler }) => [pathSchema, handler] as [string, RouteHandler]); - - return this._route(handlers, url, extension.name); - } catch (error) { - if (error instanceof RoutingError) { - error.extensionName = extension.name; - } - - throw error; - } - } - - /** - * Add a handler under the `lens://app` tree of routing. - * @param pathSchema the URI path schema to match against for this handler - * @param handler a function that will be called if a protocol path matches - */ - public addInternalHandler(urlSchema: string, handler: RouteHandler): this { - pathToRegexp(urlSchema); // verify now that the schema is valid - this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: internal registering ${urlSchema}`); - this.internalRoutes.set(urlSchema, handler); - - return this; - } - - /** - * Remove an internal protocol handler. - * @param pathSchema the path schema that the handler was registered under - */ - public removeInternalHandler(urlSchema: string): void { - this.internalRoutes.delete(urlSchema); - } -} diff --git a/src/common/rbac.ts b/src/common/rbac.ts deleted file mode 100644 index 40b9f2c43c..0000000000 --- a/src/common/rbac.ts +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type KubeResource = - "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" | - "secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | - "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" | - "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | - "priorityclasses" | "runtimeclasses" | - "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts"; - -export interface KubeApiResource { - kind: string; - group: string; // api-group, if empty then "core" - apiName: string; - namespaced: boolean; -} - -export interface KubeApiResourceDescriptor { - apiName: string; - group: string; // api-group, if empty then "core" -} - -export const formatKubeApiResource = (desc: KubeApiResourceDescriptor) => ( - desc.group - ? `${desc.group}/${desc.apiName}` - : desc.apiName -); - -export interface KubeApiResourceData { - kind: string; // resource type (e.g. "Namespace") - group: string; // api-group, if empty then "core" - namespaced: boolean; -} - -export const apiResourceRecord: Record = { - clusterroles: { - kind: "ClusterRole", - group: "rbac.authorization.k8s.io", - namespaced: false, - }, - clusterrolebindings: { - kind: "ClusterRoleBinding", - group: "rbac.authorization.k8s.io", - namespaced: false, - }, - configmaps: { - kind: "ConfigMap", - group: "", - namespaced: true, - }, - cronjobs: { - kind: "CronJob", - group: "batch", - namespaced: true, - }, - customresourcedefinitions: { - kind: "CustomResourceDefinition", - group: "apiextensions.k8s.io", - namespaced: false, - }, - daemonsets: { - kind: "DaemonSet", - group: "apps", - namespaced: true, - }, - deployments: { - kind: "Deployment", - group: "apps", - namespaced: true, - }, - endpoints: { - kind: "Endpoint", - group: "", - namespaced: true, - }, - events: { - kind: "Event", - group: "", - namespaced: true, - }, - horizontalpodautoscalers: { - kind: "HorizontalPodAutoscaler", - group: "autoscaling", - namespaced: true, - }, - ingresses: { - kind: "Ingress", - group: "networking.k8s.io", - namespaced: true, - }, - ingressclasses: { - kind: "IngressClass", - group: "networking.k8s.io", - namespaced: false, - }, - jobs: { - kind: "Job", - group: "batch", - namespaced: true, - }, - namespaces: { - kind: "Namespace", - group: "", - namespaced: false, - }, - limitranges: { - kind: "LimitRange", - group: "", - namespaced: true, - }, - leases: { - kind: "Lease", - group: "", - namespaced: true, - }, - networkpolicies: { - kind: "NetworkPolicy", - group: "networking.k8s.io", - namespaced: true, - }, - nodes: { - kind: "Node", - group: "", - namespaced: false, - }, - persistentvolumes: { - kind: "PersistentVolume", - group: "", - namespaced: false, - }, - persistentvolumeclaims: { - kind: "PersistentVolumeClaim", - group: "", - namespaced: true, - }, - pods: { - kind: "Pod", - group: "", - namespaced: true, - }, - poddisruptionbudgets: { - kind: "PodDisruptionBudget", - group: "policy", - namespaced: true, - }, - podsecuritypolicies: { - kind: "PodSecurityPolicy", - group: "policy", - namespaced: false, - }, - priorityclasses: { - kind: "PriorityClass", - group: "scheduling.k8s.io", - namespaced: false, - }, - runtimeclasses: { - kind: "RuntimeClass", - group: "node.k8s.io", - namespaced: false, - }, - resourcequotas: { - kind: "ResourceQuota", - group: "", - namespaced: true, - }, - replicasets: { - kind: "ReplicaSet", - group: "apps", - namespaced: true, - }, - roles: { - kind: "Role", - group: "rbac.authorization.k8s.io", - namespaced: true, - }, - rolebindings: { - kind: "RoleBinding", - group: "rbac.authorization.k8s.io", - namespaced: true, - }, - secrets: { - kind: "Secret", - group: "", - namespaced: true, - }, - serviceaccounts: { - kind: "ServiceAccount", - group: "", - namespaced: true, - }, - services: { - kind: "Service", - group: "", - namespaced: true, - }, - statefulsets: { - kind: "StatefulSet", - group: "apps", - namespaced: true, - }, - storageclasses: { - kind: "StorageClass", - group: "storage.k8s.io", - namespaced: false, - }, -}; diff --git a/src/common/root-frame/root-frame-rendered-channel.ts b/src/common/root-frame/root-frame-rendered-channel.ts deleted file mode 100644 index 060ae8735c..0000000000 --- a/src/common/root-frame/root-frame-rendered-channel.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; - -export type RootFrameHasRenderedChannel = MessageChannel; - -export const rootFrameHasRenderedChannel: RootFrameHasRenderedChannel = { - id: "root-frame-rendered", -}; diff --git a/src/common/runnable/run-many-for.test.ts b/src/common/runnable/run-many-for.test.ts deleted file mode 100644 index 6002a149db..0000000000 --- a/src/common/runnable/run-many-for.test.ts +++ /dev/null @@ -1,660 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { createContainer, getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import type { Runnable } from "./run-many-for"; -import { runManyFor } from "./run-many-for"; -import { getPromiseStatus } from "../test-utils/get-promise-status"; -import { runInAction } from "mobx"; -import { flushPromises } from "../test-utils/flush-promises"; - -describe("runManyFor", () => { - describe("given no hierarchy, when running many", () => { - let runMock: AsyncFnMock<(...args: unknown[]) => Promise>; - let actualPromise: Promise; - - beforeEach(() => { - const rootDi = createContainer("irrelevant"); - - runMock = asyncFn(); - - const someInjectionTokenForRunnables = getInjectionToken({ - id: "some-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-injectable", - instantiate: () => ({ - id: "some-injectable", - run: () => runMock("some-call"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - const someOtherInjectable = getInjectable({ - id: "some-other-injectable", - instantiate: () => ({ - id: "some-other-injectable", - run: () => runMock("some-other-call"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManyFor(rootDi)(someInjectionTokenForRunnables); - - actualPromise = runMany() as Promise; - }); - - it("runs all runnables at the same time", () => { - expect(runMock.mock.calls).toEqual([ - ["some-call"], - ["some-other-call"], - ]); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - it("when all runnables resolve, resolves", async () => { - await Promise.all([runMock.resolve(), runMock.resolve()]); - - expect(await actualPromise).toBe(undefined); - }); - }); - - describe("given hierarchy that is three levels deep, when running many", () => { - let runMock: AsyncFnMock<(...args: unknown[]) => Promise>; - let actualPromise: Promise; - - beforeEach(() => { - const di = createContainer("irrelevant"); - - runMock = asyncFn(); - - const someInjectionTokenForRunnables = getInjectionToken({ - id: "some-injection-token", - }); - - const someInjectable1 = getInjectable({ - id: "some-injectable-1", - - instantiate: (di) => ({ - id: "some-injectable-1", - run: () => runMock("third-level-run"), - runAfter: di.inject(someInjectable2), - }), - - injectionToken: someInjectionTokenForRunnables, - }); - - const someInjectable2 = getInjectable({ - id: "some-injectable-2", - - instantiate: (di) => ({ - id: "some-injectable-2", - run: () => runMock("second-level-run"), - runAfter: di.inject(someInjectable3), - }), - - injectionToken: someInjectionTokenForRunnables, - }); - - const someInjectable3 = getInjectable({ - id: "some-injectable-3", - instantiate: () => ({ - id: "some-injectable-3", - run: () => runMock("first-level-run"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - di.register(someInjectable1, someInjectable2, someInjectable3); - - const runMany = runManyFor(di)(someInjectionTokenForRunnables); - - actualPromise = runMany() as Promise; - }); - - it("runs first level runnables", () => { - expect(runMock.mock.calls).toEqual([["first-level-run"]]); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - describe("when first level runnables resolve", () => { - beforeEach(async () => { - runMock.mockClear(); - - await runMock.resolveSpecific(["first-level-run"]); - }); - - it("runs second level runnables", async () => { - expect(runMock.mock.calls).toEqual([["second-level-run"]]); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - describe("when second level runnables resolve", () => { - beforeEach(async () => { - runMock.mockClear(); - - await runMock.resolveSpecific(["second-level-run"]); - }); - - it("runs final third level runnables", async () => { - expect(runMock.mock.calls).toEqual([["third-level-run"]]); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - describe("when final third level runnables resolve", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["third-level-run"]); - }); - - it("resolves", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(true); - }); - }); - }); - }); - }); - - it("given invalid hierarchy, when running runnables, throws", () => { - const rootDi = createContainer("irrelevant"); - - const runMock = asyncFn<(...args: unknown[]) => void>(); - - const someInjectionToken = getInjectionToken({ - id: "some-injection-token", - }); - - const someOtherInjectionToken = getInjectionToken({ - id: "some-other-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-runnable-1", - - instantiate: (di) => ({ - id: "some-runnable-1", - run: () => runMock("some-runnable-1"), - runAfter: di.inject(someOtherInjectable), - }), - - injectionToken: someInjectionToken, - }); - - const someOtherInjectable = getInjectable({ - id: "some-runnable-2", - - instantiate: () => ({ - id: "some-runnable-2", - run: () => runMock("some-runnable-2"), - }), - - injectionToken: someOtherInjectionToken, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManyFor(rootDi)( - someInjectionToken, - ); - - return expect(() => runMany()).rejects.toThrow( - /Runnable "some-runnable-1" is unreachable for injection token "some-injection-token": run afters "some-runnable-2" are a part of different injection tokens./, - ); - }); - - it("given partially incorrect hierarchy, when running runnables, throws", () => { - const rootDi = createContainer("irrelevant"); - - const runMock = asyncFn<(...args: unknown[]) => void>(); - - const someInjectionToken = getInjectionToken({ - id: "some-injection-token", - }); - - const someOtherInjectionToken = getInjectionToken({ - id: "some-other-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-runnable-1", - - instantiate: (di) => ({ - id: "some-runnable-1", - run: () => runMock("some-runnable-1"), - runAfter: [ - di.inject(someOtherInjectable), - di.inject(someSecondInjectable), - ], - }), - - injectionToken: someInjectionToken, - }); - - const someSecondInjectable = getInjectable({ - id: "some-runnable-2", - - instantiate: () => ({ - id: "some-runnable-2", - run: () => runMock("some-runnable-2"), - }), - - injectionToken: someInjectionToken, - }); - - const someOtherInjectable = getInjectable({ - id: "some-runnable-3", - - instantiate: () => ({ - id: "some-runnable-3", - run: () => runMock("some-runnable-3"), - }), - - injectionToken: someOtherInjectionToken, - }); - - rootDi.register(someInjectable, someOtherInjectable, someSecondInjectable); - - const runMany = runManyFor(rootDi)( - someInjectionToken, - ); - - return expect(() => runMany()).rejects.toThrow( - /Runnable "some-runnable-3" is not part of the injection token "some-injection-token"/, - ); - }); - - describe("when running many with parameter", () => { - let runMock: AsyncFnMock<(...args: unknown[]) => Promise>; - - beforeEach(() => { - const rootDi = createContainer("irrelevant"); - - runMock = asyncFn(); - - const someInjectionTokenForRunnablesWithParameter = getInjectionToken< - Runnable - >({ - id: "some-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-runnable-1", - - instantiate: () => ({ - id: "some-runnable-1", - run: (parameter) => runMock("run-of-some-runnable-1", parameter), - }), - - injectionToken: someInjectionTokenForRunnablesWithParameter, - }); - - const someOtherInjectable = getInjectable({ - id: "some-runnable-2", - - instantiate: () => ({ - id: "some-runnable-2", - run: (parameter) => runMock("run-of-some-runnable-2", parameter), - }), - - injectionToken: someInjectionTokenForRunnablesWithParameter, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManyFor(rootDi)( - someInjectionTokenForRunnablesWithParameter, - ); - - runMany("some-parameter"); - }); - - it("runs all runnables using the parameter", () => { - expect(runMock.mock.calls).toEqual([ - ["run-of-some-runnable-1", "some-parameter"], - ["run-of-some-runnable-2", "some-parameter"], - ]); - }); - }); - - describe("given multiple runAfters", () => { - let runMock: AsyncFnMock<(...args: unknown[]) => void>; - let finishingPromise: Promise; - - beforeEach(async () => { - const rootDi = createContainer("irrelevant"); - - runMock = asyncFn<(...args: unknown[]) => void>(); - - const someInjectionToken = getInjectionToken({ - id: "some-injection-token", - }); - - const runnableOneInjectable = getInjectable({ - id: "runnable-1", - instantiate: () => ({ - id: "runnable-1", - run: () => runMock("runnable-1"), - }), - injectionToken: someInjectionToken, - }); - - const runnableTwoInjectable = getInjectable({ - id: "runnable-2", - instantiate: () => ({ - id: "runnable-2", - run: () => runMock("runnable-2"), - runAfter: [], // shouldn't block being called - }), - injectionToken: someInjectionToken, - }); - - const runnableThreeInjectable = getInjectable({ - id: "runnable-3", - instantiate: (di) => ({ - id: "runnable-3", - run: () => runMock("runnable-3"), - runAfter: di.inject(runnableOneInjectable), - }), - injectionToken: someInjectionToken, - }); - - const runnableFourInjectable = getInjectable({ - id: "runnable-4", - instantiate: (di) => ({ - id: "runnable-4", - run: () => runMock("runnable-4"), - runAfter: [di.inject(runnableThreeInjectable)], // should be the same as an single item - }), - injectionToken: someInjectionToken, - }); - - const runnableFiveInjectable = getInjectable({ - id: "runnable-5", - instantiate: (di) => ({ - id: "runnable-5", - run: () => runMock("runnable-5"), - runAfter: di.inject(runnableThreeInjectable), - }), - injectionToken: someInjectionToken, - }); - - const runnableSixInjectable = getInjectable({ - id: "runnable-6", - instantiate: (di) => ({ - id: "runnable-6", - run: () => runMock("runnable-6"), - runAfter: [ - di.inject(runnableFourInjectable), - di.inject(runnableFiveInjectable), - ], - }), - injectionToken: someInjectionToken, - }); - - const runnableSevenInjectable = getInjectable({ - id: "runnable-7", - instantiate: (di) => ({ - id: "runnable-7", - run: () => runMock("runnable-7"), - runAfter: [ - di.inject(runnableFiveInjectable), - di.inject(runnableSixInjectable), - ], - }), - injectionToken: someInjectionToken, - }); - - runInAction(() => { - rootDi.register( - runnableOneInjectable, - runnableTwoInjectable, - runnableThreeInjectable, - runnableFourInjectable, - runnableFiveInjectable, - runnableSixInjectable, - runnableSevenInjectable, - ); - }); - - const runMany = runManyFor(rootDi); - const runSome = runMany(someInjectionToken); - - finishingPromise = runSome(); - - await flushPromises(); - }); - - it("should run 'runnable-1'", () => { - expect(runMock).toBeCalledWith("runnable-1"); - }); - - it("should run 'runnable-2'", () => { - expect(runMock).toBeCalledWith("runnable-2"); - }); - - it("should not run 'runnable-3'", () => { - expect(runMock).not.toBeCalledWith("runnable-3"); - }); - - describe("when 'runnable-1' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-1"]); - }); - - it("should run 'runnable-3'", () => { - expect(runMock).toBeCalledWith("runnable-3"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(3); - }); - }); - - describe("when 'runnable-3' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-3"]); - }); - - it("should run 'runnable-4'", () => { - expect(runMock).toBeCalledWith("runnable-4"); - }); - - it("should run 'runnable-5'", () => { - expect(runMock).toBeCalledWith("runnable-5"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(5); - }); - }); - - describe("when 'runnable-4' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-4"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(5); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(5); - }); - }); - - describe("when 'runnable-5' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-5"]); - }); - - it("should run 'runnable-6'", () => { - expect(runMock).toBeCalledWith("runnable-6"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(6); - }); - }); - - describe("when 'runnable-6' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-6"]); - }); - - it("should run 'runnable-7'", () => { - expect(runMock).toBeCalledWith("runnable-7"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(7); - }); - - describe("when 'runnable-7' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-7"]); - }); - - it("should resolve the runMany promise call", async () => { - await finishingPromise; - }); - }); - }); - }); - }); - }); - - describe("when 'runnable-5' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-5"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(5); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(5); - }); - }); - - describe("when 'runnable-4' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-4"]); - }); - - it("should run 'runnable-6'", () => { - expect(runMock).toBeCalledWith("runnable-6"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(6); - }); - }); - - describe("when 'runnable-6' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-6"]); - }); - - it("should run 'runnable-7'", () => { - expect(runMock).toBeCalledWith("runnable-7"); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(7); - }); - - describe("when 'runnable-7' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-7"]); - }); - - it("should resolve the runMany promise call", async () => { - await finishingPromise; - }); - }); - }); - }); - }); - }); - }); - }); - - describe("when 'runnable-2' resolves", () => { - beforeEach(async () => { - await runMock.resolveSpecific(["runnable-2"]); - }); - - it("shouldn't call any more runnables", () => { - expect(runMock).toBeCalledTimes(2); - }); - }); - }); -}); diff --git a/src/common/runnable/run-many-for.ts b/src/common/runnable/run-many-for.ts deleted file mode 100644 index 106cc74da1..0000000000 --- a/src/common/runnable/run-many-for.ts +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable"; -import type { SingleOrMany } from "../utils"; -import { getOrInsert, getOrInsertSetFor, isDefined } from "../utils"; -import * as uuid from "uuid"; -import assert from "assert"; -import type { Asyncify } from "type-fest"; -import type TypedEventEmitter from "typed-emitter"; -import EventEmitter from "events"; - -export interface Runnable { - id: string; - run: Run; - runAfter?: SingleOrMany>; -} - -type Run = (parameter: Param) => Promise | void; - -export type RunMany = (injectionToken: InjectionToken, void>) => Asyncify>; - -const computedNextEdge = (traversed: string[], graph: Map>, currentId: string, seenIds: Set) => { - seenIds.add(currentId); - const currentNode = graph.get(currentId); - - assert(currentNode, `Runnable graph does not contain node with id="${currentId}"`); - - for (const nextId of currentNode.values()) { - if (traversed.includes(nextId)) { - throw new Error(`Cycle in runnable graph: "${traversed.join(`" -> "`)}" -> "${nextId}"`); - } - - computedNextEdge([...traversed, nextId], graph, nextId, seenIds); - } -}; - -const verifyRunnablesAreDAG = (injectionToken: InjectionToken, void>, runnables: Runnable[]) => { - const rootId = uuid.v4(); - const runnableGraph = new Map>(); - const seenIds = new Set(); - const addRunnableId = getOrInsertSetFor(runnableGraph); - - // Build the Directed graph - for (const runnable of runnables) { - addRunnableId(runnable.id); - - if (!runnable.runAfter || (Array.isArray(runnable.runAfter) && runnable.runAfter.length === 0)) { - addRunnableId(rootId).add(runnable.id); - } else if (Array.isArray(runnable.runAfter)) { - for (const parentRunnable of runnable.runAfter) { - addRunnableId(parentRunnable.id).add(runnable.id); - } - } else { - addRunnableId(runnable.runAfter.id).add(runnable.id); - } - } - - addRunnableId(rootId); - - // Do a DFS to find any cycles - computedNextEdge([], runnableGraph, rootId, seenIds); - - for (const id of runnableGraph.keys()) { - if (!seenIds.has(id)) { - const runnable = runnables.find(runnable => runnable.id === id); - - if (!runnable) { - throw new Error(`Runnable "${id}" is not part of the injection token "${injectionToken.id}"`); - } - - const runAfters = [runnable.runAfter] - .flat() - .filter(isDefined) - .map(runnable => runnable.id) - .join('", "'); - - throw new Error(`Runnable "${id}" is unreachable for injection token "${injectionToken.id}": run afters "${runAfters}" are a part of different injection tokens.`); - } - } -}; - -interface BarrierEvent { - finish: (id: string) => void; -} - -class DynamicBarrier { - private readonly finishedIds = new Map>(); - private readonly events: TypedEventEmitter = new EventEmitter(); - - private initFinishingPromise(id: string): Promise { - return getOrInsert(this.finishedIds, id, new Promise(resolve => { - const handler = (finishedId: string) => { - if (finishedId === id) { - resolve(); - this.events.removeListener("finish", handler); - } - }; - - this.events.addListener("finish", handler); - })); - } - - setFinished(id: string): void { - void this.initFinishingPromise(id); - - this.events.emit("finish", id); - } - - async blockOn(id: string): Promise { - await this.initFinishingPromise(id); - } -} - -const executeRunnableWith = (param: Param) => { - const barrier = new DynamicBarrier(); - - return async (runnable: Runnable): Promise => { - const parentRunnables = [runnable.runAfter].flat().filter(isDefined); - - for (const parentRunnable of parentRunnables) { - await barrier.blockOn(parentRunnable.id); - } - - await runnable.run(param); - barrier.setFinished(runnable.id); - }; -}; - -export function runManyFor(di: DiContainerForInjection): RunMany { - return (injectionToken: InjectionToken, void>) => async (param: Param) => { - const executeRunnable = executeRunnableWith(param); - const allRunnables = di.injectMany(injectionToken); - - verifyRunnablesAreDAG(injectionToken, allRunnables); - - await Promise.all(allRunnables.map(executeRunnable)); - }; -} diff --git a/src/common/runnable/run-many-sync-for.test.ts b/src/common/runnable/run-many-sync-for.test.ts deleted file mode 100644 index 3aadcad0bf..0000000000 --- a/src/common/runnable/run-many-sync-for.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { createContainer, getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import type { RunnableSync } from "./run-many-sync-for"; -import { runManySyncFor } from "./run-many-sync-for"; - -describe("runManySyncFor", () => { - describe("given hierarchy, when running many", () => { - let runMock: jest.Mock; - - beforeEach(() => { - const rootDi = createContainer("irrelevant"); - - runMock = jest.fn(); - - const someInjectionTokenForRunnables = getInjectionToken({ - id: "some-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-injectable", - instantiate: () => ({ - id: "some-injectable", - run: () => runMock("some-call"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - const someOtherInjectable = getInjectable({ - id: "some-other-injectable", - instantiate: () => ({ - id: "some-other-injectable", - run: () => runMock("some-other-call"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManySyncFor(rootDi)(someInjectionTokenForRunnables); - - runMany(); - }); - - it("runs all runnables at the same time", () => { - expect(runMock.mock.calls).toEqual([ - ["some-call"], - ["some-other-call"], - ]); - }); - }); - - describe("given hierarchy that is three levels deep, when running many", () => { - let runMock: jest.Mock<(arg: string) => void>; - - beforeEach(() => { - const di = createContainer("irrelevant"); - - runMock = jest.fn(); - - const someInjectionTokenForRunnables = getInjectionToken({ - id: "some-injection-token", - }); - - const someInjectable1 = getInjectable({ - id: "some-injectable-1", - - instantiate: (di) => ({ - id: "some-injectable-1", - run: () => void runMock("third-level-run"), - runAfter: di.inject(someInjectable2), - }), - - injectionToken: someInjectionTokenForRunnables, - }); - - const someInjectable2 = getInjectable({ - id: "some-injectable-2", - - instantiate: (di) => ({ - id: "some-injectable-2", - run: () => void runMock("second-level-run"), - runAfter: di.inject(someInjectable3), - }), - - injectionToken: someInjectionTokenForRunnables, - }); - - const someInjectable3 = getInjectable({ - id: "some-injectable-3", - instantiate: () => ({ - id: "some-injectable-3", - run: () => void runMock("first-level-run"), - }), - injectionToken: someInjectionTokenForRunnables, - }); - - di.register(someInjectable1, someInjectable2, someInjectable3); - - const runMany = runManySyncFor(di)(someInjectionTokenForRunnables); - - runMany(); - }); - - it("runs runnables in order", () => { - expect(runMock.mock.calls).toEqual([["first-level-run"], ["second-level-run"], ["third-level-run"]]); - }); - }); - - it("given invalid hierarchy, when running runnables, throws", () => { - const rootDi = createContainer("irrelevant"); - - const runMock = jest.fn(); - - const someInjectionToken = getInjectionToken({ - id: "some-injection-token", - }); - - const someOtherInjectionToken = getInjectionToken({ - id: "some-other-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-runnable-1", - - instantiate: (di) => ({ - id: "some-runnable-1", - run: () => runMock("some-runnable-1"), - runAfter: di.inject(someOtherInjectable), - }), - - injectionToken: someInjectionToken, - }); - - const someOtherInjectable = getInjectable({ - id: "some-runnable-2", - - instantiate: () => ({ - id: "some-runnable-2", - run: () => runMock("some-runnable-2"), - }), - - injectionToken: someOtherInjectionToken, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManySyncFor(rootDi)( - someInjectionToken, - ); - - return expect(() => runMany()).toThrow( - /Tried to get a composite but encountered missing parent ids: "some-runnable-2".\n\nAvailable parent ids are:\n"[0-9a-z-]+",\n"some-runnable-1"/, - ); - }); - - describe("when running many with parameter", () => { - let runMock: jest.Mock<(arg: string, arg2: string) => undefined>; - - beforeEach(() => { - const rootDi = createContainer("irrelevant"); - - runMock = jest.fn(); - - const someInjectionTokenForRunnablesWithParameter = getInjectionToken< - RunnableSync - >({ - id: "some-injection-token", - }); - - const someInjectable = getInjectable({ - id: "some-runnable-1", - - instantiate: () => ({ - id: "some-runnable-1", - run: (parameter) => void runMock("run-of-some-runnable-1", parameter), - }), - - injectionToken: someInjectionTokenForRunnablesWithParameter, - }); - - const someOtherInjectable = getInjectable({ - id: "some-runnable-2", - - instantiate: () => ({ - id: "some-runnable-2", - run: (parameter) => void runMock("run-of-some-runnable-2", parameter), - }), - - injectionToken: someInjectionTokenForRunnablesWithParameter, - }); - - rootDi.register(someInjectable, someOtherInjectable); - - const runMany = runManySyncFor(rootDi)( - someInjectionTokenForRunnablesWithParameter, - ); - - runMany("some-parameter"); - }); - - it("runs all runnables using the parameter", () => { - expect(runMock.mock.calls).toEqual([ - ["run-of-some-runnable-1", "some-parameter"], - ["run-of-some-runnable-2", "some-parameter"], - ]); - }); - }); -}); - diff --git a/src/common/runnable/run-many-sync-for.ts b/src/common/runnable/run-many-sync-for.ts deleted file mode 100644 index 08dba2f72d..0000000000 --- a/src/common/runnable/run-many-sync-for.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable"; -import type { Composite } from "../utils/composite/get-composite/get-composite"; -import { getCompositeFor } from "../utils/composite/get-composite/get-composite"; -import * as uuid from "uuid"; - -export interface RunnableSync { - id: string; - run: RunSync; - runAfter?: RunnableSync; -} - -/** - * NOTE: this is the worse of two evils. This makes sure that `RunnableSync` always is sync. - * If the return type is `void` instead then async functions (those return `Promise`) can - * coerce to it. - */ -type RunSync = (parameter: Param) => undefined; - -export type RunManySync = (injectionToken: InjectionToken, void>) => RunSync; - -function runCompositeRunnableSyncs(param: Param, composite: Composite>): undefined { - composite.value.run(param); - composite.children.map(composite => runCompositeRunnableSyncs(param, composite)); - - return undefined; -} - -export function runManySyncFor(di: DiContainerForInjection): RunManySync { - return (injectionToken: InjectionToken, void>) => (param: Param): undefined => { - const allRunnables = di.injectMany(injectionToken); - const rootId = uuid.v4(); - const getCompositeRunnables = getCompositeFor>({ - getId: (runnable) => runnable.id, - getParentId: (runnable) => ( - runnable.id === rootId - ? undefined - : runnable.runAfter?.id ?? rootId - ), - }); - const composite = getCompositeRunnables([ - // This is a dummy runnable to conform to the requirements of `getCompositeFor` to only have one root - { - id: rootId, - run: () => undefined, - }, - ...allRunnables, - ]); - - return runCompositeRunnableSyncs(param, composite); - }; -} diff --git a/src/common/terminal/channels.ts b/src/common/terminal/channels.ts deleted file mode 100644 index f958c9c696..0000000000 --- a/src/common/terminal/channels.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -export enum TerminalChannels { - STDIN = "stdin", - STDOUT = "stdout", - CONNECTED = "connected", - RESIZE = "resize", - PING = "ping", -} - -export type TerminalMessage = { - type: TerminalChannels.STDIN; - data: string; -} | { - type: TerminalChannels.STDOUT; - data: string; -} | { - type: TerminalChannels.CONNECTED; -} | { - type: TerminalChannels.RESIZE; - data: { - width: number; - height: number; - }; -} | { - type: TerminalChannels.PING; -}; diff --git a/src/common/test-utils/flush-promises.ts b/src/common/test-utils/flush-promises.ts deleted file mode 100644 index c2fdeff99e..0000000000 --- a/src/common/test-utils/flush-promises.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { setImmediate } from "timers"; - -export const flushPromises = () => new Promise(setImmediate); diff --git a/src/common/test-utils/get-global-override-for-function.ts b/src/common/test-utils/get-global-override-for-function.ts deleted file mode 100644 index 238ee5621a..0000000000 --- a/src/common/test-utils/get-global-override-for-function.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; -import { getGlobalOverride } from "./get-global-override"; -import { camelCase } from "lodash/fp"; - -export const getGlobalOverrideForFunction = ( - injectable: Injectable, -) => - getGlobalOverride(injectable, () => (...args: any[]) => { - console.warn( - `Tried to invoke a function "${injectable.id}" without override. The args were:`, - args, - ); - - throw new Error( - `Tried to invoke a function "${ - injectable.id - }" without override. Add eg. "di.override(${camelCase( - injectable.id, - )}Mock)" to the unit test interested in this.`, - ); - }); diff --git a/src/common/test-utils/get-global-override.ts b/src/common/test-utils/get-global-override.ts deleted file mode 100644 index ac3c86a33e..0000000000 --- a/src/common/test-utils/get-global-override.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; - -export interface GlobalOverride { - injectable: Injectable; - overridingInstantiate: any; -} - -export const getGlobalOverride = >( - injectable: T, - overridingInstantiate: T["instantiate"], -) => ({ - injectable, - overridingInstantiate, - }); diff --git a/src/common/test-utils/get-promise-status.ts b/src/common/test-utils/get-promise-status.ts deleted file mode 100644 index 8c171fbe54..0000000000 --- a/src/common/test-utils/get-promise-status.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { flushPromises } from "./flush-promises"; - -export const getPromiseStatus = async (promise: Promise) => { - const status = { fulfilled: false }; - - promise.finally(() => { - status.fulfilled = true; - }); - - await flushPromises(); - - return status; -}; diff --git a/src/common/test-utils/use-fake-time.ts b/src/common/test-utils/use-fake-time.ts deleted file mode 100644 index e455984861..0000000000 --- a/src/common/test-utils/use-fake-time.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { act } from "@testing-library/react"; - -let usingFakeTime = false; - -export const advanceFakeTime = (milliseconds: number) => { - if (!usingFakeTime) { - throw new Error("Tried to advance fake time but it was not enabled. Call useFakeTime() first."); - } - - act(() => { - jest.advanceTimersByTime(milliseconds); - }); -}; - -export const testUsingFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { - usingFakeTime = true; - - jest.useFakeTimers(); - - jest.setSystemTime(new Date(dateTime)); -}; diff --git a/src/common/user-store/current-timezone.global-override-for-injectable.ts b/src/common/user-store/current-timezone.global-override-for-injectable.ts deleted file mode 100644 index 6056074d3c..0000000000 --- a/src/common/user-store/current-timezone.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import currentTimezoneInjectable from "./current-timezone.injectable"; - -export default getGlobalOverride(currentTimezoneInjectable, () => "Etc/GMT"); diff --git a/src/common/user-store/current-timezone.injectable.ts b/src/common/user-store/current-timezone.injectable.ts deleted file mode 100644 index 6a6043eaf9..0000000000 --- a/src/common/user-store/current-timezone.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 moment from "moment-timezone"; - -const currentTimezoneInjectable = getInjectable({ - id: "current-timezone", - instantiate: () => moment.tz.guess(true), - causesSideEffects: true, -}); - -export default currentTimezoneInjectable; diff --git a/src/common/user-store/file-name-migration.global-override-for-injectable.ts b/src/common/user-store/file-name-migration.global-override-for-injectable.ts deleted file mode 100644 index bb0ac054f3..0000000000 --- a/src/common/user-store/file-name-migration.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable"; - -export default getGlobalOverride(userStoreFileNameMigrationInjectable, () => async () => {}); diff --git a/src/common/user-store/file-name-migration.injectable.ts b/src/common/user-store/file-name-migration.injectable.ts deleted file mode 100644 index 106f559ef0..0000000000 --- a/src/common/user-store/file-name-migration.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import fse from "fs-extra"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { isErrnoException } from "../utils"; -import { getInjectable } from "@ogre-tools/injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; - -export type UserStoreFileNameMigration = () => Promise; - -const userStoreFileNameMigrationInjectable = getInjectable({ - id: "user-store-file-name-migration", - instantiate: (di): UserStoreFileNameMigration => { - const userDataPath = di.inject(directoryForUserDataInjectable); - const joinPaths = di.inject(joinPathsInjectable); - const configJsonPath = joinPaths(userDataPath, "config.json"); - const lensUserStoreJsonPath = joinPaths(userDataPath, "lens-user-store.json"); - - return async () => { - try { - await fse.move(configJsonPath, lensUserStoreJsonPath); - } catch (error) { - if (error instanceof Error && error.message === "dest already exists.") { - await fse.remove(configJsonPath); - } else if (isErrnoException(error) && error.code === "ENOENT" && error.path === configJsonPath) { - // (No such file or directory) - return; // file already moved - } else { - // pass other errors along - throw error; - } - } - }; - }, - causesSideEffects: true, -}); - -export default userStoreFileNameMigrationInjectable; diff --git a/src/common/user-store/https-proxy.injectable.ts b/src/common/user-store/https-proxy.injectable.ts deleted file mode 100644 index 30569d4e77..0000000000 --- a/src/common/user-store/https-proxy.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 userStoreInjectable from "./user-store.injectable"; - -const httpsProxyConfigurationInjectable = getInjectable({ - id: "https-proxy-configuration", - instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); - - return computed(() => userStore.httpsProxy); - }, -}); - -export default httpsProxyConfigurationInjectable; diff --git a/src/common/user-store/index.ts b/src/common/user-store/index.ts deleted file mode 100644 index 026167519b..0000000000 --- a/src/common/user-store/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./user-store"; -export type { KubeconfigSyncEntry, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; diff --git a/src/common/user-store/kubeconfig-syncs.injectable.ts b/src/common/user-store/kubeconfig-syncs.injectable.ts deleted file mode 100644 index 7327b9d8e4..0000000000 --- a/src/common/user-store/kubeconfig-syncs.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 userStoreInjectable from "./user-store.injectable"; - -const kubeconfigSyncsInjectable = getInjectable({ - id: "kubeconfig-syncs", - instantiate: (di) => di.inject(userStoreInjectable).syncKubeconfigEntries, -}); - -export default kubeconfigSyncsInjectable; diff --git a/src/common/user-store/lens-color-theme.injectable.ts b/src/common/user-store/lens-color-theme.injectable.ts deleted file mode 100644 index 5b48de1a37..0000000000 --- a/src/common/user-store/lens-color-theme.injectable.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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 userStoreInjectable from "./user-store.injectable"; - -export type LensColorThemePreference = { - useSystemTheme: true; -} | { - useSystemTheme: false; - lensThemeId: string; -}; - -const lensColorThemePreferenceInjectable = getInjectable({ - id: "lens-color-theme-preference", - instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); - - return computed((): LensColorThemePreference => { - // TODO: remove magic strings - if (userStore.colorTheme === "system") { - return { - useSystemTheme: true, - }; - } - - return { - useSystemTheme: false, - lensThemeId: userStore.colorTheme, - }; - }); - }, -}); - -export default lensColorThemePreferenceInjectable; diff --git a/src/common/user-store/migrations-token.ts b/src/common/user-store/migrations-token.ts deleted file mode 100644 index f3959beb3a..0000000000 --- a/src/common/user-store/migrations-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; - -export const userStoreMigrationInjectionToken = getInjectionToken({ - id: "user-store-migration-token", -}); diff --git a/src/common/user-store/preference-descriptors.injectable.ts b/src/common/user-store/preference-descriptors.injectable.ts deleted file mode 100644 index 35815dbbea..0000000000 --- a/src/common/user-store/preference-descriptors.injectable.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 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 { merge } from "lodash"; -import type { ObservableMap } from "mobx"; -import { observable } from "mobx"; -import homeDirectoryPathInjectable from "../os/home-directory-path.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; -import { defaultThemeId } from "../vars"; -import currentTimezoneInjectable from "./current-timezone.injectable"; -import type { EditorConfiguration, ExtensionRegistry, KubeconfigSyncEntry, KubeconfigSyncValue, TerminalConfig } from "./preferences-helpers"; -import { defaultExtensionRegistryUrlLocation, defaultEditorConfig, defaultTerminalConfig, defaultPackageMirror, getPreferenceDescriptor, packageMirrors } from "./preferences-helpers"; - -export type PreferenceDescriptors = ReturnType; - -const userStorePreferenceDescriptorsInjectable = getInjectable({ - id: "user-store-preference-descriptors", - instantiate: (di) => { - const currentTimezone = di.inject(currentTimezoneInjectable); - const joinPaths = di.inject(joinPathsInjectable); - const homeDirectoryPath = di.inject(homeDirectoryPathInjectable); - - const mainKubeFolderPath = joinPaths(homeDirectoryPath, ".kube"); - - return ({ - httpsProxy: getPreferenceDescriptor({ - fromStore: val => val, - toStore: val => val || undefined, - }), - shell: getPreferenceDescriptor({ - fromStore: val => val, - toStore: val => val || undefined, - }), - colorTheme: getPreferenceDescriptor({ - fromStore: val => val || defaultThemeId, - toStore: val => !val || val === defaultThemeId - ? undefined - : val, - }), - terminalTheme: getPreferenceDescriptor({ - fromStore: val => val || "", - toStore: val => val || undefined, - }), - localeTimezone: getPreferenceDescriptor({ - fromStore: val => val || currentTimezone, - toStore: val => !val || val === currentTimezone - ? undefined - : val, - }), - allowUntrustedCAs: getPreferenceDescriptor({ - fromStore: val => val ?? false, - toStore: val => !val - ? undefined - : val, - }), - allowErrorReporting: getPreferenceDescriptor({ - fromStore: val => val ?? true, - toStore: val => val - ? undefined - : val, - }), - downloadMirror: getPreferenceDescriptor({ - fromStore: val => !val || !packageMirrors.has(val) - ? defaultPackageMirror - : val, - toStore: val => val === defaultPackageMirror - ? undefined - : val, - }), - downloadKubectlBinaries: getPreferenceDescriptor({ - fromStore: val => val ?? true, - toStore: val => val - ? undefined - : val, - }), - downloadBinariesPath: getPreferenceDescriptor({ - fromStore: val => val, - toStore: val => val || undefined, - }), - kubectlBinariesPath: getPreferenceDescriptor({ - fromStore: val => val, - toStore: val => val || undefined, - }), - openAtLogin: getPreferenceDescriptor({ - fromStore: val => val ?? false, - toStore: val => !val - ? undefined - : val, - }), - terminalCopyOnSelect: getPreferenceDescriptor({ - fromStore: val => val ?? false, - toStore: val => !val - ? undefined - : val, - }), - hiddenTableColumns: getPreferenceDescriptor<[string, string[]][], Map>>({ - fromStore: (val = []) => new Map( - val.map(([tableId, columnIds]) => [tableId, new Set(columnIds)]), - ), - toStore: (val) => { - const res: [string, string[]][] = []; - - for (const [table, columns] of val) { - if (columns.size) { - res.push([table, Array.from(columns)]); - } - } - - return res.length ? res : undefined; - }, - }), - syncKubeconfigEntries: getPreferenceDescriptor>({ - fromStore: val => observable.map( - val?.map(({ filePath, ...rest }) => [filePath, rest]) - ?? [[mainKubeFolderPath, {}]], - ), - toStore: val => val.size === 1 && val.has(mainKubeFolderPath) - ? undefined - : Array.from(val, ([filePath, rest]) => ({ filePath, ...rest })), - }), - editorConfiguration: getPreferenceDescriptor, EditorConfiguration>({ - fromStore: val => merge(defaultEditorConfig, val), - toStore: val => val, - }), - terminalConfig: getPreferenceDescriptor, TerminalConfig>({ - fromStore: val => merge(defaultTerminalConfig, val), - toStore: val => val, - }), - extensionRegistryUrl: getPreferenceDescriptor({ - fromStore: val => val ?? { - location: defaultExtensionRegistryUrlLocation, - }, - toStore: val => val.location === defaultExtensionRegistryUrlLocation - ? undefined - : val, - }), - }) as const; - }, -}); - -export default userStorePreferenceDescriptorsInjectable; diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts deleted file mode 100644 index 5bdc2a3852..0000000000 --- a/src/common/user-store/preferences-helpers.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { editor } from "monaco-editor"; -import { defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars"; -import type { PreferenceDescriptors } from "./preference-descriptors.injectable"; - -export interface KubeconfigSyncEntry extends KubeconfigSyncValue { - filePath: string; -} - -export interface KubeconfigSyncValue { -} -export interface TerminalConfig { - fontSize: number; - fontFamily: string; -} - -export const defaultTerminalConfig: TerminalConfig = { - fontSize: defaultFontSize, - fontFamily: defaultTerminalFontFamily, -}; - -interface BaseEditorConfiguration extends Required> { - lineNumbers: NonNullable>; -} - -export type EditorConfiguration = Required; - -export const defaultEditorConfig: EditorConfiguration = { - tabSize: 2, - lineNumbers: "on", - fontSize: defaultFontSize, - fontFamily: defaultEditorFontFamily, - minimap: { - enabled: true, - side: "right", - }, -}; - -export type StoreType

= P extends PreferenceDescription - ? Store - : never; - -export interface PreferenceDescription { - fromStore(val: T | undefined): R; - toStore(val: R): T | undefined; -} - -export const getPreferenceDescriptor = (desc: PreferenceDescription) => desc; - - -export interface DownloadMirror { - url: string; - label: string; - platforms: Set; -} - -export const defaultPackageMirror = "default"; -const defaultDownloadMirrorData: DownloadMirror = { - url: "https://storage.googleapis.com/kubernetes-release/release", - label: "Default (Google)", - platforms: new Set(["darwin", "win32", "linux"]), -}; - -export const packageMirrors = new Map([ - [defaultPackageMirror, defaultDownloadMirrorData], - ["china", { - url: "https://mirror.azure.cn/kubernetes/kubectl", - label: "China (Azure)", - platforms: new Set(["win32", "linux"]), - }], -]); - -export type ExtensionRegistryLocation = "default" | "npmrc" | "custom"; - -export type ExtensionRegistry = { - location: "default" | "npmrc"; - customUrl?: undefined; -} | { - location: "custom"; - customUrl: string; -}; - -export const defaultExtensionRegistryUrlLocation = "default"; -export const defaultExtensionRegistryUrl = "https://registry.npmjs.org"; - -type PreferencesModelType = PreferenceDescriptors[field] extends PreferenceDescription ? T : never; -type UserStoreModelType = PreferenceDescriptors[field] extends PreferenceDescription ? T : never; - -export type UserStoreFlatModel = { - [field in keyof PreferenceDescriptors]: UserStoreModelType; -}; - -export type UserPreferencesModel = { - [field in keyof PreferenceDescriptors]: PreferencesModelType; -} & { updateChannel: string }; diff --git a/src/common/user-store/shell-setting.injectable.ts b/src/common/user-store/shell-setting.injectable.ts deleted file mode 100644 index f93a4e5874..0000000000 --- a/src/common/user-store/shell-setting.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 userInfoInjectable from "./user-info.injectable"; -import userStoreInjectable from "./user-store.injectable"; - -const userShellSettingInjectable = getInjectable({ - id: "user-shell-setting", - instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); - const userInfo = di.inject(userInfoInjectable); - - return computed(() => userStore.shell || userInfo.shell); - }, -}); - -export default userShellSettingInjectable; diff --git a/src/common/user-store/terminal-config.injectable.ts b/src/common/user-store/terminal-config.injectable.ts deleted file mode 100644 index 6f5be75eaf..0000000000 --- a/src/common/user-store/terminal-config.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 { toJS } from "../utils"; -import userStoreInjectable from "./user-store.injectable"; - -const terminalConfigInjectable = getInjectable({ - id: "terminal-config", - instantiate: (di) => { - const store = di.inject(userStoreInjectable); - - return computed(() => toJS(store.terminalConfig)); - }, -}); - -export default terminalConfigInjectable; diff --git a/src/common/user-store/terminal-copy-on-select.injectable.ts b/src/common/user-store/terminal-copy-on-select.injectable.ts deleted file mode 100644 index 543a4f73b9..0000000000 --- a/src/common/user-store/terminal-copy-on-select.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 userStoreInjectable from "./user-store.injectable"; - -const terminalCopyOnSelectInjectable = getInjectable({ - id: "terminal-copy-on-select", - instantiate: (di) => { - const store = di.inject(userStoreInjectable); - - return computed(() => store.terminalCopyOnSelect); - }, -}); - -export default terminalCopyOnSelectInjectable; diff --git a/src/common/user-store/terminal-theme.injectable.ts b/src/common/user-store/terminal-theme.injectable.ts deleted file mode 100644 index a0a00c3253..0000000000 --- a/src/common/user-store/terminal-theme.injectable.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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 userStoreInjectable from "./user-store.injectable"; - -export type TerminalThemePreference = { - matchLensTheme: true; -} | { - matchLensTheme: false; - themeId: string; -}; - -const terminalThemePreferenceInjectable = getInjectable({ - id: "terminal-theme-preference", - instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); - - return computed((): TerminalThemePreference => { - // NOTE: remove use of magic strings - if (!userStore.terminalTheme) { - return { - matchLensTheme: true, - }; - } - - return { - matchLensTheme: false, - themeId: userStore.terminalTheme, - }; - }); - }, -}); - -export default terminalThemePreferenceInjectable; diff --git a/src/common/user-store/user-info.global-override-for-injectable.ts b/src/common/user-store/user-info.global-override-for-injectable.ts deleted file mode 100644 index 21fb26f8a9..0000000000 --- a/src/common/user-store/user-info.global-override-for-injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import userInfoInjectable from "./user-info.injectable"; - -export default getGlobalOverride(userInfoInjectable, () => ({ - gid: 1, - homedir: "/some-home-dir", - shell: "bash", - uid: 2, - username: "some-user-name", -})); diff --git a/src/common/user-store/user-info.injectable.ts b/src/common/user-store/user-info.injectable.ts deleted file mode 100644 index b096da03c5..0000000000 --- a/src/common/user-store/user-info.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { userInfo } from "os"; - -const userInfoInjectable = getInjectable({ - id: "user-info", - instantiate: () => userInfo(), - causesSideEffects: true, -}); - -export default userInfoInjectable; diff --git a/src/common/user-store/user-store.injectable.ts b/src/common/user-store/user-store.injectable.ts deleted file mode 100644 index 3b45b03b1d..0000000000 --- a/src/common/user-store/user-store.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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 { UserStore } from "./user-store"; -import selectedUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable"; -import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { userStoreMigrationInjectionToken } from "./migrations-token"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; -import userStorePreferenceDescriptorsInjectable from "./preference-descriptors.injectable"; - -const userStoreInjectable = getInjectable({ - id: "user-store", - - instantiate: (di) => new UserStore({ - selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable), - emitAppEvent: di.inject(emitAppEventInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, userStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - preferenceDescriptors: di.inject(userStorePreferenceDescriptorsInjectable), - }), -}); - -export default userStoreInjectable; diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts deleted file mode 100644 index 8979ba3351..0000000000 --- a/src/common/user-store/user-store.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, observable, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { getOrInsertSet, toggle, toJS, object } from "../../renderer/utils"; -import type { UserPreferencesModel, StoreType } from "./preferences-helpers"; -import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable"; - -// TODO: Remove coupling with Feature -import type { SelectedUpdateChannel } from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable"; -import type { ReleaseChannel } from "../../features/application-update/common/update-channels"; -import type { PreferenceDescriptors } from "./preference-descriptors.injectable"; - -export interface UserStoreModel { - preferences: UserPreferencesModel; -} - -interface Dependencies extends BaseStoreDependencies { - readonly selectedUpdateChannel: SelectedUpdateChannel; - readonly preferenceDescriptors: PreferenceDescriptors; - emitAppEvent: EmitAppEvent; -} - -export class UserStore extends BaseStore /* implements UserStoreFlatModel (when strict null is enabled) */ { - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-user-store", - }); - - makeObservable(this); - } - - /** - * @deprecated No longer used - */ - @observable seenContexts = observable.set(); - - /** - * @deprecated No longer used - */ - @observable newContexts = observable.set(); - - @observable allowErrorReporting!: StoreType; - @observable allowUntrustedCAs!: StoreType; - @observable colorTheme!: StoreType; - @observable terminalTheme!: StoreType; - @observable localeTimezone!: StoreType; - @observable downloadMirror!: StoreType; - @observable httpsProxy!: StoreType; - @observable shell!: StoreType; - @observable downloadBinariesPath!: StoreType; - @observable kubectlBinariesPath!: StoreType; - @observable terminalCopyOnSelect!: StoreType; - @observable terminalConfig!: StoreType; - @observable extensionRegistryUrl!: StoreType; - - /** - * Download kubectl binaries matching cluster version - */ - @observable downloadKubectlBinaries!: StoreType; - - /** - * Whether the application should open itself at login. - */ - @observable openAtLogin!: StoreType; - - /** - * The column IDs under each configurable table ID that have been configured - * to not be shown - */ - @observable hiddenTableColumns!: StoreType; - - /** - * Monaco editor configs - */ - @observable editorConfiguration!: StoreType; - - /** - * The set of file/folder paths to be synced - */ - @observable syncKubeconfigEntries!: StoreType; - - /** - * Checks if a column (by ID) for a table (by ID) is configured to be hidden - * @param tableId The ID of the table to be checked against - * @param columnIds The list of IDs the check if one is hidden - * @returns true if at least one column under the table is set to hidden - */ - isTableColumnHidden(tableId: string, ...columnIds: (string | undefined)[]): boolean { - if (columnIds.length === 0) { - return false; - } - - const config = this.hiddenTableColumns.get(tableId); - - if (!config) { - return false; - } - - return columnIds.some(columnId => columnId && config.has(columnId)); - } - - /** - * Toggles the hidden configuration of a table's column - */ - toggleTableColumnVisibility(tableId: string, columnId: string) { - toggle(getOrInsertSet(this.hiddenTableColumns, tableId), columnId); - } - - @action - resetTheme() { - this.colorTheme = this.dependencies.preferenceDescriptors.colorTheme.fromStore(undefined); - } - - @action - protected fromStore({ preferences }: Partial = {}) { - this.dependencies.logger.debug("UserStore.fromStore()", { preferences }); - - for (const [key, { fromStore }] of object.entries(this.dependencies.preferenceDescriptors)) { - const curVal = this[key]; - const newVal = fromStore((preferences)?.[key] as never) as never; - - if (isObservableArray(curVal)) { - curVal.replace(newVal); - } else if (isObservableSet(curVal) || isObservableMap(curVal)) { - curVal.replace(newVal); - } else { - this[key] = newVal; - } - } - - // TODO: Switch to action-based saving instead saving stores by reaction - if (preferences?.updateChannel) { - this.dependencies.selectedUpdateChannel.setValue(preferences?.updateChannel as ReleaseChannel); - } - } - - toJSON(): UserStoreModel { - const preferences = object.fromEntries( - object.entries(this.dependencies.preferenceDescriptors) - .map(([key, { toStore }]) => [key, toStore(this[key] as never)]), - ) as UserPreferencesModel; - - return toJS({ - preferences: { - ...preferences, - updateChannel: this.dependencies.selectedUpdateChannel.value.get().id, - }, - }); - } -} diff --git a/src/common/utils/__tests__/cluster-id-url-parsing.test.ts b/src/common/utils/__tests__/cluster-id-url-parsing.test.ts deleted file mode 100644 index 3a4ccf605b..0000000000 --- a/src/common/utils/__tests__/cluster-id-url-parsing.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getClusterIdFromHost } from "../cluster-id-url-parsing"; - -describe("getClusterIdFromHost", () => { - const clusterFakeId = "fe540901-0bd6-4f6c-b472-bce1559d7c4a"; - - it("should return undefined for non cluster frame hosts", () => { - expect(getClusterIdFromHost("lens.app:45345")).toBeUndefined(); - }); - - it("should return ClusterId for cluster frame hosts", () => { - expect(getClusterIdFromHost(`${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - }); - - it("should return ClusterId for cluster frame hosts with additional subdomains", () => { - expect(getClusterIdFromHost(`abc.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.mno.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.mno.pqr.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.mno.pqr.stu.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.mno.pqr.stu.vwx.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - expect(getClusterIdFromHost(`abc.def.ghi.jkl.mno.pqr.stu.vwx.yz.${clusterFakeId}.lens.app:59110`)).toBe(clusterFakeId); - }); -}); diff --git a/src/common/utils/__tests__/convert-memory.test.ts b/src/common/utils/__tests__/convert-memory.test.ts deleted file mode 100644 index c69f43a061..0000000000 --- a/src/common/utils/__tests__/convert-memory.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { bytesToUnits, unitsToBytes } from "../convertMemory"; - -describe("unitsToBytes", () => { - it("without any units, just parse as float", () => { - expect(unitsToBytes("1234")).toBe(1234); - }); - - it("should parse with B suffix", () => { - expect(unitsToBytes("1234B")).toBe(1234); - }); - - it("should parse with Ki suffix", () => { - expect(unitsToBytes("1234Ki")).toBe(1234 * (1024)); - }); - - it("should parse with Ki suffix as the same as KiB", () => { - expect(unitsToBytes("1234Ki")).toBe(unitsToBytes("1234KiB")); - }); - - it("should parse with Mi suffix", () => { - expect(unitsToBytes("1234Mi")).toBe(1234 * (1024 ** 2)); - }); - - it("should parse with Mi suffix as the same as MiB", () => { - expect(unitsToBytes("1234Mi")).toBe(unitsToBytes("1234MiB")); - }); - - it("should parse with Gi suffix", () => { - expect(unitsToBytes("1234Gi")).toBe(1234 * (1024 ** 3)); - }); - - it("should parse with Gi suffix as the same as GiB", () => { - expect(unitsToBytes("1234Gi")).toBe(unitsToBytes("1234GiB")); - }); - - it("should parse with Ti suffix", () => { - expect(unitsToBytes("1234Ti")).toBe(1234 * (1024 ** 4)); - }); - - it("should parse with Ti suffix as the same as TiB", () => { - expect(unitsToBytes("1234Ti")).toBe(unitsToBytes("1234TiB")); - }); - - it("should parse with Pi suffix", () => { - expect(unitsToBytes("1234Pi")).toBe(1234 * (1024 ** 5)); - }); - - it("should parse with Pi suffix as the same as PiB", () => { - expect(unitsToBytes("1234Pi")).toBe(unitsToBytes("1234PiB")); - }); - - it("given unrelated data, return NaN", () => { - expect(unitsToBytes("I am not a number")).toBeNaN(); - }); - - it("given unrelated data, but has number, return that", () => { - expect(unitsToBytes("I am not a number, but this is 0.1")).toBe(0.1); - }); -}); - -describe("bytesToUnits", () => { - it("should return N/A for invalid bytes", () => { - expect(bytesToUnits(-1)).toBe("N/A"); - expect(bytesToUnits(Infinity)).toBe("N/A"); - expect(bytesToUnits(NaN)).toBe("N/A"); - }); - - it("given a number within the magnitude of 0..124, format with B", () => { - expect(bytesToUnits(100)).toBe("100.0B"); - }); - - it("given a number within the magnitude of 1024..1024^2, format with KiB", () => { - expect(bytesToUnits(1024)).toBe("1.0KiB"); - expect(bytesToUnits(2048)).toBe("2.0KiB"); - expect(bytesToUnits(1900)).toBe("1.9KiB"); - expect(bytesToUnits(50*1024 + 1)).toBe("50.0KiB"); - }); - - it("given a number within the magnitude of 1024^2..1024^3, format with MiB", () => { - expect(bytesToUnits(1024**2)).toBe("1.0MiB"); - expect(bytesToUnits(2048**2)).toBe("4.0MiB"); - expect(bytesToUnits(1900 * 1024)).toBe("1.9MiB"); - expect(bytesToUnits(50*(1024 ** 2) + 1)).toBe("50.0MiB"); - }); - - it("given a number within the magnitude of 1024^3..1024^4, format with GiB", () => { - expect(bytesToUnits(1024**3)).toBe("1.0GiB"); - expect(bytesToUnits(2048**3)).toBe("8.0GiB"); - expect(bytesToUnits(1900 * 1024 ** 2)).toBe("1.9GiB"); - expect(bytesToUnits(50*(1024 ** 3) + 1)).toBe("50.0GiB"); - }); - - it("given a number within the magnitude of 1024^4..1024^5, format with TiB", () => { - expect(bytesToUnits(1024**4)).toBe("1.0TiB"); - expect(bytesToUnits(2048**4)).toBe("16.0TiB"); - expect(bytesToUnits(1900 * 1024 ** 3)).toBe("1.9TiB"); - expect(bytesToUnits(50*(1024 ** 4) + 1)).toBe("50.0TiB"); - }); - - it("given a number within the magnitude of 1024^5..1024^6, format with PiB", () => { - expect(bytesToUnits(1024**5)).toBe("1.0PiB"); - expect(bytesToUnits(2048**5)).toBe("32.0PiB"); - expect(bytesToUnits(1900 * 1024 ** 4)).toBe("1.9PiB"); - expect(bytesToUnits(50*(1024 ** 5) + 1)).toBe("50.0PiB"); - }); -}); - -describe("bytesToUnits -> unitsToBytes", () => { - it("given an input, round trip to the same value, given enough precision", () => { - expect(unitsToBytes(bytesToUnits(123))).toBe(123); - expect(unitsToBytes(bytesToUnits(1024**0 + 1, { precision: 2 }))).toBe(1024**0 + 1); - expect(unitsToBytes(bytesToUnits(1024**1 + 2, { precision: 3 }))).toBe(1024**1 + 2); - expect(unitsToBytes(bytesToUnits(1024**2 + 3, { precision: 6 }))).toBe(1024**2 + 3); - expect(unitsToBytes(bytesToUnits(1024**3 + 4, { precision: 9 }))).toBe(1024**3 + 4); - expect(unitsToBytes(bytesToUnits(1024**4 + 5, { precision: 12 }))).toBe(1024**4 + 5); - expect(unitsToBytes(bytesToUnits(1024**5 + 6, { precision: 16 }))).toBe(1024**5 + 6); - expect(unitsToBytes(bytesToUnits(1024**6 + 7, { precision: 20 }))).toBe(1024**6 + 7); - }); - - it("given an invalid input, round trips to NaN", () => { - expect(unitsToBytes(bytesToUnits(-1))).toBeNaN(); - }); -}); diff --git a/src/common/utils/__tests__/formatDuration.test.ts b/src/common/utils/__tests__/formatDuration.test.ts deleted file mode 100644 index 3c42db9060..0000000000 --- a/src/common/utils/__tests__/formatDuration.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; -import { formatDuration } from "../formatDuration"; - -const second = 1000; -const minute = 60 * second; -const hour = 60 * minute; -const day = 24 * hour; -const year = 365 * day; - -describe("human format durations", () => { - test("small duration should output something", () => { - expect(formatDuration(1)).toBe("0s"); - expect(formatDuration(3)).toBe("0s"); - }); - - test("returns seconds for duration under 1 min", () => { - const res = formatDuration(8 * second); - - expect(res).toBe("8s"); - }); - - test("zero duration should output something", () => { - expect(formatDuration(0)).toBe("0s"); - }); - - describe("when compact is true", () => { - - test("duration under 3 hours return minutes", () => { - const res = formatDuration(1 * hour + 35 * minute); - - expect(res).toBe("95m"); - }); - - test("duration under 8 hours return hours and minutes", () => { - const res = formatDuration(6 * hour + 15 * minute + 20 * second); - - expect(res).toBe("6h15m"); - }); - - test("duration under 48 hours return hours", () => { - const res = formatDuration(1 * day + 4 * hour + 15 * minute); - - expect(res).toBe("28h"); - }); - - test("duration under 2 years return days", () => { - const res = formatDuration(400 * day + 4 * hour + 15 * minute); - - expect(res).toBe("400d"); - }); - - test("durations less than 8 years returns years and days", () => { - const timeValue = new Date(2020, 0, 10, 12, 0, 0, 0).getTime() - new Date(2018, 0, 4, 12, 0, 0, 0).getTime(); - - const res = formatDuration(timeValue); - - expect(res).toBe("2y5d"); - }); - - test("durations more than 8 years returns years", () => { - const timeValue = Date.now() - new Date(moment().subtract(9, "years").subtract(5, "days").toDate()).getTime(); - - const res = formatDuration(timeValue); - - expect(res).toBe("9y"); - }); - - test("durations more than 8 years returns years", () => { - const res = formatDuration(10 * year + 25 * day); - - expect(res).toBe("10y"); - }); - test("durations shouldn't include zero magnitude parts", () => { - const zeroSeconds = formatDuration(8 * minute); - - expect(zeroSeconds).toBe("8m"); - - const zeroMinutes = formatDuration(8 * hour + 15 * minute); - - expect(zeroMinutes).toBe("8h"); - - const zeroHours = formatDuration(6 * day + 2 * minute); - - expect(zeroHours).toBe("6d"); - - }); - }); - -}); diff --git a/src/common/utils/__tests__/hash-set.test.ts b/src/common/utils/__tests__/hash-set.test.ts deleted file mode 100644 index 4e78a1ca4c..0000000000 --- a/src/common/utils/__tests__/hash-set.test.ts +++ /dev/null @@ -1,512 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { HashSet, ObservableHashSet } from "../hash-set"; - -describe("ObservableHashSet", () => { - it("should not throw on creation", () => { - expect(() => new ObservableHashSet<{ a: number }>([], item => item.a.toString())).not.toThrowError(); - }); - - it("should be initializable", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - }); - - it("has should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - expect(res.has({ a: 5 })).toBe(false); - }); - - it("forEach should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - let a = 1; - - res.forEach((item) => { - expect(item.a).toEqual(a++); - }); - }); - - it("delete should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - expect(res.delete({ a: 1 })).toBe(true); - expect(res.has({ a: 1 })).toBe(false); - - expect(res.has({ a: 5 })).toBe(false); - expect(res.delete({ a: 5 })).toBe(false); - expect(res.has({ a: 5 })).toBe(false); - }); - - it("toggle should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - res.toggle({ a: 1 }); - expect(res.has({ a: 1 })).toBe(false); - - expect(res.has({ a: 6 })).toBe(false); - res.toggle({ a: 6 }); - expect(res.has({ a: 6 })).toBe(true); - }); - - it("add should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 6 })).toBe(false); - res.add({ a: 6 }); - expect(res.has({ a: 6 })).toBe(true); - }); - - it("add should treat the hash to be the same as equality", () => { - const res = new ObservableHashSet([ - { a: 1, foobar: "hello" }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - res.add({ a: 1, foobar: "goodbye" }); - expect(res.has({ a: 1 })).toBe(true); - }); - - it("clear should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - res.clear(); - expect(res.size).toBe(0); - }); - - it("replace should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - res.replace([{ a: 13 }]); - expect(res.size).toBe(1); - expect(res.has({ a: 1 })).toBe(false); - expect(res.has({ a: 13 })).toBe(true); - }); - - it("toJSON should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.toJSON()).toStrictEqual([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ]); - }); - - it("values should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.values(); - - expect(iter.next()).toStrictEqual({ - value: { a: 1 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 2 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 3 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 4 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); - - it("keys should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.keys(); - - expect(iter.next()).toStrictEqual({ - value: { a: 1 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 2 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 3 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 4 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); - - it("entries should work as expected", () => { - const res = new ObservableHashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.entries(); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 1 }, { a: 1 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 2 }, { a: 2 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 3 }, { a: 3 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 4 }, { a: 4 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); -}); - -describe("HashSet", () => { - it("should not throw on creation", () => { - expect(() => new HashSet<{ a: number }>([], item => item.a.toString())).not.toThrowError(); - }); - - it("should be initializable", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - }); - - it("has should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - expect(res.has({ a: 5 })).toBe(false); - }); - - it("forEach should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - let a = 1; - - res.forEach((item) => { - expect(item.a).toEqual(a++); - }); - }); - - it("delete should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - expect(res.delete({ a: 1 })).toBe(true); - expect(res.has({ a: 1 })).toBe(false); - - expect(res.has({ a: 5 })).toBe(false); - expect(res.delete({ a: 5 })).toBe(false); - expect(res.has({ a: 5 })).toBe(false); - }); - - it("toggle should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - res.toggle({ a: 1 }); - expect(res.has({ a: 1 })).toBe(false); - - expect(res.has({ a: 6 })).toBe(false); - res.toggle({ a: 6 }); - expect(res.has({ a: 6 })).toBe(true); - }); - - it("add should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 6 })).toBe(false); - res.add({ a: 6 }); - expect(res.has({ a: 6 })).toBe(true); - }); - - it("add should treat the hash to be the same as equality", () => { - const res = new HashSet([ - { a: 1, foobar: "hello" }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.has({ a: 1 })).toBe(true); - res.add({ a: 1, foobar: "goodbye" }); - expect(res.has({ a: 1 })).toBe(true); - }); - - it("clear should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - res.clear(); - expect(res.size).toBe(0); - }); - - it("replace should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.size).toBe(4); - res.replace([{ a: 13 }]); - expect(res.size).toBe(1); - expect(res.has({ a: 1 })).toBe(false); - expect(res.has({ a: 13 })).toBe(true); - }); - - it("toJSON should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - - expect(res.toJSON()).toStrictEqual([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ]); - }); - - it("values should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.values(); - - expect(iter.next()).toStrictEqual({ - value: { a: 1 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 2 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 3 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 4 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); - - it("keys should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.keys(); - - expect(iter.next()).toStrictEqual({ - value: { a: 1 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 2 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 3 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: { a: 4 }, - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); - - it("entries should work as expected", () => { - const res = new HashSet([ - { a: 1 }, - { a: 2 }, - { a: 3 }, - { a: 4 }, - ], item => item.a.toString()); - const iter = res.entries(); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 1 }, { a: 1 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 2 }, { a: 2 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 3 }, { a: 3 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: [{ a: 4 }, { a: 4 }], - done: false, - }); - - expect(iter.next()).toStrictEqual({ - value: undefined, - done: true, - }); - }); -}); diff --git a/src/common/utils/__tests__/iter.test.ts b/src/common/utils/__tests__/iter.test.ts deleted file mode 100644 index 2489649d90..0000000000 --- a/src/common/utils/__tests__/iter.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { join, nth, reduce, concat } from "../iter"; - -describe("iter", () => { - describe("reduce", () => { - it("can reduce a value", () => { - expect(reduce([1, 2, 3], (acc: number[], current: number) => [current, ...acc], [0])).toEqual([3, 2, 1, 0]); - }); - - it("can reduce an empty iterable", () => { - expect(reduce([], (acc: number[], current: number) => [acc[0] + current], [])).toEqual([]); - }); - }); - - describe("join", () => { - it("should not prefix the output by the seperator", () => { - expect(join(["a", "b", "c"].values(), " ")).toBe("a b c"); - }); - - it("should return empty string if iterator is empty", () => { - expect(join([].values(), " ")).toBe(""); - }); - - it("should return just first entry if iterator is of size 1", () => { - expect(join(["d"].values(), " ")).toBe("d"); - }); - }); - - describe("nth", () => { - it("should return undefined past the end of the iterator", () => { - expect(nth(["a"], 123)).toBeUndefined(); - }); - - it("should by 0-indexing the index", () => { - expect(nth(["a", "b"], 0)).toBe("a"); - }); - }); - - describe("concat", () => { - it("should yield undefined for empty args", () => { - const iter = concat(); - - expect(iter.next()).toEqual({ done: true }); - }); - - it("should yield undefined for only empty args", () => { - const iter = concat([].values(), [].values(), [].values(), [].values()); - - expect(iter.next()).toEqual({ done: true }); - }); - - it("should yield all of the first and then all of the second", () => { - const iter = concat([1, 2, 3].values(), [4, 5, 6].values()); - - expect(iter.next()).toEqual({ done: false, value: 1 }); - expect(iter.next()).toEqual({ done: false, value: 2 }); - expect(iter.next()).toEqual({ done: false, value: 3 }); - expect(iter.next()).toEqual({ done: false, value: 4 }); - expect(iter.next()).toEqual({ done: false, value: 5 }); - expect(iter.next()).toEqual({ done: false, value: 6 }); - expect(iter.next()).toEqual({ done: true }); - }); - }); -}); diff --git a/src/common/utils/__tests__/n-fircate.test.ts b/src/common/utils/__tests__/n-fircate.test.ts deleted file mode 100644 index 7e6f84a095..0000000000 --- a/src/common/utils/__tests__/n-fircate.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { nFircate } from "../n-fircate"; - -describe("nFircate", () => { - it("should produce an empty array if no parts are provided", () => { - expect(nFircate([{ a: 1 }, { a: 2 }], "a", []).length).toBe(0); - }); - - it("should ignore non-matching parts", () => { - const res = nFircate([{ a: 1 }, { a: 2 }], "a", [1]); - - expect(res.length).toBe(1); - expect(res[0].length).toBe(1); - }); - - it("should include all matching parts in each type", () => { - const res = nFircate([{ a: 1, b: "a" }, { a: 2, b: "b" }, { a: 1, b: "c" }], "a", [1, 2]); - - expect(res.length).toBe(2); - expect(res[0].length).toBe(2); - expect(res[0][0].b).toBe("a"); - expect(res[0][1].b).toBe("c"); - expect(res[1].length).toBe(1); - expect(res[1][0].b).toBe("b"); - }); - - it("should throw a type error if the same part is provided more than once", () => { - try { - nFircate([{ a: 1, b: "a" }, { a: 2, b: "b" }, { a: 1, b: "c" }], "a", [1, 2, 1]); - fail("Expected error"); - } catch (error) { - expect(error).toBeInstanceOf(TypeError); - } - }); -}); diff --git a/src/common/utils/__tests__/paths.test.ts b/src/common/utils/__tests__/paths.test.ts deleted file mode 100644 index 5a52843432..0000000000 --- a/src/common/utils/__tests__/paths.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DiContainer } from "@ogre-tools/injectable"; -import path from "path"; -import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; -import getAbsolutePathInjectable from "../../path/get-absolute-path.injectable"; -import getDirnameOfPathInjectable from "../../path/get-dirname.injectable"; -import type { IsLogicalChildPath } from "../../path/is-logical-child-path.injectable"; -import isLogicalChildPathInjectable from "../../path/is-logical-child-path.injectable"; - -describe("isLogicalChildPath", () => { - let di: DiContainer; - let isLogicalChildPath: IsLogicalChildPath; - - beforeEach(() => { - di = getDiForUnitTesting(); - }); - - describe("when using win32 paths", () => { - beforeEach(() => { - di.override(getAbsolutePathInjectable, () => path.win32.resolve); - di.override(getDirnameOfPathInjectable, () => path.win32.dirname); - isLogicalChildPath = di.inject(isLogicalChildPathInjectable); - }); - - it.each([ - { - parentPath: "C:\\Foo", - testPath: "C:\\Foo\\Bar", - expected: true, - }, - { - parentPath: "C:\\Foo", - testPath: "C:\\Bar", - expected: false, - }, - { - parentPath: "C:\\Foo", - testPath: "C:/Bar", - expected: false, - }, - { - parentPath: "C:\\Foo", - testPath: "C:/Foo/Bar", - expected: true, - }, - { - parentPath: "C:\\Foo", - testPath: "D:\\Foo\\Bar", - expected: false, - }, - ])("test %#", (testData) => { - expect(isLogicalChildPath(testData.parentPath, testData.testPath)).toBe(testData.expected); - }); - }); - - describe("when using posix paths", () => { - beforeEach(() => { - di.override(getAbsolutePathInjectable, () => path.posix.resolve); - di.override(getDirnameOfPathInjectable, () => path.posix.dirname); - isLogicalChildPath = di.inject(isLogicalChildPathInjectable); - }); - - it.each([ - { - parentPath: "/foo", - testPath: "/foo", - expected: false, - }, - { - parentPath: "/foo", - testPath: "/bar", - expected: false, - }, - { - parentPath: "/foo", - testPath: "/foobar", - expected: false, - }, - { - parentPath: "/foo", - testPath: "/foo/bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/../bar", - expected: false, - }, - { - parentPath: "/foo", - testPath: "/foo/./bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/.bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/..bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/...bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/..\\.bar", - expected: true, - }, - { - parentPath: "/bar/../foo", - testPath: "/foo/bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "/foo/\\bar", - expected: true, - }, - { - parentPath: "/foo", - testPath: "./bar", - expected: false, - }, - ])("test %#", (testData) => { - expect(isLogicalChildPath(testData.parentPath, testData.testPath)).toBe(testData.expected); - }); - }); -}); diff --git a/src/common/utils/__tests__/splitArray.test.ts b/src/common/utils/__tests__/splitArray.test.ts deleted file mode 100644 index 038d4731d8..0000000000 --- a/src/common/utils/__tests__/splitArray.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { bifurcateArray, splitArray } from "../splitArray"; - -describe("split array on element tests", () => { - it("empty array", () => { - expect(splitArray([], 10)).toStrictEqual([[], [], false]); - }); - - it("one element, not in array", () => { - expect(splitArray([1], 10)).toStrictEqual([[1], [], false]); - }); - - it("ten elements, not in array", () => { - expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [], false]); - }); - - it("one elements, in array", () => { - expect(splitArray([1], 1)).toStrictEqual([[], [], true]); - }); - - it("ten elements, in front array", () => { - expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0)).toStrictEqual([[], [1, 2, 3, 4, 5, 6, 7, 8, 9], true]); - }); - - it("ten elements, in middle array", () => { - expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)).toStrictEqual([[0, 1, 2, 3], [5, 6, 7, 8, 9], true]); - }); - - it("ten elements, in end array", () => { - expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8], [], true]); - }); -}); - -describe("bifurcateArray", () => { - it("should return tuple of empty arrays from empty array", () => { - const [left, right] = bifurcateArray([], () => true); - - expect(left).toStrictEqual([]); - expect(right).toStrictEqual([]); - }); - - it("should return all true condition returning items in the right array", () => { - const [left, right] = bifurcateArray([1, 2, 3], () => true); - - expect(left).toStrictEqual([]); - expect(right).toStrictEqual([1, 2, 3]); - }); - - it("should return all false condition returning items in the right array", () => { - const [left, right] = bifurcateArray([1, 2, 3], () => false); - - expect(left).toStrictEqual([1, 2, 3]); - expect(right).toStrictEqual([]); - }); - - it("should split array as specified", () => { - const [left, right] = bifurcateArray([1, 2, 3], (i) => Boolean(i % 2)); - - expect(left).toStrictEqual([2]); - expect(right).toStrictEqual([1, 3]); - }); -}); diff --git a/src/common/utils/__tests__/toJS.test.ts b/src/common/utils/__tests__/toJS.test.ts deleted file mode 100644 index 1704ed7e25..0000000000 --- a/src/common/utils/__tests__/toJS.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { isObservable, observable } from "mobx"; -import { toJS } from "../toJS"; - -describe("utils/toJS(data: any)", () => { - const y = { y: 2 }; - - const data = observable({ x: 1, y }, {}, { - deep: false, // this will keep ref to "y" - }); - const data2 = { - x: 1, // partially observable - y: observable(y), - }; - - test("converts mobx-observable to corresponding js struct with links preserving", () => { - expect(toJS(data).y).toBe(y); - expect(isObservable(toJS(data).y)).toBeFalsy(); - }); - - test("converts partially observable js struct", () => { - expect(toJS(data2).y).not.toBe(y); - expect(toJS(data2).y).toEqual(y); - expect(isObservable(toJS(data2).y)).toBeFalsy(); - }); -}); diff --git a/src/common/utils/__tests__/tuple.test.ts b/src/common/utils/__tests__/tuple.test.ts deleted file mode 100644 index 73a4ae1e25..0000000000 --- a/src/common/utils/__tests__/tuple.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { tuple } from "../../utils"; - -describe("tuple tests", () => { - describe("zip()", () => { - it("should yield 0 times and return 1 tuple of empty arrays when given empty array", () => { - expect(tuple.zip([]).next()).toEqual({ - done: true, - value: [[]], - }); - }); - - it("should yield 1 times and return 2 tuple of empty arrays when given one element array tuples", () => { - const i = tuple.zip([1], [2]); - - expect(i.next()).toEqual({ - done: false, - value: [1, 2], - }); - expect(i.next()).toEqual({ - done: true, - value: [[], []], - }); - }); - - it("should yield 1 times and return 2 tuple of partial arrays when given one element array tuples", () => { - const i = tuple.zip([1], [2, 3]); - - expect(i.next()).toEqual({ - done: false, - value: [1, 2], - }); - expect(i.next()).toEqual({ - done: true, - value: [[], [3]], - }); - }); - }); -}); diff --git a/src/common/utils/__tests__/union-env-path.test.ts b/src/common/utils/__tests__/union-env-path.test.ts deleted file mode 100644 index ff8ca916d2..0000000000 --- a/src/common/utils/__tests__/union-env-path.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import { unionPATHs } from "../union-env-path"; - -describe("unionPATHs", () => { - it("return the same path if given only one with no double delimiters", () => { - expect(unionPATHs(`/bin/bar${path.delimiter}/usr/bin`)).toBe(`/bin/bar${path.delimiter}/usr/bin`); - }); - - it("return equivalent path if given only one with no double delimiters", () => { - expect(unionPATHs(`/bin/bar${path.delimiter}${path.delimiter}/usr/bin`)).toBe(`/bin/bar${path.delimiter}/usr/bin`); - }); - - it("should remove duplicate entries, appending non duplicates in order received", () => { - expect(unionPATHs( - `/bin/bar${path.delimiter}/usr/bin`, - `/bin/bar${path.delimiter}/usr/lens/bat`, - )).toBe(`/bin/bar${path.delimiter}/usr/bin${path.delimiter}/usr/lens/bat`); - }); - - it("should remove duplicate entries, appending non duplicates in order received, 3", () => { - expect(unionPATHs( - `/bin/bar${path.delimiter}/usr/bin`, - `/bin/bar${path.delimiter}/usr/lens/bat`, - `/usr/local/lens${path.delimiter}/usr/bin`, - )).toBe(`/bin/bar${path.delimiter}/usr/bin${path.delimiter}/usr/lens/bat${path.delimiter}/usr/local/lens`); - }); -}); diff --git a/src/common/utils/abort-controller.ts b/src/common/utils/abort-controller.ts deleted file mode 100644 index b062fce487..0000000000 --- a/src/common/utils/abort-controller.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import AbortController from "abort-controller"; - -/** - * This is like an `AbortController` but will also abort if the parent aborts, - * but won't make the parent abort if this aborts (single direction) - */ -export class WrappedAbortController extends AbortController { - constructor(parent?: AbortController | undefined) { - super(); - - parent?.signal.addEventListener("abort", () => { - this.abort(); - }); - } -} - -export function setTimeoutFor(controller: AbortController, timeout: number): void { - const handle = setTimeout(() => controller.abort(), timeout); - - controller.signal.addEventListener("abort", () => clearTimeout(handle)); -} diff --git a/src/common/utils/add-separator/add-separator.test.ts b/src/common/utils/add-separator/add-separator.test.ts deleted file mode 100644 index 8a45b45739..0000000000 --- a/src/common/utils/add-separator/add-separator.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { addSeparator } from "./add-separator"; - -describe("add-separator", () => { - it("given multiple items, adds separators", () => { - const items = ["first", "second", "third"]; - - const actual = addSeparator((left, right) => `separator-between-${left}-and-${right}`, items); - - expect(actual).toEqual([ - "first", - "separator-between-first-and-second", - "second", - "separator-between-second-and-third", - "third", - ]); - }); - - it("given multiple items including falsy ones, adds separators", () => { - const items = [false, undefined, null, NaN]; - - const actual = addSeparator((left, right) => `separator-between-${left}-and-${right}`, items); - - expect(actual).toEqual([ - false, - "separator-between-false-and-undefined", - undefined, - "separator-between-undefined-and-null", - null, - "separator-between-null-and-NaN", - NaN, - ]); - }); - - it("given no items, does not add separator", () => { - const items: any[] = []; - - const actual = addSeparator(() => "separator", items); - - expect(actual).toEqual([]); - }); - - it("given one item, does not add separator", () => { - const items = ["first"]; - - const actual = addSeparator(() => "separator", items); - - expect(actual).toEqual(["first"]); - }); -}); diff --git a/src/common/utils/add-separator/add-separator.ts b/src/common/utils/add-separator/add-separator.ts deleted file mode 100644 index 6fd6b14642..0000000000 --- a/src/common/utils/add-separator/add-separator.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type GetSeparator = (left: Item, right: Item) => Separator; - -export const addSeparator = ( - getSeparator: GetSeparator, - items: Item[], -) => items.flatMap(toSeparatedTupleUsing(getSeparator)); - -const toSeparatedTupleUsing = - (getSeparator: GetSeparator) => - (leftItem: Item, index: number, arr: Item[]) => { - const itemIsLast = arr.length === index + 1; - - if (itemIsLast) { - return [leftItem]; - } - - const rightItem = arr[index + 1]; - const separator = getSeparator(leftItem, rightItem); - - return [leftItem, separator]; - }; diff --git a/src/common/utils/array.ts b/src/common/utils/array.ts deleted file mode 100644 index b9071c33ec..0000000000 --- a/src/common/utils/array.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * A inference typed version of `Array(length).fill(value)` - * @param length The number of entries - * @param value The value of each of the indices - */ -export function filled(length: number, value: T): T[] { - return Array(length).fill(value); -} diff --git a/src/common/utils/async-result.ts b/src/common/utils/async-result.ts deleted file mode 100644 index 69927f3275..0000000000 --- a/src/common/utils/async-result.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export type AsyncResult = - | ( - Response extends void - ? { callWasSuccessful: true; response?: undefined } - : { callWasSuccessful: true; response: Response } - ) - | { callWasSuccessful: false; error: Error }; diff --git a/src/common/utils/autobind.ts b/src/common/utils/autobind.ts deleted file mode 100644 index 49feb435e3..0000000000 --- a/src/common/utils/autobind.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Options } from "auto-bind"; -import autoBindClass from "auto-bind"; -import autoBindReactClass from "auto-bind/react"; -import React from "react"; - -// Automatically bind methods to their class instance -export function autoBind(obj: T, opts?: Options): T { - if (obj instanceof React.Component) { - return autoBindReactClass(obj, opts); - } - - return autoBindClass(obj, opts); -} diff --git a/src/common/utils/base64.ts b/src/common/utils/base64.ts deleted file mode 100755 index c55a5de6a7..0000000000 --- a/src/common/utils/base64.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Encode/decode utf-8 base64 string -import * as Base64 from "crypto-js/enc-base64"; -import * as Utf8 from "crypto-js/enc-utf8"; - -/** - * Computes utf-8 from base64 - * @param data A Base64 encoded string - * @returns The original utf-8 string - */ -export function decode(data: string): string { - return Base64.parse(data).toString(Utf8); -} - -/** - * Computes base64 from utf-8 - * @param data A normal string - * @returns A base64 encoded version - */ -export function encode(data: string): string { - return Utf8.parse(data).toString(Base64); -} diff --git a/src/common/utils/binary-name.injectable.ts b/src/common/utils/binary-name.injectable.ts deleted file mode 100644 index d240b3aae0..0000000000 --- a/src/common/utils/binary-name.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 normalizedPlatformInjectable from "../vars/normalized-platform.injectable"; - -const binaryNameInjectable = getInjectable({ - id: "binary-name", - instantiate: (di, binaryName) => { - const normalizedPlatform = di.inject(normalizedPlatformInjectable); - - if (normalizedPlatform === "windows") { - return `${binaryName}.exe`; - } - - return binaryName; - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, binaryName: string) => binaryName, - }), -}); - -export default binaryNameInjectable; diff --git a/src/common/utils/buildUrl.ts b/src/common/utils/buildUrl.ts deleted file mode 100644 index 82d114cd08..0000000000 --- a/src/common/utils/buildUrl.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { compile } from "path-to-regexp"; -import type { RouteProps } from "react-router"; -import { isDefined } from "./type-narrowing"; - -export interface UrlRouteProps extends RouteProps { - path: string; -} - -export interface URLParams

{ - params?: P; - query?: Q; - fragment?: string; -} - -export function buildURL

(path: string, { params, query, fragment }: URLParams = {}) { - const pathBuilder = compile(String(path)); - - const queryParams = query ? new URLSearchParams(Object.entries(query)).toString() : ""; - const parts = [ - pathBuilder(params), - queryParams && `?${queryParams}`, - fragment && `#${fragment}`, - ]; - - return parts.filter(isDefined).join(""); -} - -export function buildURLPositional

(path: string) { - return function (params?: P, query?: Q, fragment?: string): string { - return buildURL(path, { params, query, fragment }); - }; -} - -export type UrlParamsFor = - Pathname extends `${string}/:${infer A}?/${infer Tail}` - ? Partial> & UrlParamsFor<`/${Tail}`> - : Pathname extends `${string}/:${infer A}/${infer Tail}` - ? Record & UrlParamsFor<`/${Tail}`> - : Pathname extends `${string}/:${infer A}?` - ? Partial> - : Pathname extends `${string}/:${infer A}` - ? Record - : {}; - -export interface UrlBuilder { - compile(params: UrlParamsFor, query?: object, fragment?: string): string; -} - -export function urlBuilderFor(pathname: Pathname): UrlBuilder { - return { - compile: buildURLPositional(pathname), - }; -} diff --git a/src/common/utils/bundled-binary-path.injectable.ts b/src/common/utils/bundled-binary-path.injectable.ts deleted file mode 100644 index 42ecbca0d9..0000000000 --- a/src/common/utils/bundled-binary-path.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 joinPathsInjectable from "../path/join-paths.injectable"; -import baseBundledBinariesDirectoryInjectable from "../vars/base-bundled-binaries-dir.injectable"; -import binaryNameInjectable from "./binary-name.injectable"; - -const bundledBinaryPathInjectable = getInjectable({ - id: "bundled-binary-path", - instantiate: (di, name) => { - const joinPaths = di.inject(joinPathsInjectable); - const binaryName = di.inject(binaryNameInjectable, name); - const baseBundledBinariesDirectory = di.inject(baseBundledBinariesDirectoryInjectable); - - return joinPaths(baseBundledBinariesDirectory, binaryName); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, binaryName: string) => binaryName, - }), -}); - -export default bundledBinaryPathInjectable; diff --git a/src/common/utils/camelCase.ts b/src/common/utils/camelCase.ts deleted file mode 100644 index a37e4c7f5c..0000000000 --- a/src/common/utils/camelCase.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Convert object's keys to camelCase format -import { camelCase } from "lodash"; -import type { SingleOrMany } from "./types"; -import { isObject, isString } from "./type-narrowing"; -import * as object from "./objects"; - -export function toCamelCase[]>(obj: T): T; -export function toCamelCase>(obj: T): T; - -export function toCamelCase(obj: SingleOrMany | unknown>): SingleOrMany | unknown> { - if (Array.isArray(obj)) { - return obj.map(toCamelCase); - } - - if (isObject(obj)) { - return object.fromEntries( - object.entries(obj) - .filter((pair): pair is [string, unknown] => isString(pair[0])) - .map(([key, value]) => [camelCase(key), isObject(value) ? toCamelCase(value) : value]), - ); - } - - return obj; -} diff --git a/src/common/utils/channel/channel-injection-token.ts b/src/common/utils/channel/channel-injection-token.ts deleted file mode 100644 index 6006290f89..0000000000 --- a/src/common/utils/channel/channel-injection-token.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -export interface Channel { - id: string; - _messageTemplate?: MessageTemplate; - _returnTemplate?: ReturnTemplate; -} - diff --git a/src/common/utils/channel/channel.test.ts b/src/common/utils/channel/channel.test.ts deleted file mode 100644 index 9a361b6770..0000000000 --- a/src/common/utils/channel/channel.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainer } from "@ogre-tools/injectable"; -import { getInjectable } from "@ogre-tools/injectable"; -import type { SendMessageToChannel } from "./message-to-channel-injection-token"; -import { sendMessageToChannelInjectionToken } from "./message-to-channel-injection-token"; -import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; -import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; -import type { LensWindow } from "../../../main/start-main-application/lens-window/application-window/create-lens-window.injectable"; -import type { MessageChannel } from "./message-channel-listener-injection-token"; -import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token"; -import type { RequestFromChannel } from "./request-from-channel-injection-token"; -import { requestFromChannelInjectionToken } from "./request-from-channel-injection-token"; -import type { RequestChannel } from "./request-channel-listener-injection-token"; -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { getPromiseStatus } from "../../test-utils/get-promise-status"; -import { runInAction } from "mobx"; -import type { RequestChannelHandler } from "../../../main/utils/channel/channel-listeners/listener-tokens"; -import { getRequestChannelListenerInjectable } from "../../../main/utils/channel/channel-listeners/listener-tokens"; - -type TestMessageChannel = MessageChannel; -type TestRequestChannel = RequestChannel; - -describe("channel", () => { - describe("messaging from main to renderer, given listener for channel in a window and application has started", () => { - let messageListenerInWindowMock: jest.Mock; - let mainDi: DiContainer; - let messageToChannel: SendMessageToChannel; - let builder: ApplicationBuilder; - - beforeEach(async () => { - builder = getApplicationBuilder(); - - messageListenerInWindowMock = jest.fn(); - - const testChannelListenerInTestWindowInjectable = getInjectable({ - id: "test-channel-listener-in-test-window", - - instantiate: () => ({ - channel: testMessageChannel, - handler: messageListenerInWindowMock, - }), - - injectionToken: messageChannelListenerInjectionToken, - }); - - builder.beforeWindowStart((windowDi) => { - runInAction(() => { - windowDi.register(testChannelListenerInTestWindowInjectable); - }); - }); - - mainDi = builder.mainDi; - - await builder.startHidden(); - - messageToChannel = mainDi.inject(sendMessageToChannelInjectionToken); - }); - - describe("given window is started", () => { - let someWindowFake: LensWindow; - - beforeEach(async () => { - someWindowFake = builder.applicationWindow.create("some-window"); - - await someWindowFake.start(); - }); - - it("when sending message, triggers listener in window", () => { - messageToChannel(testMessageChannel, "some-message"); - - expect(messageListenerInWindowMock).toHaveBeenCalledWith("some-message"); - }); - - it("given window is hidden, when sending message, does not trigger listener in window", () => { - someWindowFake.close(); - - messageToChannel(testMessageChannel, "some-message"); - - expect(messageListenerInWindowMock).not.toHaveBeenCalled(); - }); - }); - - it("given multiple started windows, when sending message, triggers listeners in all windows", async () => { - const someWindowFake = builder.applicationWindow.create("some-window"); - const someOtherWindowFake = builder.applicationWindow.create("some-other-window"); - - await someWindowFake.start(); - await someOtherWindowFake.start(); - - messageToChannel(testMessageChannel, "some-message"); - - expect(messageListenerInWindowMock.mock.calls).toEqual([ - ["some-message"], - ["some-message"], - ]); - }); - }); - - describe("messaging from renderer to main, given listener for channel in a main and application has started", () => { - let messageListenerInMainMock: jest.Mock; - let messageToChannel: SendMessageToChannel; - - beforeEach(async () => { - const applicationBuilder = getApplicationBuilder(); - - messageListenerInMainMock = jest.fn(); - - const testChannelListenerInMainInjectable = getInjectable({ - id: "test-channel-listener-in-main", - - instantiate: () => ({ - channel: testMessageChannel, - handler: messageListenerInMainMock, - }), - - injectionToken: messageChannelListenerInjectionToken, - }); - - applicationBuilder.beforeApplicationStart((mainDi) => { - runInAction(() => { - mainDi.register(testChannelListenerInMainInjectable); - }); - }); - - await applicationBuilder.render(); - - const windowDi = applicationBuilder.applicationWindow.only.di; - - messageToChannel = windowDi.inject(sendMessageToChannelInjectionToken); - }); - - it("when sending message, triggers listener in main", () => { - messageToChannel(testMessageChannel, "some-message"); - - expect(messageListenerInMainMock).toHaveBeenCalledWith("some-message"); - }); - }); - - describe("requesting from main in renderer, given listener for channel in a main and application has started", () => { - let requestListenerInMainMock: AsyncFnMock>; - let requestFromChannel: RequestFromChannel; - - beforeEach(async () => { - const applicationBuilder = getApplicationBuilder(); - - requestListenerInMainMock = asyncFn(); - - const testChannelListenerInMainInjectable = getRequestChannelListenerInjectable({ - channel: testRequestChannel, - handler: () => requestListenerInMainMock, - }); - - applicationBuilder.beforeApplicationStart((mainDi) => { - runInAction(() => { - mainDi.register(testChannelListenerInMainInjectable); - }); - }); - - await applicationBuilder.render(); - - const windowDi = applicationBuilder.applicationWindow.only.di; - - requestFromChannel = windowDi.inject( - requestFromChannelInjectionToken, - ); - }); - - describe("when requesting from channel", () => { - let actualPromise: Promise; - - beforeEach(() => { - actualPromise = requestFromChannel(testRequestChannel, "some-request"); - }); - - it("triggers listener in main", () => { - expect(requestListenerInMainMock).toHaveBeenCalledWith("some-request"); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(actualPromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - it("when main resolves with response, resolves with response", async () => { - await requestListenerInMainMock.resolve("some-response"); - - const actual = await actualPromise; - - expect(actual).toBe("some-response"); - }); - }); - }); - - it("when registering multiple handlers for the same channel, throws", async () => { - const applicationBuilder = getApplicationBuilder(); - - const testChannelListenerInMainInjectable = getRequestChannelListenerInjectable({ - channel: testRequestChannel, - handler: () => () => "some-value", - }); - const testChannelListenerInMain2Injectable = getRequestChannelListenerInjectable({ - channel: testRequestChannel, - handler: () => () => "some-other-value", - }); - - testChannelListenerInMain2Injectable.id += "2"; - - applicationBuilder.beforeApplicationStart((mainDi) => { - runInAction(() => { - mainDi.register(testChannelListenerInMainInjectable); - mainDi.register(testChannelListenerInMain2Injectable); - }); - }); - - await expect(applicationBuilder.render()).rejects.toThrow('Tried to register a multiple channel handlers for "some-request-channel-id", only one handler is supported for a request channel.'); - }); -}); - -const testMessageChannel: TestMessageChannel = { - id: "some-message-channel-id", -}; - -const testRequestChannel: TestRequestChannel = { - id: "some-request-channel-id", -}; - diff --git a/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts b/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts deleted file mode 100644 index 34f62d51d5..0000000000 --- a/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { Disposer } from "../disposer"; -import type { MessageChannel, MessageChannelListener } from "./message-channel-listener-injection-token"; - -export type EnlistMessageChannelListener = (listener: MessageChannelListener>) => Disposer; - -export const enlistMessageChannelListenerInjectionToken = getInjectionToken({ - id: "enlist-message-channel-listener", -}); diff --git a/src/common/utils/channel/get-request-channel.ts b/src/common/utils/channel/get-request-channel.ts deleted file mode 100644 index 4dc5b4914e..0000000000 --- a/src/common/utils/channel/get-request-channel.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { RequestChannel } from "./request-channel-listener-injection-token"; - -export const getRequestChannel = (id: string): RequestChannel => ({ - id, -}); diff --git a/src/common/utils/channel/listening-on-message-channels.injectable.ts b/src/common/utils/channel/listening-on-message-channels.injectable.ts deleted file mode 100644 index afe0c08f24..0000000000 --- a/src/common/utils/channel/listening-on-message-channels.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 { getStartableStoppable } from "../get-startable-stoppable"; -import { disposer } from "../index"; -import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token"; -import { enlistMessageChannelListenerInjectionToken } from "./enlist-message-channel-listener-injection-token"; - -const listeningOnMessageChannelsInjectable = getInjectable({ - id: "listening-on-message-channels", - - instantiate: (di) => { - const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken); - const messageChannelListeners = di.injectMany(messageChannelListenerInjectionToken); - - return getStartableStoppable("listening-on-channels", () => ( - disposer(messageChannelListeners.map(enlistMessageChannelListener)) - )); - }, -}); - - -export default listeningOnMessageChannelsInjectable; diff --git a/src/common/utils/channel/message-channel-listener-injection-token.ts b/src/common/utils/channel/message-channel-listener-injection-token.ts deleted file mode 100644 index 5bfc45a82d..0000000000 --- a/src/common/utils/channel/message-channel-listener-injection-token.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainerForInjection } from "@ogre-tools/injectable"; -import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; - -export interface MessageChannel { - id: string; - _messageSignature?: Message; // only used to mark `Message` as used -} - -export type MessageChannelHandler = Channel extends MessageChannel - ? (message: Message) => void - : never; - -export interface MessageChannelListener { - channel: Channel; - handler: MessageChannelHandler; -} - -export const messageChannelListenerInjectionToken = getInjectionToken>>( - { - id: "message-channel-listener", - }, -); - -export interface GetMessageChannelListenerInfo< - Channel extends MessageChannel, - Message, -> { - id: string; - channel: Channel; - handler: (di: DiContainerForInjection) => MessageChannelHandler; - causesSideEffects?: boolean; -} - -export function getMessageChannelListenerInjectable< - Channel extends MessageChannel, - Message, ->(info: GetMessageChannelListenerInfo) { - return getInjectable({ - id: `${info.channel.id}-listener-${info.id}`, - instantiate: (di) => ({ - channel: info.channel, - handler: info.handler(di), - }), - injectionToken: messageChannelListenerInjectionToken, - causesSideEffects: info.causesSideEffects, - }); -} diff --git a/src/common/utils/channel/message-to-channel-injection-token.ts b/src/common/utils/channel/message-to-channel-injection-token.ts deleted file mode 100644 index 3ffd75f4f7..0000000000 --- a/src/common/utils/channel/message-to-channel-injection-token.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MessageChannel } from "./message-channel-listener-injection-token"; - -export interface SendMessageToChannel { - (channel: MessageChannel): void; - (channel: MessageChannel, message: Message): void; -} - -export type MessageChannelSender = Channel extends MessageChannel - ? () => void - : Channel extends MessageChannel - ? (message: Message) => void - : never; - -export const sendMessageToChannelInjectionToken = getInjectionToken({ - id: "send-message-to-message-channel", -}); diff --git a/src/common/utils/channel/request-channel-listener-injection-token.ts b/src/common/utils/channel/request-channel-listener-injection-token.ts deleted file mode 100644 index 2f0b84a3cc..0000000000 --- a/src/common/utils/channel/request-channel-listener-injection-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface RequestChannel { - id: string; - _requestSignature?: Request; // used only to mark `Request` as "used" - _responseSignature?: Response; // used only to mark `Response` as "used" -} diff --git a/src/common/utils/channel/request-from-channel-injection-token.ts b/src/common/utils/channel/request-from-channel-injection-token.ts deleted file mode 100644 index 14e925f190..0000000000 --- a/src/common/utils/channel/request-from-channel-injection-token.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { RequestChannel } from "./request-channel-listener-injection-token"; - -export interface RequestFromChannel { - (channel: RequestChannel, request: Request): Promise; - (channel: RequestChannel): Promise; -} - -export const requestFromChannelInjectionToken = getInjectionToken({ - id: "request-from-request-channel", -}); diff --git a/src/common/utils/cluster-id-url-parsing.ts b/src/common/utils/cluster-id-url-parsing.ts deleted file mode 100644 index 393a791f09..0000000000 --- a/src/common/utils/cluster-id-url-parsing.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ClusterId } from "../cluster-types"; - -/** - * Grab the `ClusterId` out of a host that was generated by `getClusterFrameUrl`, or nothing - * @param host The host section of a URL - * @returns The `ClusterId` part of the host, or `undefined` - */ -export function getClusterIdFromHost(host: string): ClusterId | undefined { - // e.g host == "%clusterId.localhost:45345" - const subDomains = host.split(":")[0].split("."); - - return subDomains.slice(-3, -2)[0]; // ClusterId or undefined -} - -/** - * Get the OpenLens backend routing host for a given `ClusterId` - * @param clusterId The ID to put in front of the current host - * @returns a new URL host section - */ -export function getClusterFrameUrl(clusterId: ClusterId) { - return `//${clusterId}.${location.host}`; -} diff --git a/src/common/utils/collection-functions.ts b/src/common/utils/collection-functions.ts deleted file mode 100644 index 5d6dc80548..0000000000 --- a/src/common/utils/collection-functions.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { runInAction } from "mobx"; -import { inspect } from "util"; -import { isDefined } from "./type-narrowing"; - -/** - * Get the value behind `key`. If it was not present, first insert `value` - * @param map The map to interact with - * @param key The key to insert into the map with - * @param value The value to optional add to the map - * @returns The value in the map - */ -export function getOrInsert(map: Map, key: K, value: V): V { - if (!map.has(key)) { - map.set(key, value); - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return map.get(key)!; -} - -/** - * Updates map and returns the value that was just inserted - */ -export function put(map: Map, key: K, value: V): V { - map.set(key, value); - - return value; -} - -/** - * Like `getOrInsert` but specifically for when `V` is `Map` so that - * the typings are inferred correctly. - */ -export function getOrInsertMap(map: Map>, key: K): Map { - return getOrInsert(map, key, new Map()); -} - -/** - * Like `getOrInsert` but specifically for when `V` is `Set` so that - * the typings are inferred. - */ -export function getOrInsertSet(map: Map>, key: K): Set { - return getOrInsert(map, key, new Set()); -} - -/** - * A currying version of {@link getOrInsertSet} - */ -export function getOrInsertSetFor(map: Map>): (key: K) => Set { - return (key) => getOrInsertSet(map, key); -} - -/** - * Like `getOrInsert` but with delayed creation of the item. Which is useful - * if it is very expensive to create the initial value. - */ -export function getOrInsertWith(map: Map, key: K, builder: () => V): V; -export function getOrInsertWith(map: Map | WeakMap, key: K, builder: () => V): V; - -export function getOrInsertWith(map: Map | WeakMap, key: K, builder: () => V): V { - if (!map.has(key)) { - map.set(key, builder()); - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return map.get(key)!; -} - -/** - * Like {@link getOrInsertWith} but the builder is async and will be awaited before inserting into the map - */ -export async function getOrInsertWithAsync(map: Map, key: K, asyncBuilder: () => Promise): Promise { - if (!map.has(key)) { - map.set(key, await asyncBuilder()); - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return map.get(key)!; -} - -/** - * Set the value associated with `key` iff there was not a previous value - * @param map The map to interact with - * @throws if `key` already in map - * @returns `this` so that `strictSet` can be chained - */ -export function strictSet(map: Map, key: K, val: V): typeof map { - if (map.has(key)) { - throw new TypeError(`Map already contains key: ${inspect(key)}`); - } - - return map.set(key, val); -} - -/** - * Get the value associated with `key` - * @param map The map to interact with - * @throws if `key` did not a value associated with it - */ -export function strictGet(map: Map, key: K): V { - if (!map.has(key)) { - throw new TypeError(`Map does not contains key: ${inspect(key)}`); - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return map.get(key)!; -} - -/** - * If `key` is in `set`, remove it otherwise add it. - * @param set The set to manipulate - * @param key The key to toggle the "is in"-ness of - */ -export function toggle(set: Set, key: K): void { - runInAction(() => { - // Returns true if value was already in Set; otherwise false. - if (!set.delete(key)) { - set.add(key); - } - }); -} - -/** - * A helper function to also check for defined-ness - */ -export function includes(src: T[], value: T | null | undefined): boolean { - return isDefined(value) && src.includes(value); -} diff --git a/src/common/utils/composable-responsibilities/discriminable/discriminable.ts b/src/common/utils/composable-responsibilities/discriminable/discriminable.ts deleted file mode 100644 index 7976004ee6..0000000000 --- a/src/common/utils/composable-responsibilities/discriminable/discriminable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions -export interface Discriminable { readonly kind: T } - -// Note: this will fail at transpilation time, if all kinds are not instructed in switch/case. -// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking -export const checkThatAllDiscriminablesAreExhausted = (value: T) => { - const _exhaustiveCheck: never = value; - - return new Error( - `Tried to exhaust discriminables, but no instructions were found for ${(_exhaustiveCheck as any).kind}`, - ); -}; diff --git a/src/common/utils/composable-responsibilities/labelable/labelable.ts b/src/common/utils/composable-responsibilities/labelable/labelable.ts deleted file mode 100644 index e0f2d98c21..0000000000 --- a/src/common/utils/composable-responsibilities/labelable/labelable.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export interface Labelable { - readonly label: string; -} diff --git a/src/common/utils/composable-responsibilities/orderable/orderable.ts b/src/common/utils/composable-responsibilities/orderable/orderable.ts deleted file mode 100644 index 2b4d95b3f4..0000000000 --- a/src/common/utils/composable-responsibilities/orderable/orderable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { sortBy } from "lodash/fp"; - -export interface Orderable { - readonly orderNumber: number; -} - -export type MaybeOrderable = Orderable | object; - -export const orderByOrderNumber = (maybeOrderables: T[]) => - sortBy( - (orderable) => - "orderNumber" in orderable - ? orderable.orderNumber - : Number.MAX_SAFE_INTEGER, - maybeOrderables, - ); - -export const byOrderNumber = (left: T, right: T) => ( - left.orderNumber - right.orderNumber -); diff --git a/src/common/utils/composable-responsibilities/showable/showable.ts b/src/common/utils/composable-responsibilities/showable/showable.ts deleted file mode 100644 index ad8e2ed25b..0000000000 --- a/src/common/utils/composable-responsibilities/showable/showable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IComputedValue } from "mobx"; -import { isBoolean } from "../../type-narrowing"; - -export interface Showable { - readonly isShown: IComputedValue | boolean; -} - -export type MaybeShowable = Showable | object; - -export const isShown = (showable: MaybeShowable) => { - if (!("isShown" in showable)) { - return true; - } - - if (showable.isShown === undefined) { - return true; - } - - if (isBoolean(showable.isShown)) { - return showable.isShown; - } - - return showable.isShown.get(); -}; diff --git a/src/common/utils/composite/composite-has-descendant/composite-has-descendant.test.ts b/src/common/utils/composite/composite-has-descendant/composite-has-descendant.test.ts deleted file mode 100644 index 0b7f07bf2d..0000000000 --- a/src/common/utils/composite/composite-has-descendant/composite-has-descendant.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Composite } from "../get-composite/get-composite"; -import { compositeHasDescendant } from "./composite-has-descendant"; -import { getCompositeFor } from "../get-composite/get-composite"; - -describe("composite-has-descendant, given composite with children and grand children", () => { - let composite: Composite<{ id: string; parentId?: string }>; - - beforeEach(() => { - const items = [ - { id: "some-root-id", parentId: undefined }, - { id: "some-child-item", parentId: "some-root-id" }, - - { - id: "some-grand-child-item", - parentId: "some-child-item", - }, - ]; - - const getComposite = getCompositeFor<{ - id: string; - parentId?: string; - }>({ - rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - composite = getComposite(items); - }); - - it("has a child as descendant", () => { - const actual = compositeHasDescendant( - (referenceComposite) => referenceComposite.value.id === "some-child-item", - )(composite); - - expect(actual).toBe(true); - }); - - it("has a grand child as descendant", () => { - const actual = compositeHasDescendant( - (referenceComposite) => - referenceComposite.value.id === "some-grand-child-item", - )(composite); - - expect(actual).toBe(true); - }); - - it("does not have an unrelated descendant", () => { - const actual = compositeHasDescendant( - (referenceComposite) => - referenceComposite.value.id === "some-unknown-item", - )(composite); - - expect(actual).toBe(false); - }); -}); diff --git a/src/common/utils/composite/composite-has-descendant/composite-has-descendant.ts b/src/common/utils/composite/composite-has-descendant/composite-has-descendant.ts deleted file mode 100644 index 030f79ad74..0000000000 --- a/src/common/utils/composite/composite-has-descendant/composite-has-descendant.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Composite } from "../get-composite/get-composite"; - -const compositeHasDescendant = ( - predicate: (referenceComposite: Composite) => boolean, -) => { - const _compositeHasDescendant = (composite: Composite): boolean => - predicate(composite) || - !!composite.children.find((childComposite) => - _compositeHasDescendant(childComposite), - ); - - return _compositeHasDescendant; -}; - -export { compositeHasDescendant }; diff --git a/src/common/utils/composite/find-composite/find-composite.test.ts b/src/common/utils/composite/find-composite/find-composite.test.ts deleted file mode 100644 index 5b4147070e..0000000000 --- a/src/common/utils/composite/find-composite/find-composite.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Composite } from "../get-composite/get-composite"; -import { findComposite } from "./find-composite"; -import { getCompositeFor } from "../get-composite/get-composite"; - -describe("find-composite", () => { - let composite: Composite<{ id: string; parentId?: string }>; - - beforeEach(() => { - const getComposite = getCompositeFor<{ - id: string; - parentId?: string; - }>({ - rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - composite = getComposite([ - { id: "some-root-id" }, - { id: "some-child-id", parentId: "some-root-id" }, - { id: "some-grandchild-id", parentId: "some-child-id" }, - { id: "some-other-grandchild-id", parentId: "some-child-id" }, - ]); - }); - - it("when finding root using path, does so", () => { - const actual = findComposite("some-root-id")(composite); - - expect(actual.id).toBe("some-root-id"); - }); - - it("when finding child using path, does so", () => { - const actual = findComposite("some-root-id", "some-child-id")(composite); - - expect(actual.id).toBe("some-child-id"); - }); - - it("when finding grandchild using path, does so", () => { - const actual = findComposite( - "some-root-id", - "some-child-id", - "some-grandchild-id", - )(composite); - - expect(actual.id).toBe("some-grandchild-id"); - }); - - it("when finding with non existing leaf-level path, throws", () => { - expect(() => { - findComposite( - "some-root-id", - "some-child-id", - "some-non-existing-grandchild-id", - )(composite); - }).toThrow(`Tried to find 'some-root-id -> some-child-id -> some-non-existing-grandchild-id' from a composite, but found nothing. - -Node 'some-root-id -> some-child-id' had only following children: -some-grandchild-id -some-other-grandchild-id`); - }); - - it("when finding with non-existing mid-level path, throws", () => { - expect(() => { - findComposite( - "some-root-id", - "some-non-existing-child-id", - "some-non-existing-grandchild-id", - )(composite); - }).toThrow(`Tried to find 'some-root-id -> some-non-existing-child-id -> some-non-existing-grandchild-id' from a composite, but found nothing. - -Node 'some-root-id' had only following children: -some-child-id`); - }); - - it("when finding with non-existing root-level path, throws", () => { - expect(() => { - findComposite( - "some-non-existing-root-id", - "some-non-existing-child-id", - "some-non-existing-grandchild-id", - )(composite); - }).toThrow(`Tried to find 'some-non-existing-root-id -> some-non-existing-child-id -> some-non-existing-grandchild-id' from a composite, but found nothing. - -Node 'some-root-id' had only following children: -some-child-id`); - }); -}); diff --git a/src/common/utils/composite/find-composite/find-composite.ts b/src/common/utils/composite/find-composite/find-composite.ts deleted file mode 100644 index 71dcf5baa3..0000000000 --- a/src/common/utils/composite/find-composite/find-composite.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Composite } from "../get-composite/get-composite"; - -const _findComposite = (currentLeftIds: string[], currentId: string, currentRightIds: string[], composite: Composite): Composite => { - const [nextId, ...nextRightIds] = currentRightIds; - const nextLeftIds = [...currentLeftIds, currentId]; - - if (currentRightIds.length === 0 && composite.id === currentId) { - return composite; - } - - const foundChildComposite = composite.children.find((child) => child.id === nextId); - - if (foundChildComposite) { - return _findComposite(nextLeftIds, nextId, nextRightIds, foundChildComposite); - } - - const fullPathString = [...currentLeftIds, currentId, ...currentRightIds].join(" -> "); - - throw new Error(`Tried to find '${fullPathString}' from a composite, but found nothing. - -Node '${[...currentLeftIds, composite.id].join(" -> ")}' had only following children: -${composite.children.map((child) => child.id).join("\n")}`); -}; - -export const findComposite = - (...path: string[]) => - (composite: Composite): Composite => { - const [currentId, ...rightIds] = path; - const leftIds: string[] = []; - - return _findComposite(leftIds, currentId, rightIds, composite); - }; diff --git a/src/common/utils/composite/get-composite-normalization/get-composite-normalization.test.ts b/src/common/utils/composite/get-composite-normalization/get-composite-normalization.test.ts deleted file mode 100644 index ac88973251..0000000000 --- a/src/common/utils/composite/get-composite-normalization/get-composite-normalization.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getCompositeNormalization } from "./get-composite-normalization"; -import { getCompositeFor } from "../get-composite/get-composite"; - - -describe("get-composite-normalization", () => { - it("given a composite, flattens it to paths and composites", () => { - const someRootItem = { - id: "some-root-id", - parentId: undefined, - }; - - const someItem = { - id: "some-id", - parentId: "some-root-id", - }; - - const someNestedItem = { - id: "some-child-id", - parentId: "some-id", - }; - - const items = [someRootItem, someItem, someNestedItem]; - - const getComposite = getCompositeFor<{ - id: string; - parentId?: string; - orderNumber?: number; - }>({ - rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - const composite = getComposite(items); - - const actual = getCompositeNormalization(composite); - - expect(actual).toEqual([ - [["some-root-id"], expect.objectContaining({ value: someRootItem })], - - [["some-root-id", "some-id"], expect.objectContaining({ value: someItem })], - - [ - ["some-root-id", "some-id", "some-child-id"], - expect.objectContaining({ value: someNestedItem }), - ], - ]); - }); -}); diff --git a/src/common/utils/composite/get-composite-normalization/get-composite-normalization.ts b/src/common/utils/composite/get-composite-normalization/get-composite-normalization.ts deleted file mode 100644 index eb5628171a..0000000000 --- a/src/common/utils/composite/get-composite-normalization/get-composite-normalization.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Composite } from "../get-composite/get-composite"; - -export const getCompositeNormalization = (composite: Composite) => { - const _normalizeComposite = ( - composite: Composite, - previousPath: string[] = [], - ): (readonly [path: string[], composite: Composite])[] => { - const currentPath = [...previousPath, composite.id]; - - const pathAndCompositeTuple = [currentPath, composite] as const; - - return [ - pathAndCompositeTuple, - - ...composite.children.flatMap((child) => - _normalizeComposite(child, currentPath), - ), - ]; - }; - - return _normalizeComposite(composite); -}; diff --git a/src/common/utils/composite/get-composite-paths/get-composite-paths.test.ts b/src/common/utils/composite/get-composite-paths/get-composite-paths.test.ts deleted file mode 100644 index 079f7e1b83..0000000000 --- a/src/common/utils/composite/get-composite-paths/get-composite-paths.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getCompositePaths } from "./get-composite-paths"; -import { sortBy } from "lodash/fp"; -import { getCompositeFor } from "../get-composite/get-composite"; - -describe("get-composite-paths", () => { - it("given composite with transformed children, returns paths of transformed children in hierarchical order", () => { - const someRootItem = { - id: "some-root-id", - }; - - const someChildItem1 = { - id: "some-child-id-1", - parentId: "some-root-id", - orderNumber: 1, - }; - - const someChildItem2 = { - id: "some-child-id-2", - parentId: "some-root-id", - orderNumber: 2, - }; - - const someGrandchildItem1 = { - id: "some-grandchild-id-1", - parentId: "some-child-id-1", - orderNumber: 1, - }; - - const someGrandchildItem2 = { - id: "some-grandchild-id-2", - parentId: "some-child-id-1", - orderNumber: 2, - }; - - const items = [ - someRootItem, - // Note: not in order yet. - someChildItem2, - someChildItem1, - someGrandchildItem2, - someGrandchildItem1, - ]; - - const getComposite = getCompositeFor<{ - id: string; - parentId?: string; - orderNumber?: number; - }>({ - rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - transformChildren: children => sortBy(child => child.orderNumber, children), - }); - - const composite = getComposite(items); - - const actual = getCompositePaths(composite); - - expect(actual).toEqual([ - ["some-root-id"], - ["some-root-id", "some-child-id-1"], - ["some-root-id", "some-child-id-1", "some-grandchild-id-1"], - ["some-root-id", "some-child-id-1", "some-grandchild-id-2"], - ["some-root-id", "some-child-id-2"], - ]); - }); -}); diff --git a/src/common/utils/composite/get-composite-paths/get-composite-paths.ts b/src/common/utils/composite/get-composite-paths/get-composite-paths.ts deleted file mode 100644 index 2b97467431..0000000000 --- a/src/common/utils/composite/get-composite-paths/get-composite-paths.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { pipeline } from "@ogre-tools/fp"; -import { map } from "lodash/fp"; -import type { Composite } from "../get-composite/get-composite"; -import { getCompositeNormalization } from "../get-composite-normalization/get-composite-normalization"; - -export const getCompositePaths = ( - composite: Composite, -): string[][] => pipeline(composite, getCompositeNormalization, map(([path]) => path)); diff --git a/src/common/utils/composite/get-composite/get-composite.test.ts b/src/common/utils/composite/get-composite/get-composite.test.ts deleted file mode 100644 index 594d0f3d01..0000000000 --- a/src/common/utils/composite/get-composite/get-composite.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Composite } from "./get-composite"; -import { getCompositePaths } from "../get-composite-paths/get-composite-paths"; -import { sortBy } from "lodash/fp"; -import { getCompositeFor } from "./get-composite"; - -interface SomeItem { - id: string; - parentId?: string; - orderNumber?: number; -} - -describe("get-composite", () => { - it("given items and an explicit root id, creates a composite", () => { - const someRootItem = { - id: "some-root-id", - someProperty: "some-root-content", - }; - - const someIrrelevantRootItem = { - id: "some-irrelevant-root-id", - someProperty: "some-other-root-content", - }; - - const someItem = { - id: "some-id", - parentId: "some-root-id", - someProperty: "some-content", - }; - - const someNestedItem = { - id: "some-nested-id", - parentId: "some-id", - someProperty: "some-nested-content", - }; - - const items = [someRootItem, someIrrelevantRootItem, someItem, someNestedItem]; - - const getComposite = getCompositeFor({ - rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - const composite = getComposite(items); - - expect(composite).toEqual({ - id: "some-root-id", - value: someRootItem, - - children: [ - { - id: "some-id", - parentId: "some-root-id", - value: someItem, - - children: [ - { - id: "some-nested-id", - parentId: "some-id", - value: someNestedItem, - children: [], - }, - ], - }, - ], - }); - }); - - it("given items and implicit root, creates a composite", () => { - const someRootItem = { - id: "some-root-id", - someProperty: "some-root-content", - // Notice: no "parentId" makes this the implicit root. - parentId: undefined, - }; - - const someItem = { - id: "some-id", - parentId: "some-root-id", - someProperty: "some-content", - }; - - const someNestedItem = { - id: "some-nested-id", - parentId: "some-id", - someProperty: "some-nested-content", - }; - - const items = [someRootItem, someItem, someNestedItem]; - - const getComposite = getCompositeFor({ - // Notice: no root id - // rootId: "some-root-id", - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - const composite = getComposite(items); - - expect(composite).toEqual({ - id: "some-root-id", - value: someRootItem, - - children: [ - { - id: "some-id", - parentId: "some-root-id", - value: someItem, - - children: [ - { - id: "some-nested-id", - parentId: "some-id", - value: someNestedItem, - children: [], - }, - ], - }, - ], - }); - }); - - it("given items and an unspecified root id and multiple items without parent as root, throws", () => { - const someRootItem = { - id: "some-root-id", - // Notice: no "parentId" makes this a root. - parentId: undefined, - }; - - const someOtherRootItem = { - id: "some-other-root-id", - // Notice: no "parentId" makes also this a root. - parentId: undefined, - }; - - const items = [someRootItem, someOtherRootItem]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - expect(() => { - getComposite(items); - }).toThrow( - 'Tried to get a composite, but multiple roots where encountered: "some-root-id", "some-other-root-id"', - ); - }); - - it("given non-unique ids, throws", () => { - const someItem = { - id: "some-id", - parentId: "irrelevant", - }; - - const someOtherItem = { - id: "some-id", - parentId: "irrelevant", - }; - - const items = [someItem, someOtherItem]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - expect(() => { - getComposite(items); - }).toThrow( - 'Tried to get a composite but encountered non-unique ids: "some-id"', - ); - }); - - it("given items with missing parent ids, when creating composite without handling for unknown parents, throws", () => { - const someItem = { - id: "some-id", - parentId: undefined, - }; - - const someItemWithMissingParentId = { - id: "some-other-id", - parentId: "some-missing-id", - }; - - const items = [someItem, someItemWithMissingParentId]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - expect(() => { - getComposite(items); - }).toThrow( - `Tried to get a composite but encountered missing parent ids: "some-missing-id". - -Available parent ids are: -"some-id", -"some-other-id"`, - ); - }); - - describe("given items with missing parents, when creating composite with handling for missing parents", () => { - let composite: Composite; - let handleMissingParentIdMock: jest.Mock; - - beforeEach(() => { - const someItem = { - id: "some-root-id", - }; - - const someItemWithMissingParentId = { - id: "some-orphan-id", - // Note: the item corresponding to this id does not exist, - // making this item have a "missing parent". - parentId: "some-missing-id", - }; - - const items = [someItem, someItemWithMissingParentId]; - - handleMissingParentIdMock = jest.fn(); - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - handleMissingParentIds: handleMissingParentIdMock, - }); - - composite = getComposite(items); - }); - - it("creates composite without the orphan item, and without throwing", () => { - const paths = getCompositePaths(composite); - - expect(paths).toEqual([["some-root-id"]]); - }); - - it("handles the missing parent ids", () => { - expect(handleMissingParentIdMock).toHaveBeenCalledWith({ - missingParentIds: ["some-missing-id"], - availableParentIds: ["some-root-id", "some-orphan-id"], - }); - }); - }); - - it("given items with same id and parent id, throws", () => { - const someItem = { - id: "some-id", - parentId: "some-id", - }; - - const someRoot = { - id: "root", - parentId: undefined, - }; - - const items = [someItem, someRoot]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - expect(() => { - getComposite(items); - }).toThrow( - 'Tried to get a composite, but found items with self as parent: "some-id"', - ); - }); - - it("given undefined ids, throws", () => { - const root = { - parentId: undefined, - id: "some-root", - }; - - const someItem = { - parentId: "some-root", - id: undefined, - }; - - const someOtherItem = { - parentId: "some-root", - id: undefined, - }; - - const items = [root, someItem, someOtherItem]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - }); - - expect(() => { - getComposite(items); - }).toThrow("Tried to get a composite but encountered 2 undefined ids"); - }); - - it("given transformed children, creates a composite with transformed children", () => { - const someRootItem = { - id: "some-root-id", - orderNumber: 1, - }; - - const someItem1 = { - id: "some-id-1", - parentId: "some-root-id", - orderNumber: 1, - }; - - const someItem2 = { - id: "some-id-2", - parentId: "some-root-id", - orderNumber: 2, - }; - - const someChildItem1 = { - id: "some-child-id-1", - parentId: "some-id-1", - orderNumber: 1, - }; - - const someChildItem2 = { - id: "some-child-id-2", - parentId: "some-id-1", - orderNumber: 2, - }; - - const items = [ - someRootItem, - // Note: not in order yet. - someItem2, - someItem1, - someChildItem2, - someChildItem1, - ]; - - const getComposite = getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - transformChildren: (things) => - sortBy((thing) => thing.orderNumber, things), - }); - - const composite = getComposite(items); - - const orderedPaths = getCompositePaths(composite); - - expect(orderedPaths).toEqual([ - ["some-root-id"], - ["some-root-id", "some-id-1"], - ["some-root-id", "some-id-1", "some-child-id-1"], - ["some-root-id", "some-id-1", "some-child-id-2"], - ["some-root-id", "some-id-2"], - ]); - }); -}); diff --git a/src/common/utils/composite/get-composite/get-composite.ts b/src/common/utils/composite/get-composite/get-composite.ts deleted file mode 100644 index 7e499ad40a..0000000000 --- a/src/common/utils/composite/get-composite/get-composite.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { pipeline } from "@ogre-tools/fp"; -import { - countBy, - filter, - toPairs, - nth, - map, - uniq, - without, - compact, - identity, -} from "lodash/fp"; - -export interface Composite { - id: string; - parentId: string | undefined; - value: T; - children: Composite[]; -} - -interface Configuration { - rootId?: string; - getId: (thing: T) => string; - getParentId: (thing: T) => string | undefined; - transformChildren?: (things: T[]) => T[]; - handleMissingParentIds?: (parentIdsForHandling: ParentIdsForHandling) => void; -} - -export const getCompositeFor = ({ - rootId = undefined, - getId, - getParentId, - transformChildren = identity, - handleMissingParentIds = throwMissingParentIds, -}: Configuration) => (source: T[]) => { - const undefinedIds = pipeline( - source, - filter((x) => getId(x) === undefined), - ); - - if (undefinedIds.length) { - throw new Error( - `Tried to get a composite but encountered ${undefinedIds.length} undefined ids`, - ); - } - - const selfReferencingIds = pipeline( - source, - filter((x) => getId(x) === getParentId(x)), - map(getId), - ); - - if (selfReferencingIds.length) { - throw new Error( - `Tried to get a composite, but found items with self as parent: "${selfReferencingIds.join( - '", ', - )}"`, - ); - } - - const duplicateIds = pipeline( - source, - countBy(getId), - toPairs, - filter(([, count]) => count > 1), - map(nth(0)), - ); - - if (duplicateIds.length) { - throw new Error( - `Tried to get a composite but encountered non-unique ids: "${duplicateIds - .map((x) => String(x)) - .join('", "')}"`, - ); - } - - const allIds = pipeline(source, map(getId)); - - const allParentIds = pipeline(source, map(getParentId), uniq, compact); - - const missingParentIds = without(allIds, allParentIds); - - if (missingParentIds.length) { - handleMissingParentIds({ missingParentIds, availableParentIds: allIds }); - } - - const toComposite = (thing: T): Composite => { - const thingId = getId(thing); - - return { - id: thingId, - parentId: getParentId(thing), - value: thing, - - children: pipeline( - source, - - filter((childThing) => { - const parentId = getParentId(childThing); - - return parentId === thingId; - }), - - transformChildren, - - map(toComposite), - ), - }; - }; - - const isRootId = rootId - ? (thing: T) => getId(thing) === rootId - : (thing: T) => getParentId(thing) === undefined; - - const roots = source.filter(isRootId); - - if (roots.length > 1) { - throw new Error( - `Tried to get a composite, but multiple roots where encountered: "${roots - .map(getId) - .join('", "')}"`, - ); - } - - return toComposite(roots[0]); - }; - -interface ParentIdsForHandling { - missingParentIds: string[]; - availableParentIds: string[]; -} - -const throwMissingParentIds = ({ - missingParentIds, - availableParentIds, -}: ParentIdsForHandling) => { - throw new Error( - `Tried to get a composite but encountered missing parent ids: "${missingParentIds.join( - '", "', - )}".\n\nAvailable parent ids are:\n"${availableParentIds.join('",\n"')}"`, - ); -}; diff --git a/src/common/utils/composite/interfaces.ts b/src/common/utils/composite/interfaces.ts deleted file mode 100644 index 1c29a0e4ce..0000000000 --- a/src/common/utils/composite/interfaces.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface ParentOfChildComposite { - id: Id; -} - -export interface ChildOfParentComposite { - parentId: ParentId; -} - -export type RootComposite = - & { parentId: undefined } - & ParentOfChildComposite; diff --git a/src/common/utils/computed-or.ts b/src/common/utils/computed-or.ts deleted file mode 100644 index 4e93394924..0000000000 --- a/src/common/utils/computed-or.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IComputedValue } from "mobx"; -import { computed } from "mobx"; - -export const computedOr = (...values: IComputedValue[]) => computed(( - () => values.some(value => value.get()) -)); diff --git a/src/common/utils/convertCpu.ts b/src/common/utils/convertCpu.ts deleted file mode 100644 index 86b57b78f8..0000000000 --- a/src/common/utils/convertCpu.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { TypedRegEx } from "typed-regex"; - -// Helper to convert CPU K8S units to numbers - -const unitConverters = new Map([ - ["n", 1000 ** -3], - ["u", 1000 ** -2], - ["m", 1000 ** -1], // milli - ["", 1000 ** 0], // no units - ["k", 1000 ** 1], - ["M", 1000 ** 2], - ["G", 1000 ** 3], - ["P", 1000 ** 4], - ["T", 1000 ** 5], - ["E", 1000 ** 6], -]); - -const cpuUnitsRegex = TypedRegEx("^(?[+-]?[0-9.]+(e[-+]?[0-9]+)?)(?[EinumkKMGTP]*)$"); - -export function cpuUnitsToNumber(value: string) { - const match = cpuUnitsRegex.captures(value); - - if (!match) { - return undefined; - } - - const { digits = "", unit } = match; - const conversion = unitConverters.get(unit); - - if (conversion === undefined) { - return undefined; - } - - return parseFloat(digits) * conversion; -} diff --git a/src/common/utils/convertMemory.ts b/src/common/utils/convertMemory.ts deleted file mode 100644 index 7cae1cf0c1..0000000000 --- a/src/common/utils/convertMemory.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import assert from "assert"; -import * as iter from "./iter"; - -// Helper to convert memory from units Ki, Mi, Gi, Ti, Pi to bytes and vise versa - -const baseMagnitude = 1024; -const maxMagnitude = ["PiB", baseMagnitude ** 5] as const; -const magnitudes = new Map([ - ["B", 1] as const, - ["KiB", baseMagnitude ** 1] as const, - ["MiB", baseMagnitude ** 2] as const, - ["GiB", baseMagnitude ** 3] as const, - ["TiB", baseMagnitude ** 4] as const, - maxMagnitude, -]); -const unitRegex = /(?[0-9]+(\.[0-9]*)?)(?(B|[KMGTP]iB?))?/; - -type BinaryUnit = typeof magnitudes extends Map ? Key : never; - -export function unitsToBytes(value: string): number { - const unitsMatch = value.match(unitRegex); - - if (!unitsMatch?.groups) { - return NaN; - } - - const parsedValue = parseFloat(unitsMatch.groups.value); - - if (!unitsMatch.groups?.suffix) { - return parsedValue; - } - - const magnitude = magnitudes.get(unitsMatch.groups.suffix as BinaryUnit) - ?? magnitudes.get(`${unitsMatch.groups.suffix}B` as BinaryUnit); - - assert(magnitude, "UnitRegex is wrong some how"); - - return parseInt((parsedValue * magnitude).toFixed(1)); -} - -export interface BytesToUnitesOptions { - /** - * The number of decimal places. MUST be an integer. MUST be in the range [0, 20]. - * @default 1 - */ - precision?: number; -} - -export function bytesToUnits(bytes: number, { precision = 1 }: BytesToUnitesOptions = {}): string { - if (bytes <= 0 || isNaN(bytes) || !isFinite(bytes)) { - return "N/A"; - } - - const index = Math.floor(Math.log(bytes) / Math.log(baseMagnitude)); - const [suffix, magnitude] = iter.nth(magnitudes.entries(), index) ?? maxMagnitude; - - return `${(bytes / magnitude).toFixed(precision)}${suffix}`; -} diff --git a/src/common/utils/date/get-current-date-time.ts b/src/common/utils/date/get-current-date-time.ts deleted file mode 100644 index aa4d5e7fa3..0000000000 --- a/src/common/utils/date/get-current-date-time.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import moment from "moment"; - -export const getCurrentDateTime = () => moment().utc().format(); - -export const getMillisecondsFromUnixEpoch = () => Date.now(); - -export const getSecondsFromUnixEpoch = () => Math.floor(getMillisecondsFromUnixEpoch() / 1000); diff --git a/src/common/utils/debouncePromise.ts b/src/common/utils/debouncePromise.ts deleted file mode 100755 index c15b206a76..0000000000 --- a/src/common/utils/debouncePromise.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Debouncing promise evaluation - -export function debouncePromise(func: (...args: F) => T | Promise, timeout = 0): (...args: F) => Promise { - let timer: NodeJS.Timeout; - - return (...params: F) => new Promise(resolve => { - clearTimeout(timer); - timer = global.setTimeout(() => resolve(func(...params)), timeout); - }); -} diff --git a/src/common/utils/delay.ts b/src/common/utils/delay.ts deleted file mode 100644 index 96171f6535..0000000000 --- a/src/common/utils/delay.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type AbortController from "abort-controller"; - -/** - * Return a promise that will be resolved after at least `timeout` ms have - * passed. If `failFast` is provided then the promise is also resolved if it has - * been aborted. - * @param timeout The number of milliseconds before resolving - * @param failFast An abort controller instance to cause the delay to short-circuit - */ -export function delay(timeout = 1000, failFast?: AbortController): Promise { - return new Promise(resolve => { - const timeoutId = setTimeout(resolve, timeout); - - failFast?.signal.addEventListener("abort", () => { - clearTimeout(timeoutId); - resolve(); - }); - }); -} diff --git a/src/common/utils/disposer.ts b/src/common/utils/disposer.ts deleted file mode 100644 index 2949a7ae35..0000000000 --- a/src/common/utils/disposer.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { SingleOrMany } from "./types"; - - - -export interface Disposer { - (): void; -} - -export interface Disposable { - dispose(): void; -} - -export interface ExtendableDisposer extends Disposer { - push(...vals: (Disposer | ExtendableDisposer | Disposable)[]): void; -} - -export function disposer(...items: SingleOrMany[]): ExtendableDisposer { - return Object.assign(() => { - for (const item of items.flat()) { - if (!item) { - continue; - } - - if (typeof item === "function") { - item(); - } else { - item.dispose(); - } - } - items.length = 0; - }, { - push: (...newItems) => items.push(...newItems), - } as Pick); -} diff --git a/src/common/utils/escapeRegExp.ts b/src/common/utils/escapeRegExp.ts deleted file mode 100644 index 9d5c7e2ff6..0000000000 --- a/src/common/utils/escapeRegExp.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Helper to sanitize / escape special chars for passing to RegExp-constructor - -export function escapeRegExp(str: string) { - return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string -} diff --git a/src/common/utils/find-exactly-one/find-exactly-one.test.ts b/src/common/utils/find-exactly-one/find-exactly-one.test.ts deleted file mode 100644 index 08dbb9ce72..0000000000 --- a/src/common/utils/find-exactly-one/find-exactly-one.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { findExactlyOne } from "./find-exactly-one"; - -describe("find-exactly-one", () => { - it("when predicate matches to single item, returns the item", () => { - const actual = findExactlyOne((item) => item === "some-item")([ - "some-item", - "some-other-item", - ]); - - expect(actual).toBe("some-item"); - }); - - it("when predicate matches to many items, throws", () => { - expect(() => { - findExactlyOne((item) => item === "some-item")([ - "some-item", - "some-item", - ]); - }).toThrow("Tried to find exactly one, but found many"); - }); - - it("when predicate does not match, throws", () => { - expect(() => { - findExactlyOne((item) => item === "some-item")([ - "some-other-item", - ]); - }).toThrow("Tried to find exactly one, but didn't find any"); - }); -}); diff --git a/src/common/utils/find-exactly-one/find-exactly-one.ts b/src/common/utils/find-exactly-one/find-exactly-one.ts deleted file mode 100644 index 181720d8b1..0000000000 --- a/src/common/utils/find-exactly-one/find-exactly-one.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export const findExactlyOne = (predicate: (item: T) => boolean) => (collection: T[]): T => { - const itemsFound = collection.filter(predicate); - - if (!itemsFound.length) { - throw new Error( - "Tried to find exactly one, but didn't find any", - ); - } - - if (itemsFound.length > 1) { - throw new Error( - "Tried to find exactly one, but found many", - ); - } - - return itemsFound[0]; -}; diff --git a/src/common/utils/formatDuration.ts b/src/common/utils/formatDuration.ts deleted file mode 100644 index 1ea4e1e546..0000000000 --- a/src/common/utils/formatDuration.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import moment from "moment"; - -/** - * This function formats durations in a more human readable form. - * @param timeValue the duration in milliseconds to format - */ -export function formatDuration(timeValue: number, compact = true) { - const duration = moment.duration(timeValue, "milliseconds"); - const seconds = Math.floor(duration.asSeconds()); - const separator = compact ? "": " "; - - if (seconds < 0) { - return "0s"; - } else if (seconds < 60*2 ) { - return `${seconds}s`; - } - - const minutes = Math.floor(duration.asMinutes()); - - if (minutes < 10) { - const seconds = duration.seconds(); - - return getMeaningfulValues([minutes, seconds], ["m", "s"], separator); - } else if (minutes < 60 * 3) { - if (!compact) { - return getMeaningfulValues([minutes, duration.seconds()], ["m", "s"]); - } - - return `${minutes}m`; - } - - const hours = Math.floor(duration.asHours()); - - if(hours < 8) { - const minutes = duration.minutes(); - - return getMeaningfulValues([hours, minutes], ["h", "m"], separator); - } else if (hours < 48) { - if (compact) { - return `${hours}h`; - } else { - return getMeaningfulValues([hours, duration.minutes()], ["h", "m"]); - } - } - - const days = Math.floor(duration.asDays()); - - if (days < 8) { - const hours = duration.hours(); - - if (compact) { - return getMeaningfulValues([days, hours], ["d", "h"], separator); - } else { - return getMeaningfulValues([days, hours, duration.minutes()], ["d", "h", "m"]); - } - } - const years = Math.floor(duration.asYears()); - - if (years < 2) { - if (compact) { - return `${days}d`; - } else { - return getMeaningfulValues([days, duration.hours(), duration.minutes()], ["d", "h", "m"]); - } - } else if (years < 8) { - const days = duration.days(); - - if (compact) { - return getMeaningfulValues([years, days], ["y", "d"], separator); - } - } - - if (compact) { - return `${years}y`; - } - - return getMeaningfulValues([years, duration.days(), duration.hours(), duration.minutes()], ["y", "d", "h", "m"]); -} - -function getMeaningfulValues(values: number[], suffixes: string[], separator = " ") { - return values - .map((a, i): [number, string] => [a, suffixes[i]]) - .filter(([dur]) => dur > 0) - .map(([dur, suf]) => dur + suf) - .join(separator); -} diff --git a/src/common/utils/generate-new-id-for.ts b/src/common/utils/generate-new-id-for.ts deleted file mode 100644 index 31aba26e20..0000000000 --- a/src/common/utils/generate-new-id-for.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { createHash } from "crypto"; - -export function generateNewIdFor(cluster: { kubeConfigPath: string; contextName: string }): string { - return createHash("md5").update(`${cluster.kubeConfigPath}:${cluster.contextName}`).digest("hex"); -} diff --git a/src/common/utils/get-error-message.ts b/src/common/utils/get-error-message.ts deleted file mode 100644 index b5d7dd4244..0000000000 --- a/src/common/utils/get-error-message.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export const getErrorMessage = (error: unknown): string => { - if (typeof error === "string") { - return error; - } - - if (error instanceof Error) { - return error.message; - } - - return JSON.stringify(error); -}; diff --git a/src/common/utils/get-random-id.global-override-for-injectable.ts b/src/common/utils/get-random-id.global-override-for-injectable.ts deleted file mode 100644 index a0f87b4180..0000000000 --- a/src/common/utils/get-random-id.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import getRandomIdInjectable from "./get-random-id.injectable"; - -export default getGlobalOverride(getRandomIdInjectable, () => () => "some-irrelevant-random-id"); diff --git a/src/common/utils/get-random-id.injectable.ts b/src/common/utils/get-random-id.injectable.ts deleted file mode 100644 index b6d8b99f41..0000000000 --- a/src/common/utils/get-random-id.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { v4 as getRandomId } from "uuid"; - -const getRandomIdInjectable = getInjectable({ - id: "get-random-id", - instantiate: () => () => getRandomId(), - causesSideEffects: true, -}); - -export default getRandomIdInjectable; diff --git a/src/common/utils/get-startable-stoppable.test.ts b/src/common/utils/get-startable-stoppable.test.ts deleted file mode 100644 index dc8b24dd43..0000000000 --- a/src/common/utils/get-startable-stoppable.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { StartableStoppable } from "./get-startable-stoppable"; -import { getStartableStoppable } from "./get-startable-stoppable"; - -describe("getStartableStoppable", () => { - let stopMock: jest.MockedFunction<() => void>; - let startMock: jest.MockedFunction<() => () => void>; - let actual: StartableStoppable; - - beforeEach(() => { - stopMock = jest.fn(); - startMock = jest.fn().mockImplementation(() => stopMock); - actual = getStartableStoppable("some-id", startMock); - }); - - it("does not start yet", () => { - expect(startMock).not.toHaveBeenCalled(); - }); - - it("does not stop yet", () => { - expect(stopMock).not.toHaveBeenCalled(); - }); - - it("when stopping before ever starting, throws", () => { - expect(() => actual.stop()).toThrow("Tried to stop \"some-id\", but it is already stopped."); - }); - - it("is not started", () => { - expect(actual.started).toBe(false); - }); - - describe("when started", () => { - beforeEach(() => { - actual.start(); - }); - - it("calls start function", () => { - expect(startMock).toHaveBeenCalled(); - }); - - it("is started", () => { - expect(actual.started).toBe(true); - }); - - describe("when stopped", () => { - beforeEach(() => { - actual.stop(); - }); - - it("calls stop function", () => { - expect(stopMock).toBeCalled(); - }); - - it("is stopped", () => { - expect(actual.started).toBe(false); - }); - }); - }); -}); diff --git a/src/common/utils/get-startable-stoppable.ts b/src/common/utils/get-startable-stoppable.ts deleted file mode 100644 index 05d8b4d9af..0000000000 --- a/src/common/utils/get-startable-stoppable.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type Stopper = () => void; -export type Starter = () => Stopper; - -export interface StartableStoppable { - readonly started: boolean; - start: () => void; - stop: () => void; -} - -type StartableStoppableState = "stopped" | "started" | "starting"; - -export function getStartableStoppable(id: string, startAndGetStopper: Starter): StartableStoppable { - let stop: Stopper; - let state: StartableStoppableState = "stopped"; - - return { - get started() { - return state === "started"; - }, - - start: () => { - if (state !== "stopped") { - throw new Error(`Tried to start "${id}", but it is already ${state}.`); - } - - state = "starting"; - stop = startAndGetStopper(); - state = "started"; - }, - - stop: () => { - if (state !== "started") { - throw new Error(`Tried to stop "${id}", but it is already ${state}.`); - } - - stop(); - state = "stopped"; - }, - }; -} diff --git a/src/common/utils/getRandId.ts b/src/common/utils/getRandId.ts deleted file mode 100644 index 489456d56b..0000000000 --- a/src/common/utils/getRandId.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Create random system name - -export function getRandId({ prefix = "", suffix = "", sep = "_" } = {}) { - const randId = () => Math.random().toString(16).slice(2); - - return [prefix, randId(), suffix].filter(s => s).join(sep); -} diff --git a/src/common/utils/hash-set.ts b/src/common/utils/hash-set.ts deleted file mode 100644 index 23ce13b1b2..0000000000 --- a/src/common/utils/hash-set.ts +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IInterceptable, IInterceptor, IListenable, ISetWillChange, ObservableMap } from "mobx"; -import { action, observable, ObservableSet } from "mobx"; - -export function makeIterableIterator(iterator: Iterator): IterableIterator { - (iterator as IterableIterator)[Symbol.iterator] = () => iterator as IterableIterator; - - return iterator as IterableIterator; -} - -export class HashSet implements Set { - #hashmap: Map; - - constructor(initialValues: Iterable, protected hasher: (item: T) => string) { - this.#hashmap = new Map(Array.from(initialValues, value => [this.hasher(value), value])); - } - - replace(other: ObservableHashSet | ObservableSet | Set | readonly T[]): this { - if (other === null || other === undefined) { - return this; - } - - if (!(Array.isArray(other) || other instanceof Set || other instanceof ObservableHashSet || other instanceof ObservableSet)) { - throw new Error(`ObservableHashSet: Cannot initialize set from ${other}`); - } - - this.clear(); - - for (const value of other) { - this.add(value); - } - - return this; - } - - clear(): void { - this.#hashmap.clear(); - } - - add(value: T): this { - this.#hashmap.set(this.hasher(value), value); - - return this; - } - - toggle(value: T): void { - const hash = this.hasher(value); - - if (this.#hashmap.has(hash)) { - this.#hashmap.delete(hash); - } else { - this.#hashmap.set(hash, value); - } - } - - delete(value: T): boolean { - return this.#hashmap.delete(this.hasher(value)); - } - - forEach(callbackfn: (value: T, key: T, set: Set) => void, thisArg?: any): void { - this.#hashmap.forEach(value => callbackfn(value, value, thisArg ?? this)); - } - - has(value: T): boolean { - return this.#hashmap.has(this.hasher(value)); - } - - get size(): number { - return this.#hashmap.size; - } - - entries(): IterableIterator<[T, T]> { - let nextIndex = 0; - const keys = Array.from(this.keys()); - const values = Array.from(this.values()); - - return makeIterableIterator<[T, T]>({ - next() { - const index = nextIndex++; - - return index < values.length - ? { value: [keys[index], values[index]], done: false } - : { done: true, value: undefined }; - }, - }); - } - - keys(): IterableIterator { - return this.values(); - } - - values(): IterableIterator { - let nextIndex = 0; - const observableValues = Array.from(this.#hashmap.values()); - - return makeIterableIterator({ - next: () => { - return nextIndex < observableValues.length - ? { value: observableValues[nextIndex++], done: false } - : { done: true, value: undefined }; - }, - }); - } - - [Symbol.iterator](): IterableIterator { - return this.#hashmap.values(); - } - - get [Symbol.toStringTag](): string { - return "Set"; - } - - toJSON(): T[] { - return Array.from(this); - } - - toString(): string { - return "[object Set]"; - } -} - -export class ObservableHashSet implements Set, IInterceptable, IListenable { - #hashmap: ObservableMap; - - get interceptors_(): IInterceptor>[] { - return []; - } - - get changeListeners_(): Function[] { - return []; - } - - constructor(initialValues: Iterable, protected hasher: (item: T) => string) { - this.#hashmap = observable.map(Array.from(initialValues, value => [this.hasher(value), value]), undefined); - } - - @action - replace(other: ObservableHashSet | ObservableSet | Set | readonly T[]): this { - if (other === null || other === undefined) { - return this; - } - - if (!(Array.isArray(other) || other instanceof Set || other instanceof ObservableHashSet || other instanceof ObservableSet)) { - throw new Error(`ObservableHashSet: Cannot initialize set from ${other}`); - } - - this.clear(); - - for (const value of other) { - this.add(value); - } - - return this; - } - - clear(): void { - this.#hashmap.clear(); - } - - add(value: T): this { - this.#hashmap.set(this.hasher(value), value); - - return this; - } - - @action - toggle(value: T): void { - const hash = this.hasher(value); - - if (this.#hashmap.has(hash)) { - this.#hashmap.delete(hash); - } else { - this.#hashmap.set(hash, value); - } - } - - delete(value: T): boolean { - return this.#hashmap.delete(this.hasher(value)); - } - - forEach(callbackfn: (value: T, key: T, set: Set) => void, thisArg?: any): void { - this.#hashmap.forEach(value => callbackfn(value, value, thisArg ?? this)); - } - - has(value: T): boolean { - return this.#hashmap.has(this.hasher(value)); - } - - get size(): number { - return this.#hashmap.size; - } - - entries(): IterableIterator<[T, T]> { - let nextIndex = 0; - const keys = Array.from(this.keys()); - const values = Array.from(this.values()); - - return makeIterableIterator<[T, T]>({ - next() { - const index = nextIndex++; - - return index < values.length - ? { value: [keys[index], values[index]], done: false } - : { done: true, value: undefined }; - }, - }); - } - - keys(): IterableIterator { - return this.values(); - } - - values(): IterableIterator { - let nextIndex = 0; - const observableValues = Array.from(this.#hashmap.values()); - - return makeIterableIterator({ - next: () => { - return nextIndex < observableValues.length - ? { value: observableValues[nextIndex++], done: false } - : { done: true, value: undefined }; - }, - }); - } - - [Symbol.iterator](): IterableIterator { - return this.#hashmap.values(); - } - - get [Symbol.toStringTag](): string { - return "Set"; - } - - toJSON(): T[] { - return Array.from(this); - } - - toString(): string { - return "[object ObservableSet]"; - } -} diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts deleted file mode 100644 index 4857d04418..0000000000 --- a/src/common/utils/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./abort-controller"; -export * from "./autobind"; -export * from "./camelCase"; -export * from "./cluster-id-url-parsing"; -export * from "./collection-functions"; -export * from "./convertCpu"; -export * from "./convertMemory"; -export * from "./debouncePromise"; -export * from "./delay"; -export * from "./disposer"; -export * from "./escapeRegExp"; -export * from "./formatDuration"; -export * from "./getRandId"; -export * from "./hash-set"; -export * from "./n-fircate"; -export * from "./noop"; -export * from "./observable-crate/impl"; -export * from "./promise-exec"; -export * from "./readonly"; -export * from "./reject-promise"; -export * from "./singleton"; -export * from "./sort-compare"; -export * from "./splitArray"; -export * from "./tar"; -export * from "./toJS"; -export * from "./type-narrowing"; -export * from "./types"; -export * from "./wait-for-path"; -export * from "./wait"; - -export type { Tuple } from "./tuple"; - -import * as iter from "./iter"; -import * as array from "./array"; -import * as tuple from "./tuple"; -import * as base64 from "./base64"; -import * as object from "./objects"; -import * as json from "./json"; - -export { - iter, - array, - tuple, - base64, - object, - json, -}; diff --git a/src/common/utils/is-promise/is-promise.test.ts b/src/common/utils/is-promise/is-promise.test.ts deleted file mode 100644 index 565f272ed6..0000000000 --- a/src/common/utils/is-promise/is-promise.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { isPromise } from "./is-promise"; - -describe("isPromise", () => { - it("given promise, returns true", () => { - const actual = isPromise(new Promise(() => {})); - - expect(actual).toBe(true); - }); - - it("given non-promise, returns false", () => { - const actual = isPromise({}); - - expect(actual).toBe(false); - }); - - it("given thenable, returns false", () => { - const actual = isPromise({ then: () => {} }); - - expect(actual).toBe(false); - }); - - it("given nothing, returns false", () => { - const actual = isPromise(undefined); - - expect(actual).toBe(false); - }); -}); diff --git a/src/common/utils/is-promise/is-promise.ts b/src/common/utils/is-promise/is-promise.ts deleted file mode 100644 index 6261f569cd..0000000000 --- a/src/common/utils/is-promise/is-promise.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export function isPromise(reference: any): reference is Promise { - return reference?.constructor === Promise; -} diff --git a/src/common/utils/iter.ts b/src/common/utils/iter.ts deleted file mode 100644 index dc1c4621fd..0000000000 --- a/src/common/utils/iter.ts +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type Falsey = false | 0 | "" | null | undefined; - -interface Iterator extends Iterable { - filter(fn: (val: T) => unknown): Iterator; - filterMap(fn: (val: T) => Falsey | U): Iterator; - find(fn: (val: T) => unknown): T | undefined; - collect(fn: (values: Iterable) => U): U; - map(fn: (val: T) => U): Iterator; - flatMap(fn: (val: T) => U[]): Iterator; - concat(src2: IterableIterator): Iterator; - join(sep?: string): string; -} - -export function chain(src: IterableIterator): Iterator { - return { - filter: (fn) => chain(filter(src, fn)), - filterMap: (fn) => chain(filterMap(src, fn)), - map: (fn) => chain(map(src, fn)), - flatMap: (fn) => chain(flatMap(src, fn)), - find: (fn) => find(src, fn), - join: (sep) => join(src, sep), - collect: (fn) => fn(src), - concat: (src2) => chain(concat(src, src2)), - [Symbol.iterator]: () => src, - }; -} - -/** - * Create a new type safe empty Iterable - * @returns An `Iterable` that yields 0 items - */ -export function* newEmpty(): IterableIterator {} - -/** - * Creates a new `Iterable` that yields at most n items from src. - * Does not modify `src` which can be used later. - * @param src An initial iterator - * @param n The maximum number of elements to take from src. Yields up to the floor of `n` and 0 items if n < 0 - */ -export function* take(src: Iterable, n: number): IterableIterator { - outer: for (let i = 0; i < n; i += 1) { - for (const item of src) { - yield item; - continue outer; - } - - // if we are here that means that `src` has been exhausted. Don't bother trying again. - break outer; - } -} - -/** - * Creates a new iterator that iterates (lazily) over its input and yields the - * result of `fn` for each item. - * @param src A type that can be iterated over - * @param fn The function that is called for each value - */ -export function* map(src: Iterable, fn: (from: T) => U): IterableIterator { - for (const from of src) { - yield fn(from); - } -} - -/** - * The single layer flattening of an iterator, discarding `Falsey` values. - * @param src A type that can be iterated over - * @param fn The function that returns either an iterable over items that should be filtered out or a `Falsey` value indicating that it should be ignored - */ -export function* filterFlatMap(src: Iterable, fn: (from: T) => Iterable | Falsey): IterableIterator { - for (const from of src) { - if (!from) { - continue; - } - - const mapping = fn(from); - - if (!mapping) { - continue; - } - - for (const mapped of mapping) { - if (mapped) { - yield mapped; - } - } - } -} - -/** - * Returns a new iterator that yields the items that each call to `fn` would produce - * @param src A type that can be iterated over - * @param fn A function that returns an iterator - */ -export function* flatMap(src: Iterable, fn: (from: T) => Iterable): IterableIterator { - for (const from of src) { - yield* fn(from); - } -} - -/** - * Creates a new iterator that iterates (lazily) over its input and yields the - * items that return a `truthy` value from `fn`. - * @param src A type that can be iterated over - * @param fn The function that is called for each value - */ -export function* filter(src: Iterable, fn: (from: T) => any): IterableIterator { - for (const from of src) { - if (fn(from)) { - yield from; - } - } -} - -/** - * Creates a new iterator that iterates (lazily) over its input and yields the - * result of `fn` when it is `truthy` - * @param src A type that can be iterated over - * @param fn The function that is called for each value - */ -export function* filterMap(src: Iterable, fn: (from: T) => U | Falsey): IterableIterator { - for (const from of src) { - const res = fn(from); - - if (res) { - yield res; - } - } -} - -/** - * Creates a new iterator that iterates (lazily) over its input and yields the - * result of `fn` when it is not null or undefined - * @param src A type that can be iterated over - * @param fn The function that is called for each value - */ -export function* filterMapStrict(src: Iterable, fn: (from: T) => U | null | undefined): IterableIterator { - for (const from of src) { - const res = fn(from); - - if (res != null) { - yield res; - } - } -} - -/** - * Iterate through `src` until `match` returns a truthy value - * @param src A type that can be iterated over - * @param match A function that should return a truthy value for the item that you want to find - * @returns The first entry that `match` returns a truthy value for, or `undefined` - */ -export function find(src: Iterable, match: (i: T) => any): T | undefined { - for (const from of src) { - if (match(from)) { - return from; - } - } - - return void 0; -} - -/** - * Iterate over `src` calling `reducer` with the previous produced value and the current - * yielded value until `src` is exausted. Then return the final value. - * @param src The value to iterate over - * @param reducer A function for producing the next item from an accumilation and the current item - * @param initial The initial value for the iteration - */ -export function reduce>(src: Iterable, reducer: (acc: R, cur: T) => R, initial: R): R; - -export function reduce(src: Iterable, reducer: (acc: R, cur: T) => R, initial: R): R { - let acc = initial; - - for (const item of src) { - acc = reducer(acc, item); - } - - return acc; -} - -/** - * A convenience function for reducing over an iterator of strings and concatenating them together - * @param src The value to iterate over - * @param connector The string value to intersperse between the yielded values - * @returns The concatenated entries of `src` interspersed with copies of `connector` - */ -export function join(src: IterableIterator, connector = ","): string { - const iterSrc = src[Symbol.iterator](); - const first = iterSrc.next(); - - if (first.done === true) { - return ""; - } - - return reduce(iterSrc, (acc, cur) => `${acc}${connector}${cur}`, `${first.value}`); -} - -/** - * Iterate `n` times and then return the next value. - * @param src The value to iterate over - * @param n The zero-index value for the item to return to. - */ -export function nth(src: Iterable, n: number): T | undefined { - const iterator = src[Symbol.iterator](); - - while (n --> 0) { - iterator.next(); - } - - return iterator.next().value; -} - -/** - * A convenience function to get the first item of an iterator - * @param src The value to iterate over - */ -export function first(src: Iterable): T | undefined { - return nth(src, 0); -} - -/** - * Iterate through `src` and return `true` if `fn` returns a thruthy value for every yielded value. - * Otherwise, return `false`. This function short circuits. - * @param src The type to be iterated over - * @param fn A function to check each iteration - */ -export function every(src: Iterable, fn: (val: T) => any): boolean { - for (const val of src) { - if (!fn(val)) { - return false; - } - } - - return true; -} - -export function* concat(...sources: IterableIterator[]): IterableIterator { - for (const source of sources) { - for (const val of source) { - yield val; - } - } -} diff --git a/src/common/utils/json.ts b/src/common/utils/json.ts deleted file mode 100644 index 53d357f05c..0000000000 --- a/src/common/utils/json.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { JsonValue } from "type-fest"; - -export function parse(input: string): JsonValue { - return JSON.parse(input); -} diff --git a/src/common/utils/n-fircate.ts b/src/common/utils/n-fircate.ts deleted file mode 100644 index 0f195adc10..0000000000 --- a/src/common/utils/n-fircate.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Split an iterable into several arrays with matching fields - * @param from The iterable of items to split up - * @param field The field of each item to split over - * @param parts What each array will be filtered to - * @returns A `parts.length` tuple of `T[]` where each array has matching `field` values - */ -export function nFircate(from: Iterable, field: keyof T, parts: []): []; -export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field]]): [T[]]; -export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field], T[typeof field]]): [T[], T[]]; -export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field], T[typeof field], T[typeof field]]): [T[], T[], T[]]; - -export function nFircate(from: Iterable, field: keyof T, parts: T[typeof field][]): T[][] { - if (new Set(parts).size !== parts.length) { - throw new TypeError("Duplicate parts entries"); - } - - const res = Array.from(parts, () => [] as T[]); - - for (const item of from) { - const index = parts.indexOf(item[field]); - - if (index < 0) { - continue; - } - - res[index].push(item); - } - - return res; -} diff --git a/src/common/utils/noop.ts b/src/common/utils/noop.ts deleted file mode 100644 index a9171f2618..0000000000 --- a/src/common/utils/noop.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -/** - * A function that does nothing - */ -export function noop(...args: T): void { - return void args; -} diff --git a/src/common/utils/objects.ts b/src/common/utils/objects.ts deleted file mode 100644 index 9b6355d3e7..0000000000 --- a/src/common/utils/objects.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * A better typed version of `Object.fromEntries` where the keys are known to - * be a specific subset - */ -export function fromEntries(entries: Iterable): Record { - return Object.fromEntries(entries) as Record; -} - -export function keys(obj: Partial>): K[]; - -export function keys(obj: Record): K[] { - return Object.keys(obj) as K[]; -} - -export function entries(obj: Partial> | null | undefined): [K, V][]; -export function entries(obj: Partial> | null | undefined): [K, V][]; -export function entries(obj: Record | null | undefined): [K, V][]; - -export function entries(obj: Record | null | undefined): [K, V][] { - if (obj && typeof obj == "object") { - return Object.entries(obj) as never; - } - - return [] as never; -} diff --git a/src/common/utils/observable-crate/impl.ts b/src/common/utils/observable-crate/impl.ts deleted file mode 100644 index e9874c9b7d..0000000000 --- a/src/common/utils/observable-crate/impl.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable, runInAction } from "mobx"; -import { getOrInsertMap } from "../collection-functions"; -import { noop } from "../noop"; - -export interface ObservableCrate { - get(): T; - set(value: T): void; -} - -export interface ObservableCrateFactory { - (initialValue: T, transitionHandlers?: ObservableCrateTransitionHandlers): ObservableCrate; -} - -export interface ObservableCrateTransitionHandler { - from: T; - to: T; - onTransition: () => void; -} -export type ObservableCrateTransitionHandlers = ObservableCrateTransitionHandler[]; - -function convertToHandlersMap(handlers: ObservableCrateTransitionHandlers): Map void>> { - const res: ReturnType> = new Map(); - - for (const { from, to, onTransition } of handlers) { - getOrInsertMap(res, from).set(to, onTransition); - } - - return res; -} - -export const observableCrate = ((initialValue, transitionHandlers = []) => { - const crate = observable.box(initialValue); - const handlers = convertToHandlersMap(transitionHandlers); - - return { - get() { - return crate.get(); - }, - set(value) { - const onTransition = handlers.get(crate.get())?.get(value) ?? noop; - - runInAction(() => { - crate.set(value); - onTransition(); - }); - }, - }; -}) as ObservableCrateFactory; diff --git a/src/common/utils/observable-crate/observable-crate.test.ts b/src/common/utils/observable-crate/observable-crate.test.ts deleted file mode 100644 index 03ee2e43f8..0000000000 --- a/src/common/utils/observable-crate/observable-crate.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ObservableCrate } from "./impl"; -import { observableCrate } from "./impl"; - -describe("observable-crate", () => { - it("can be constructed with initial value", () => { - expect(() => observableCrate(0)).not.toThrow(); - }); - - it("has a definite type if the initial value is provided", () => { - expect (() => { - const res: ObservableCrate = observableCrate(0); - - void res; - }).not.toThrow(); - }); - - it("accepts an array of transitionHandlers", () => { - expect(() => observableCrate(0, [])).not.toThrow(); - }); - - describe("with a crate over an enum, and some transition handlers", () => { - enum Test { - Start, - T1, - End, - } - - let crate: ObservableCrate; - let correctHandler: jest.MockedFunction<() => void>; - let incorrectHandler: jest.MockedFunction<() => void>; - - beforeEach(() => { - correctHandler = jest.fn(); - incorrectHandler = jest.fn(); - crate = observableCrate(Test.Start, [ - { - from: Test.Start, - to: Test.Start, - onTransition: incorrectHandler, - }, - { - from: Test.Start, - to: Test.T1, - onTransition: correctHandler, - }, - { - from: Test.Start, - to: Test.End, - onTransition: incorrectHandler, - }, - { - from: Test.T1, - to: Test.Start, - onTransition: incorrectHandler, - }, - { - from: Test.T1, - to: Test.T1, - onTransition: incorrectHandler, - }, - { - from: Test.T1, - to: Test.End, - onTransition: incorrectHandler, - }, - { - from: Test.End, - to: Test.Start, - onTransition: incorrectHandler, - }, - { - from: Test.End, - to: Test.T1, - onTransition: incorrectHandler, - }, - { - from: Test.End, - to: Test.End, - onTransition: incorrectHandler, - }, - ]); - }); - - it("initial value is available", () => { - expect(crate.get()).toBe(Test.Start); - }); - - it("does not call any transition handler", () => { - expect(correctHandler).not.toBeCalled(); - expect(incorrectHandler).not.toBeCalled(); - }); - - describe("when setting a new value", () => { - beforeEach(() => { - crate.set(Test.T1); - }); - - it("calls the associated transition handler", () => { - expect(correctHandler).toBeCalled(); - }); - - it("does not call any other transition handler", () => { - expect(incorrectHandler).not.toBeCalled(); - }); - - it("new value is available", () => { - expect(crate.get()).toBe(Test.T1); - }); - }); - }); -}); diff --git a/src/common/utils/open-link-in-browser.global-override-for-injectable.ts b/src/common/utils/open-link-in-browser.global-override-for-injectable.ts deleted file mode 100644 index 62c1539757..0000000000 --- a/src/common/utils/open-link-in-browser.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import openLinkInBrowserInjectable from "./open-link-in-browser.injectable"; - -export default getGlobalOverride(openLinkInBrowserInjectable, () => async () => {}); diff --git a/src/common/utils/open-link-in-browser.injectable.ts b/src/common/utils/open-link-in-browser.injectable.ts deleted file mode 100644 index eaa91939e9..0000000000 --- a/src/common/utils/open-link-in-browser.injectable.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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 { shell } from "electron"; - -const allowedProtocols = new Set(["http:", "https:"]); - -export type OpenLinkInBrowser = (url: string) => Promise; - -const openLinkInBrowserInjectable = getInjectable({ - id: "open-link-in-browser", - instantiate: (): OpenLinkInBrowser => ( - async (url) => { - const { protocol } = new URL(url); - - if (!allowedProtocols.has(protocol)) { - throw new TypeError("not an http(s) URL"); - } - - await shell.openExternal(url); - } - ), - causesSideEffects: true, -}); - -export default openLinkInBrowserInjectable; diff --git a/src/common/utils/promise-exec.ts b/src/common/utils/promise-exec.ts deleted file mode 100644 index e2471d2611..0000000000 --- a/src/common/utils/promise-exec.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as util from "util"; -import { execFile } from "child_process"; - -export const promiseExecFile = util.promisify(execFile); diff --git a/src/common/utils/random-bytes.global-override-for-injectable.ts b/src/common/utils/random-bytes.global-override-for-injectable.ts deleted file mode 100644 index 6f83a264e4..0000000000 --- a/src/common/utils/random-bytes.global-override-for-injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import randomBytesInjectable from "./random-bytes.injectable"; - -export default getGlobalOverride(randomBytesInjectable, () => async (size) => { - const res = Buffer.alloc(size); - - for (let i = 0; i < size; i += 1) { - res[i] = i; - } - - return res; -}); diff --git a/src/common/utils/random-bytes.injectable.ts b/src/common/utils/random-bytes.injectable.ts deleted file mode 100644 index 9f00961824..0000000000 --- a/src/common/utils/random-bytes.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 { randomBytes } from "crypto"; -import { promisify } from "util"; - -export type RandomBytes = (size: number) => Promise; - -const randomBytesInjectable = getInjectable({ - id: "random-bytes", - instantiate: (): RandomBytes => promisify(randomBytes), - causesSideEffects: true, -}); - -export default randomBytesInjectable; diff --git a/src/common/utils/reactive-now/reactive-now.test.tsx b/src/common/utils/reactive-now/reactive-now.test.tsx deleted file mode 100644 index ab9b185438..0000000000 --- a/src/common/utils/reactive-now/reactive-now.test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { RenderResult } from "@testing-library/react"; -import { render } from "@testing-library/react"; -import type { IComputedValue } from "mobx"; -import { computed, observe } from "mobx"; -import React from "react"; -import { observer } from "mobx-react"; -import { advanceFakeTime, testUsingFakeTime } from "../../test-utils/use-fake-time"; -import { reactiveNow } from "./reactive-now"; - -describe("reactiveNow", () => { - let someComputed: IComputedValue; - - beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - someComputed = computed(() => { - const currentTimestamp = reactiveNow(); - - return currentTimestamp > new Date("2015-10-21T07:28:00Z").getTime(); - }); - }); - - describe("react-context", () => { - let rendered: RenderResult; - - beforeEach(() => { - const TestComponent = observer( - ({ someComputed }: { someComputed: IComputedValue }) => ( -

{someComputed.get() ? "true" : "false"}
- ), - ); - - rendered = render(); - }); - - it("given time passes, works", () => { - advanceFakeTime(1000); - - expect(rendered.container.textContent).toBe("true"); - }); - - it("does not share the state from previous test", () => { - expect(rendered.container.textContent).toBe("false"); - }); - }); - - describe("non-react-context", () => { - let actual: boolean; - - beforeEach(() => { - observe(someComputed, (changed) => { - actual = changed.newValue as boolean; - }, true); - }); - - it("given time passes, works", () => { - advanceFakeTime(1000); - - expect(actual).toBe(true); - }); - - it("does not share the state from previous test", () => { - expect(actual).toBe(false); - }); - }); -}); diff --git a/src/common/utils/reactive-now/reactive-now.ts b/src/common/utils/reactive-now/reactive-now.ts deleted file mode 100644 index febac37010..0000000000 --- a/src/common/utils/reactive-now/reactive-now.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { _isComputingDerivation } from "mobx"; -import type { IResource } from "mobx-utils"; -import { fromResource } from "mobx-utils"; - -// Note: This file is copy-pasted from mobx-utils to fix very specific issue. -// TODO: Remove this file once https://github.com/mobxjs/mobx-utils/issues/306 is fixed. -const tickers: Record> = {}; - -export function reactiveNow(interval?: number | "frame") { - if (interval === void 0) { interval = 1000; } - - if (!_isComputingDerivation()) { - // See #40 - return Date.now(); - } - - // Note: This is the kludge until https://github.com/mobxjs/mobx-utils/issues/306 is fixed - const synchronizationIsEnabled = !process.env.JEST_WORKER_ID; - - if (!tickers[interval] || !synchronizationIsEnabled) { - if (typeof interval === "number") - tickers[interval] = createIntervalTicker(interval); - else - tickers[interval] = createAnimationFrameTicker(); - } - - return tickers[interval].current(); -} - -function createIntervalTicker(interval: number) { - let subscriptionHandle: NodeJS.Timer; - - return fromResource(function (sink) { - sink(Date.now()); - subscriptionHandle = setInterval(function () { return sink(Date.now()); }, interval); - }, function () { - clearInterval(subscriptionHandle); - }, Date.now()); -} - -function createAnimationFrameTicker() { - const frameBasedTicker = fromResource(function (sink) { - sink(Date.now()); - - function scheduleTick() { - window.requestAnimationFrame(function () { - sink(Date.now()); - if (frameBasedTicker.isAlive()) - scheduleTick(); - }); - } - scheduleTick(); - }, function () { }, Date.now()); - - return frameBasedTicker; -} diff --git a/src/common/utils/readableStream.ts b/src/common/utils/readableStream.ts deleted file mode 100644 index 59fb5ae4f4..0000000000 --- a/src/common/utils/readableStream.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { Readable } from "readable-stream"; -import type { TypedArray } from "type-fest"; - -/** - * ReadableWebToNodeStream - * - * Copied from https://github.com/Borewit/readable-web-to-node-stream - * - * Adds read error handler - * - * */ -export class ReadableWebToNodeStream extends Readable { - - public bytesRead = 0; - public released = false; - - /** - * Default web API stream reader - * https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader - */ - private reader: ReadableStreamDefaultReader; - private pendingRead?: Promise>; - - /** - * - * @param stream ReadableStream: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream - */ - constructor(stream: ReadableStream) { - super(); - this.reader = stream.getReader(); - } - - /** - * Implementation of readable._read(size). - * When readable._read() is called, if data is available from the resource, - * the implementation should begin pushing that data into the read queue - * https://nodejs.org/api/stream.html#stream_readable_read_size_1 - */ - public async _read() { - // Should start pushing data into the queue - // Read data from the underlying Web-API-readable-stream - if (this.released) { - this.push(null); // Signal EOF - - return; - } - - try { - this.pendingRead = this.reader.read(); - const data = await this.pendingRead; - - // clear the promise before pushing pushing new data to the queue and allow sequential calls to _read() - delete this.pendingRead; - - if (data.done || this.released) { - this.push(null); // Signal EOF - } else { - this.bytesRead += data.value.length; - this.push(data.value); // Push new data to the queue - } - } catch (error) { - this.push(null); // Signal EOF - } - } - - /** - * If there is no unresolved read call to Web-API ReadableStream immediately returns; - * otherwise will wait until the read is resolved. - */ - public async waitForReadToComplete() { - if (this.pendingRead) { - await this.pendingRead; - } - } - - /** - * Close wrapper - */ - public async close(): Promise { - await this.syncAndRelease(); - } - - private async syncAndRelease() { - this.released = true; - await this.waitForReadToComplete(); - await this.reader.releaseLock(); - } -} diff --git a/src/common/utils/readonly.ts b/src/common/utils/readonly.ts deleted file mode 100644 index b379594d9d..0000000000 --- a/src/common/utils/readonly.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ReadonlyDeep } from "type-fest"; - -export function readonly(src: T): ReadonlyDeep { - return src as ReadonlyDeep; -} diff --git a/src/common/utils/reject-promise.ts b/src/common/utils/reject-promise.ts deleted file mode 100644 index 8212bacd3f..0000000000 --- a/src/common/utils/reject-promise.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { AbortSignal } from "abort-controller"; - -/** - * Creates a new promise that will be rejected when the signal rejects. - * - * Useful for `Promise.race()` applications. - * @param signal The AbortController's signal to reject with - */ -export function rejectPromiseBy(signal: AbortSignal): Promise { - return new Promise((_, reject) => { - signal.addEventListener("abort", reject); - }); -} diff --git a/src/common/utils/resolve-system-proxy/resolve-system-proxy-channel.ts b/src/common/utils/resolve-system-proxy/resolve-system-proxy-channel.ts deleted file mode 100644 index c823a8a8f9..0000000000 --- a/src/common/utils/resolve-system-proxy/resolve-system-proxy-channel.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { RequestChannel } from "../channel/request-channel-listener-injection-token"; - -export type ResolveSystemProxyChannel = RequestChannel; - -export const resolveSystemProxyChannel: ResolveSystemProxyChannel = { - id: "resolve-system-proxy-channel", -}; diff --git a/src/common/utils/resolve-system-proxy/resolve-system-proxy-injection-token.ts b/src/common/utils/resolve-system-proxy/resolve-system-proxy-injection-token.ts deleted file mode 100644 index 616718ea98..0000000000 --- a/src/common/utils/resolve-system-proxy/resolve-system-proxy-injection-token.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export type ResolveSystemProxy = (url: string) => Promise; - -export const resolveSystemProxyInjectionToken = getInjectionToken({ - id: "resolve-system-proxy", -}); diff --git a/src/common/utils/singleton.ts b/src/common/utils/singleton.ts deleted file mode 100644 index 0dea1f7526..0000000000 --- a/src/common/utils/singleton.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface StaticThis { new(...args: R): T } - -/** - * @deprecated This is a form of global shared state - */ -export class Singleton { - private static readonly instances = new WeakMap(); - private static creating = ""; - - constructor() { - if (Singleton.creating.length === 0) { - throw new TypeError("A singleton class must be created by createInstance()"); - } - } - - /** - * Creates the single instance of the child class if one was not already created. - * - * Multiple calls will return the same instance. - * Essentially throwing away the arguments to the subsequent calls. - * - * Note: this is a racy function, if two (or more) calls are racing to call this function - * only the first's arguments will be used. - * @param this Implicit argument that is the child class type - * @param args The constructor arguments for the child class - * @returns An instance of the child class - */ - static createInstance(this: StaticThis, ...args: R): T { - if (!Singleton.instances.has(this)) { - if (Singleton.creating.length > 0) { - throw new TypeError(`Cannot create a second singleton (${this.name}) while creating a first (${Singleton.creating})`); - } - - try { - Singleton.creating = this.name; - Singleton.instances.set(this, new this(...args)); - } finally { - Singleton.creating = ""; - } - } - - return Singleton.instances.get(this) as T; - } - - /** - * Get the instance of the child class that was previously created. - * @param this Implicit argument that is the child class type - * @param strict If false will return `undefined` instead of throwing when an instance doesn't exist. - * Default: `true` - * @returns An instance of the child class - */ - static getInstance(this: StaticThis, strict?: true): T; - static getInstance(this: StaticThis, strict: false): T | undefined; - static getInstance(this: StaticThis, strict = true): T | undefined { - if (!Singleton.instances.has(this) && strict) { - throw new TypeError(`instance of ${this.name} is not created`); - } - - return Singleton.instances.get(this) as (T | undefined); - } - - /** - * Delete the instance of the child class. - * - * Note: this doesn't prevent callers of `getInstance` from storing the result in a global. - * - * There is *no* way in JS or TS to prevent globals like that. - */ - static resetInstance() { - Singleton.instances.delete(this); - } -} - -export default Singleton; diff --git a/src/common/utils/sort-compare.ts b/src/common/utils/sort-compare.ts deleted file mode 100644 index 35d88c01b9..0000000000 --- a/src/common/utils/sort-compare.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import semver, { coerce } from "semver"; - -export enum Ordering { - LESS = -1, - EQUAL = 0, - GREATER = 1, -} - -/** - * This function switches the direction of `ordering` if `direction` is `"desc"` - * @param ordering The original ordering (assumed to be an "asc" ordering) - * @param direction The new desired direction - */ -export function rectifyOrdering(ordering: Ordering, direction: "asc" | "desc"): Ordering { - if (direction === "desc") { - return -ordering; - } - - return ordering; -} - -/** - * An ascending sorting function - * @param left An item from an array - * @param right An item from an array - * @returns The relative ordering in an ascending manner. - * - Less if left < right - * - Equal if left == right - * - Greater if left > right - */ -export function sortCompare(left: T, right: T): Ordering { - if (left < right) { - return Ordering.LESS; - } - - if (left === right) { - return Ordering.EQUAL; - } - - return Ordering.GREATER; -} - -/** - * This function sorts of list of items that have what should be a semver version formated string - * as the field `version` but if it is not loosely coercable to semver falls back to sorting them - * alphanumerically - */ -export function sortBySemverVersion(versioneds: T[]): T[] { - return versioneds - .map(versioned => ({ - __version: coerce(versioned.version, { loose: true }), - raw: versioned, - })) - .sort((left, right) => { - if (left.__version && right.__version) { - return semver.compare(right.__version, left.__version); - } - - if (!left.__version && right.__version) { - return Ordering.GREATER; - } - - if (left.__version && !right.__version) { - return Ordering.LESS; - } - - return sortCompare(left.raw.version, right.raw.version); - }) - .map(({ raw }) => raw); -} diff --git a/src/common/utils/sort-function.ts b/src/common/utils/sort-function.ts deleted file mode 100644 index 4eeab57746..0000000000 --- a/src/common/utils/sort-function.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Get an ordering function based on the function getter - */ -export function byValue(getOrderValue: (src: T) => number): (left: T, right: T) => number { - return (left, right) => { - const leftValue = getOrderValue(left); - const rightValue = getOrderValue(right); - - return leftValue - rightValue; - }; -} diff --git a/src/common/utils/splitArray.ts b/src/common/utils/splitArray.ts deleted file mode 100644 index 142d4a33ff..0000000000 --- a/src/common/utils/splitArray.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * This function splits an array into two sub arrays on the first instance of - * element (from the left). If the array does not contain the element. The - * return value is defined to be `[array, [], false]`. If the element is in - * the array then the return value is `[left, right, true]` where `left` is - * the elements of `array` from `[0, index)` and `right` is `(index, length)` - * @param array the full array to split into two sub-arrays - * @param element the element in the middle of the array - * @returns the left and right sub-arrays which when conjoined with `element` - * is the same as `array`, and `true` - */ -export function splitArray(array: T[], element: T): [T[], T[], boolean] { - const index = array.indexOf(element); - - if (index < 0) { - return [array, [], false]; - } - - return [array.slice(0, index), array.slice(index + 1, array.length), true]; -} - -/** - * Splits an array into two parts based on the outcome of `condition`. If `true` - * the value will be returned as part of the right array. If `false` then part of - * the left array. - * @param src the full array to bifurcate - * @param condition the function to determine which set each is in - */ -export function bifurcateArray(src: T[], condition: (item: T) => boolean): [falses: T[], trues: T[]] { - const res: [T[], T[]] = [[], []]; - - for (const item of src) { - res[+condition(item)].push(item); - } - - return res; -} diff --git a/src/common/utils/sync-box/channel-listener.injectable.ts b/src/common/utils/sync-box/channel-listener.injectable.ts deleted file mode 100644 index a97d95d726..0000000000 --- a/src/common/utils/sync-box/channel-listener.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { syncBoxChannel } from "./channels"; -import { getMessageChannelListenerInjectable } from "../channel/message-channel-listener-injection-token"; -import syncBoxStateInjectable from "./sync-box-state.injectable"; - -const syncBoxChannelListenerInjectable = getMessageChannelListenerInjectable({ - id: "init", - channel: syncBoxChannel, - handler: (di) => ({ id, value }) => di.inject(syncBoxStateInjectable, id).set(value), -}); - -export default syncBoxChannelListenerInjectable; diff --git a/src/common/utils/sync-box/channels.ts b/src/common/utils/sync-box/channels.ts deleted file mode 100644 index 4df0462dc3..0000000000 --- a/src/common/utils/sync-box/channels.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { MessageChannel } from "../channel/message-channel-listener-injection-token"; -import type { RequestChannel } from "../channel/request-channel-listener-injection-token"; - -export type SyncBoxChannel = MessageChannel<{ id: string; value: any }>; - -export const syncBoxChannel: SyncBoxChannel = { - id: "sync-box-channel", -}; - -export type SyncBoxInitialValueChannel = RequestChannel< - void, - { id: string; value: any }[] ->; - -export const syncBoxInitialValueChannel: SyncBoxInitialValueChannel = { - id: "sync-box-initial-value-channel", -}; diff --git a/src/common/utils/sync-box/create-sync-box.injectable.ts b/src/common/utils/sync-box/create-sync-box.injectable.ts deleted file mode 100644 index 4a01fe71a0..0000000000 --- a/src/common/utils/sync-box/create-sync-box.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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 { IObservableValue } from "mobx"; -import { computed } from "mobx"; -import { syncBoxChannel } from "./channels"; -import { sendMessageToChannelInjectionToken } from "../channel/message-to-channel-injection-token"; -import syncBoxStateInjectable from "./sync-box-state.injectable"; -import type { SyncBox } from "./sync-box-injection-token"; -import { toJS } from "../toJS"; - -const createSyncBoxInjectable = getInjectable({ - id: "create-sync-box", - - instantiate: (di) => { - const messageToChannel = di.inject(sendMessageToChannelInjectionToken); - const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id); - - return (id: string, initialValue: Value): SyncBox => { - const state = getSyncBoxState(id) as IObservableValue; - - state.set(initialValue); - - return { - id, - - value: computed(() => toJS(state.get())), - - set: (value) => { - state.set(value); - - messageToChannel(syncBoxChannel, { id, value }); - }, - }; - }; - }, -}); - -export default createSyncBoxInjectable; - diff --git a/src/common/utils/sync-box/handler.injectable.ts b/src/common/utils/sync-box/handler.injectable.ts deleted file mode 100644 index f520585474..0000000000 --- a/src/common/utils/sync-box/handler.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 { MessageChannelHandler } from "../channel/message-channel-listener-injection-token"; -import type { SyncBoxChannel } from "./channels"; -import syncBoxStateInjectable from "./sync-box-state.injectable"; - -const syncBoxChannelHandlerInjectable = getInjectable({ - id: "sync-box-channel-handler", - instantiate: (di): MessageChannelHandler => { - const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id); - - return ({ id, value }) => getSyncBoxState(id)?.set(value); - }, -}); - -export default syncBoxChannelHandlerInjectable; diff --git a/src/common/utils/sync-box/sync-box-injection-token.ts b/src/common/utils/sync-box/sync-box-injection-token.ts deleted file mode 100644 index 8db80243d3..0000000000 --- a/src/common/utils/sync-box/sync-box-injection-token.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -export interface SyncBox { - id: string; - value: IComputedValue; - set: (value: Value) => void; -} - -export const syncBoxInjectionToken = getInjectionToken>({ - id: "sync-box", -}); diff --git a/src/common/utils/sync-box/sync-box-state.injectable.ts b/src/common/utils/sync-box/sync-box-state.injectable.ts deleted file mode 100644 index e695833da4..0000000000 --- a/src/common/utils/sync-box/sync-box-state.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { observable } from "mobx"; - -const syncBoxStateInjectable = getInjectable({ - id: "sync-box-state", - - instantiate: () => observable.box(), - - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, id: string) => id, - }), -}); - -export default syncBoxStateInjectable; diff --git a/src/common/utils/sync-box/sync-box.test.ts b/src/common/utils/sync-box/sync-box.test.ts deleted file mode 100644 index 95f139a8cd..0000000000 --- a/src/common/utils/sync-box/sync-box.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainer } from "@ogre-tools/injectable"; -import { getInjectable } from "@ogre-tools/injectable"; -import { observe, runInAction } from "mobx"; -import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; -import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; -import createSyncBoxInjectable from "./create-sync-box.injectable"; -import type { SyncBox } from "./sync-box-injection-token"; -import { syncBoxInjectionToken } from "./sync-box-injection-token"; - -describe("sync-box", () => { - let applicationBuilder: ApplicationBuilder; - - beforeEach(() => { - applicationBuilder = getApplicationBuilder(); - - applicationBuilder.beforeApplicationStart(mainDi => { - runInAction(() => { - mainDi.register(someInjectable); - }); - }); - - applicationBuilder.beforeWindowStart((windowDi) => { - runInAction(() => { - windowDi.register(someInjectable); - }); - }); - }); - - describe("given application is started, when value is set in main", () => { - let valueInMain: string; - let syncBoxInMain: SyncBox; - - beforeEach(async () => { - await applicationBuilder.startHidden(); - - syncBoxInMain = applicationBuilder.mainDi.inject(someInjectable); - - observe(syncBoxInMain.value, ({ newValue }) => { - valueInMain = newValue as string; - }, true); - - runInAction(() => { - syncBoxInMain.set("some-value-from-main"); - }); - }); - - it("knows value in main", () => { - expect(valueInMain).toBe("some-value-from-main"); - }); - - describe("when window starts", () => { - let valueInRenderer: string; - let syncBoxInRenderer: SyncBox; - let rendererDi: DiContainer; - - beforeEach(async () => { - const applicationWindow = - applicationBuilder.applicationWindow.create("some-window-id"); - - await applicationWindow.start(); - - rendererDi = applicationWindow.di; - - syncBoxInRenderer = rendererDi.inject(someInjectable); - - observe(syncBoxInRenderer.value, ({ newValue }) => { - valueInRenderer = newValue as string; - }, true); - }); - - it("has the value from main", () => { - expect(valueInRenderer).toBe("some-value-from-main"); - }); - - describe("when value is set from renderer", () => { - beforeEach(() => { - runInAction(() => { - syncBoxInRenderer.set("some-value-from-renderer"); - }); - }); - - it("has value in main", () => { - expect(valueInMain).toBe("some-value-from-renderer"); - }); - - it("has value in renderer", () => { - expect(valueInRenderer).toBe("some-value-from-renderer"); - }); - }); - }); - }); - - describe("when application starts with a window", () => { - let valueInRenderer: string; - let valueInMain: string; - let syncBoxInMain: SyncBox; - let syncBoxInRenderer: SyncBox; - - beforeEach(async () => { - await applicationBuilder.render(); - - const applicationWindow = applicationBuilder.applicationWindow.only; - - syncBoxInMain = applicationBuilder.mainDi.inject(someInjectable); - syncBoxInRenderer = applicationWindow.di.inject(someInjectable); - - observe(syncBoxInRenderer.value, ({ newValue }) => { - valueInRenderer = newValue as string; - }, true); - - observe(syncBoxInMain.value, ({ newValue }) => { - valueInMain = newValue as string; - }, true); - }); - - it("knows initial value in main", () => { - expect(valueInMain).toBe("some-initial-value"); - }); - - it("knows initial value in renderer", () => { - expect(valueInRenderer).toBe("some-initial-value"); - }); - - describe("when value is set from main", () => { - beforeEach(() => { - runInAction(() => { - syncBoxInMain.set("some-value-from-main"); - }); - }); - - it("has value in main", () => { - expect(valueInMain).toBe("some-value-from-main"); - }); - - it("has value in renderer", () => { - expect(valueInRenderer).toBe("some-value-from-main"); - }); - - describe("when value is set from renderer", () => { - beforeEach(() => { - runInAction(() => { - syncBoxInRenderer.set("some-value-from-renderer"); - }); - }); - - it("has value in main", () => { - expect(valueInMain).toBe("some-value-from-renderer"); - }); - - it("has value in renderer", () => { - expect(valueInRenderer).toBe("some-value-from-renderer"); - }); - }); - }); - }); -}); - -const someInjectable = getInjectable({ - id: "some-injectable", - - instantiate: (di) => { - const createSyncBox = di.inject(createSyncBoxInjectable); - - return createSyncBox("some-sync-box", "some-initial-value"); - }, - - injectionToken: syncBoxInjectionToken, -}); diff --git a/src/common/utils/tar.ts b/src/common/utils/tar.ts deleted file mode 100644 index 3102098976..0000000000 --- a/src/common/utils/tar.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Helper for working with tarball files (.tar, .tgz) -// Docs: https://github.com/npm/node-tar -import tar from "tar"; -import path from "path"; -import { parse } from "./json"; -import type { JsonValue } from "type-fest"; - -export type ReadFileFromTarOpts = { - tarPath: string; - filePath: string; -} & ( - ParseJson extends true - ? { - parseJson: true; - } - : { - parseJson?: false; - } -); - -export function readFileFromTar(opts: ReadFileFromTarOpts): Promise; -export function readFileFromTar(opts: ReadFileFromTarOpts): Promise; - -export function readFileFromTar({ tarPath, filePath, parseJson = false }: ReadFileFromTarOpts): Promise { - return new Promise((resolve, reject) => { - const fileChunks: Buffer[] = []; - - tar.list({ - file: tarPath, - filter: entryPath => path.normalize(entryPath) === filePath, - sync: true, - onentry(entry) { - entry.on("data", chunk => { - fileChunks.push(chunk); - }); - entry.once("error", err => { - reject(new Error(`reading file has failed ${entry.path}: ${err}`)); - }); - entry.once("end", () => { - const data = Buffer.concat(fileChunks); - const result = parseJson ? parse(data.toString("utf8")) : data; - - resolve(result); - }); - }, - }); - - if (!fileChunks.length) { - reject(new Error("Not found")); - } - }); -} - -export async function listTarEntries(filePath: string): Promise { - const entries: string[] = []; - - await tar.list({ - file: filePath, - onentry: (entry) => { - entries.push(path.normalize(entry.path)); - }, - }); - - return entries; -} diff --git a/src/common/utils/toJS.ts b/src/common/utils/toJS.ts deleted file mode 100644 index 7a949b46fc..0000000000 --- a/src/common/utils/toJS.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Wrapper for mobx.toJS() to support partially observable objects as data-input (>= mobx6). - * Otherwise, output result won't be recursively converted to corresponding plain JS-structure. - * - * @example - * mobx.toJS({one: 1, two: observable.array([2])}); // "data.two" == ObservableArray - */ -import * as mobx from "mobx"; -import { isObservable, observable } from "mobx"; - -export function toJS(data: T): T { - // make data observable for recursive toJS()-output - if (typeof data === "object" && !isObservable(data)) { - return mobx.toJS(observable.box(data).get()); - } - - return mobx.toJS(data); -} diff --git a/src/common/utils/tuple.ts b/src/common/utils/tuple.ts deleted file mode 100644 index 1268678617..0000000000 --- a/src/common/utils/tuple.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as array from "../utils/array"; - -/** - * A strict N-tuple of type T - */ -export type Tuple = N extends N - ? number extends N - ? T[] - : TupleOfImpl - : never; -type TupleOfImpl = R["length"] extends N - ? R - : TupleOfImpl; - -/** - * Iterates over `sources` yielding full tuples until one of the tuple arrays - * is empty. Then it returns a tuple with the rest of each of tuples - * @param sources The source arrays - * @yields A tuple of the next element from each of the sources - * @returns The tuple of all the sources as soon as at least one of the sources is exausted - */ -export function zip(...sources: Tuple): Iterator, Tuple>; -export function zip(...sources: Tuple): Iterator, Tuple>; -export function zip(...sources: Tuple): Iterator, Tuple>; - -export function* zip(...sources: Tuple): Iterator, Tuple> { - const maxSafeLength = Math.min(...sources.map(source => source.length)); - - for (let i = 0; i < maxSafeLength; i += 1) { - yield sources.map(source => source[i]) as Tuple; - } - - return sources.map(source => source.slice(maxSafeLength)) as Tuple; -} - -/** - * Returns a `length` tuple filled with copies of `value` - * @param length The size of the tuple - * @param value The value for each of the tuple entries - */ -export function filled(length: L, value: T): Tuple { - return array.filled(length, value) as Tuple; -} - -/** - * A function for converting an explicit array to a tuple but without the `readonly` typing - */ -export function from(...args: T): [...T] { - return args; -} diff --git a/src/common/utils/type-narrowing.ts b/src/common/utils/type-narrowing.ts deleted file mode 100644 index 68d5265237..0000000000 --- a/src/common/utils/type-narrowing.ts +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ExecException, ExecFileException } from "child_process"; -import type { IncomingMessage } from "http"; - -/** - * Narrows `val` to include the property `key` (if true is returned) - * @param val The object to be tested - * @param key The key to test if it is present on the object (must be a literal for tsc to do any meaningful typing) - */ -export function hasOwnProperty(val: S, key: K): val is (S & { [key in K]: unknown }) { - // this call syntax is for when `val` was created by `Object.create(null)` - return Object.prototype.hasOwnProperty.call(val, key); -} - -/** - * Narrows `val` to a static type that includes fields of names in `keys` - * @param val the value that we are trying to type narrow - * @param keys the key names (must be literals for tsc to do any meaningful typing) - */ -export function hasOwnProperties(val: S, ...keys: K[]): val is (S & { [key in K]: unknown }) { - return keys.every(key => hasOwnProperty(val, key)); -} - -/** - * Narrows `val` to include the property `key` with type `V` - * @param val the value that we are trying to type narrow - * @param key The key to test if it is present on the object (must be a literal for tsc to do any meaningful typing) - * @param isValid a function to check if the field is valid - */ -export function hasTypedProperty(val: S, key: K, isValid: (value: unknown) => value is V): val is (S & { [key in K]: V }) { - return hasOwnProperty(val, key) && isValid(val[key]); -} - -/** - * Narrows `val` to include the property `key` with type string - * @param val the value that we are trying to type narrow - * @param key The key to test if it is present on the object (must be a literal for tsc to do any meaningful typing) - */ -export function hasStringProperty(val: S, key: K): val is (S & { [key in K]: string }) { - return hasOwnProperty(val, key) && isString(val[key]); -} - -/** - * Narrows `val` to include the property `key` with type `V | undefined` or doesn't contain it - * @param val the value that we are trying to type narrow - * @param key The key to test if it is present on the object (must be a literal for tsc to do any meaningful typing) - * @param isValid a function to check if the field (when present) is valid - */ -export function hasOptionalTypedProperty(val: S, key: K, isValid: (value: unknown) => value is V): val is (S & { [key in K]?: V }) { - if (hasOwnProperty(val, key)) { - return typeof val[key] === "undefined" || isValid(val[key]); - } - - return true; -} - -/** - * isRecord checks if `val` matches the signature `Record` or `{ [label in T]: V }` - * @param val The value to be checked - * @param isKey a function for checking if the key is of the correct type - * @param isValue a function for checking if a value is of the correct type - */ -export function isRecord(val: unknown, isKey: (key: unknown) => key is T, isValue: (value: unknown) => value is V): val is Record { - return isObject(val) && Object.entries(val).every(([key, value]) => isKey(key) && isValue(value)); -} - -/** - * isTypedArray checks if `val` is an array and all of its entries are of type `T` - * @param val The value to be checked - * @param isEntry a function for checking if an entry is the correct type - */ -export function isTypedArray(val: unknown, isEntry: (entry: unknown) => entry is T): val is T[] { - return Array.isArray(val) && val.every(isEntry); -} - -/** - * checks if val is of type string - * @param val the value to be checked - */ -export function isString(val: unknown): val is string { - return typeof val === "string"; -} - -/** - * checks if val is of type Buffer - * @param val the value to be checked - */export function isBuffer(val: unknown): val is Buffer { - return val instanceof Buffer; -} - -/** - * checks if val is of type number - * @param val the value to be checked - */ -export function isNumber(val: unknown): val is number { - return typeof val === "number"; -} - -/** - * checks if val is of type boolean - * @param val the value to be checked - */ -export function isBoolean(val: unknown): val is boolean { - return typeof val === "boolean"; -} - -/** - * checks if val is of type object and isn't null - * @param val the value to be checked - */ -export function isObject(val: unknown): val is Record { - return typeof val === "object" && val !== null; -} - -/** - * checks if `val` is defined, useful for filtering out undefined values in a strict manner - */ -export function isDefined(val: T | undefined | null): val is T { - return val != null; -} - -export function isFunction(val: unknown): val is (...args: unknown[]) => unknown { - return typeof val === "function"; -} - -/** - * Checks if the value in the second position is non-nullable - */ -export function hasDefinedTupleValue(pair: readonly [K, V | undefined | null]): pair is [K, V] { - return pair[1] != null; -} - -/** - * Creates a new predicate function (with the same predicate) from `fn`. Such - * that it can be called with just the value to be tested. - * - * This is useful for when using `hasOptionalProperty` and `hasTypedProperty` - * @param fn A typescript user predicate function to be bound - * @param boundArgs the set of arguments to be passed to `fn` in the new function - */ -export function bindPredicate(fn: (arg1: unknown, ...args: FnArgs) => arg1 is T, ...boundArgs: FnArgs): (arg1: unknown) => arg1 is T { - return (arg1: unknown): arg1 is T => fn(arg1, ...boundArgs); -} - -export function hasDefiniteField(field: Field): (val: T) => val is T & { [f in Field]-?: NonNullable } { - return (val): val is T & { [f in Field]-?: NonNullable } => val[field] != null; -} - -export function isPromiseLike(res: unknown): res is (Promise | { then: (fn: (val: unknown) => any) => Promise }) { - if (res instanceof Promise) { - return true; - } - - return isObject(res) - && hasTypedProperty(res, "then", isFunction); -} - -export function isPromiseSettledRejected(result: PromiseSettledResult): result is PromiseRejectedResult { - return result.status === "rejected"; -} - -export function isPromiseSettledFulfilled(result: PromiseSettledResult): result is PromiseFulfilledResult { - return result.status === "fulfilled"; -} - -export function isErrnoException(error: unknown): error is NodeJS.ErrnoException { - return isObject(error) - && hasOptionalTypedProperty(error, "code", isString) - && hasOptionalTypedProperty(error, "path", isString) - && hasOptionalTypedProperty(error, "syscall", isString) - && hasOptionalTypedProperty(error, "errno", isNumber) - && error instanceof Error; -} - -export function isExecException(error: unknown): error is ExecException { - return isObject(error) - && hasOptionalTypedProperty(error, "cmd", isString) - && hasOptionalTypedProperty(error, "killed", isBoolean) - && hasOptionalTypedProperty(error, "signal", isString) - && hasOptionalTypedProperty(error, "code", isNumber) - && error instanceof Error; -} - -export function isExecFileException(error: unknown): error is ExecFileException { - return isExecException(error) && isErrnoException(error); -} - -export type OutputFormat = "string" | "buffer"; -export type ComputeOutputFormat = Format extends "string" - ? string - : Format extends "buffer" - ? Buffer - : string | Buffer; - -export interface ChildProcessExecpetion extends ExecFileException { - stderr: ComputeOutputFormat; - stdout: ComputeOutputFormat; -} - -const isStringOrBuffer = (val: unknown): val is string | Buffer => isString(val) || isBuffer(val); - -export function isChildProcessError(error: unknown, format?: OutputFormat): error is ChildProcessExecpetion { - if (!isExecFileException(error)) { - return false; - } - - if (format === "string") { - return hasTypedProperty(error, "stderr", isString) - && hasTypedProperty(error, "stdout", isString); - } else if (format === "buffer") { - return hasTypedProperty(error, "stderr", isBuffer) - && hasTypedProperty(error, "stdout", isBuffer); - } else { - return hasTypedProperty(error, "stderr", isStringOrBuffer) - && hasTypedProperty(error, "stdout", isStringOrBuffer); - } -} - -export interface RequestLikeError extends Error { - statusCode?: number; - failed?: boolean; - timedOut?: boolean; - error?: string; - response?: IncomingMessage & { body?: any }; -} - -/** - * A type guard for checking if the error is similar in shape to a request package error - */ -export function isRequestError(error: unknown): error is RequestLikeError { - return isObject(error) - && hasOptionalTypedProperty(error, "statusCode", isNumber) - && hasOptionalTypedProperty(error, "failed", isBoolean) - && hasOptionalTypedProperty(error, "timedOut", isBoolean) - && hasOptionalTypedProperty(error, "error", isString) - && hasOptionalTypedProperty(error, "response", isObject) - && error instanceof Error; -} diff --git a/src/common/utils/types.ts b/src/common/utils/types.ts deleted file mode 100644 index 32ebe14e33..0000000000 --- a/src/common/utils/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { SetRequired } from "type-fest"; - -export type RemoveUndefinedFromValues = { - [P in keyof K]: NonNullable; -}; - -/** - * This type helps define which fields of some type will always be defined - */ -export type Defaulted = RemoveUndefinedFromValues>> & Omit; - -export type OptionVarient = { - type: Key; -} & Pick & { - [OtherKey in Exclude]?: undefined; -}; - -export type SingleOrMany = T | T[]; - -export type IfEquals = - (() => G extends T ? 1 : 2) extends - (() => G extends U ? 1 : 2) ? Y : N; - -export type MaybeSetRequired = Query extends true - ? SetRequired - : BaseType; diff --git a/src/common/utils/union-env-path.ts b/src/common/utils/union-env-path.ts deleted file mode 100644 index 991e2c776c..0000000000 --- a/src/common/utils/union-env-path.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import * as iter from "./iter"; - -/** - * Join all entires with a PATH env var delimated string together - * @param PATHs Any number of PATH env variables - * - * NOTE: This function does not attempt to handle any sort of escape sequences since after testing - * it was found that `zsh` (at least on `macOS`) does not when trying to find programs - */ -export function unionPATHs(...PATHs: string[]): string { - const entries = new Set(iter.filterFlatMap(PATHs, PATH => PATH.split(path.delimiter))); - - return iter.join(entries.values(), path.delimiter); -} diff --git a/src/common/utils/wait-for-path.ts b/src/common/utils/wait-for-path.ts deleted file mode 100644 index f5a068075b..0000000000 --- a/src/common/utils/wait-for-path.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { FSWatcher } from "chokidar"; -import path from "path"; - -/** - * Wait for `filePath` and all parent directories to exist. - * @param pathname The file path to wait until it exists - * - * NOTE: There is technically a race condition in this function of the form - * "time-of-check to time-of-use" because we have to wait for each parent - * directory to exist first. - */ -export async function waitForPath(pathname: string): Promise { - const dirOfPath = path.dirname(pathname); - - if (dirOfPath === pathname) { - // The root of this filesystem, assume it exists - return; - } else { - await waitForPath(dirOfPath); - } - - return new Promise((resolve, reject) => { - const watcher = new FSWatcher({ - depth: 0, - disableGlobbing: true, - }); - const onAddOrAddDir = (filePath: string) => { - if (filePath === pathname) { - watcher.unwatch(dirOfPath); - watcher - .close() - .then(() => resolve()) - .catch(reject); - } - }; - const onError = (error: any) => { - watcher.unwatch(dirOfPath); - watcher - .close() - .then(() => reject(error)) - .catch(() => reject(error)); - }; - - watcher - .on("add", onAddOrAddDir) - .on("addDir", onAddOrAddDir) - .on("error", onError) - .add(dirOfPath); - }); -} diff --git a/src/common/utils/wait.ts b/src/common/utils/wait.ts deleted file mode 100644 index 402d556b5d..0000000000 --- a/src/common/utils/wait.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { IComputedValue } from "mobx"; -import { runInAction, when } from "mobx"; -import type { Disposer } from "./disposer"; - -export async function waitUntilDefined(getter: (() => T | null | undefined) | IComputedValue, opts?: { timeout?: number }): Promise { - return new Promise((resolve, reject) => { - when( - () => { - const res = typeof getter === "function" - ? getter() - : getter.get(); - const isDefined = res != null; - - if (isDefined) { - resolve(res); - } - - return isDefined; - }, - () => {}, - { - onError: reject, - ...(opts ?? {}), - }, - ); - }); -} - -export function onceDefined(getter: () => T | null | undefined, action: (val: T) => void): Disposer { - let res: T | null | undefined; - - return when( - () => { - res = getter(); - - if (res != null) { - const r = res; - - runInAction(() => { - action(r); - }); - - return true; - } - - return false; - }, - () => {}, - ); -} diff --git a/src/common/utils/with-concurrency-limit.ts b/src/common/utils/with-concurrency-limit.ts deleted file mode 100644 index 284bb334e1..0000000000 --- a/src/common/utils/with-concurrency-limit.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import plimit from "p-limit"; - -export type ConcurrencyLimiter = (fn: (...args: Args) => Res) => (...args: Args) => Promise; - -export function withConcurrencyLimit(limit: number): ConcurrencyLimiter { - const limiter = plimit(limit); - - return fn => (...args) => limiter(() => fn(...args)); -} diff --git a/src/common/utils/with-error-logging/with-error-logging.injectable.ts b/src/common/utils/with-error-logging/with-error-logging.injectable.ts deleted file mode 100644 index 0aaca9f97d..0000000000 --- a/src/common/utils/with-error-logging/with-error-logging.injectable.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 logErrorInjectable from "../../log-error.injectable"; -import { isPromise } from "../is-promise/is-promise"; - -export type WithErrorLoggingFor = ( - getErrorMessage: (error: unknown) => string -) => any>( - toBeDecorated: T -) => (...args: Parameters) => ReturnType; - -const withErrorLoggingInjectable = getInjectable({ - id: "with-error-logging", - - instantiate: (di): WithErrorLoggingFor => { - const logError = di.inject(logErrorInjectable); - - return (getErrorMessage) => - (toBeDecorated) => - (...args) => { - let returnValue: ReturnType; - - try { - returnValue = toBeDecorated(...args); - } catch (e) { - const errorMessage = getErrorMessage(e); - - logError(errorMessage, e); - - throw e; - } - - if (isPromise(returnValue)) { - return returnValue.catch((e: unknown) => { - const errorMessage = getErrorMessage(e); - - logError(errorMessage, e); - - throw e; - }); - } - - return returnValue; - }; - }, -}); - -export default withErrorLoggingInjectable; diff --git a/src/common/utils/with-error-logging/with-error-logging.test.ts b/src/common/utils/with-error-logging/with-error-logging.test.ts deleted file mode 100644 index b1140d4e54..0000000000 --- a/src/common/utils/with-error-logging/with-error-logging.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; -import withErrorLoggingInjectable from "./with-error-logging.injectable"; -import { pipeline } from "@ogre-tools/fp"; -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { getPromiseStatus } from "../../test-utils/get-promise-status"; -import logErrorInjectable from "../../log-error.injectable"; - -describe("with-error-logging", () => { - describe("given decorated sync function", () => { - let toBeDecorated: jest.Mock; - let decorated: (a: string, b: string) => number | undefined; - let logErrorMock: jest.Mock; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - logErrorMock = jest.fn(); - - di.override(logErrorInjectable, () => logErrorMock); - - const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); - - toBeDecorated = jest.fn(); - - decorated = pipeline( - toBeDecorated, - withErrorLoggingFor((error: any) => `some-error-message-for-${error.message}`), - ); - }); - - describe("when function does not throw and returns value", () => { - let returnValue: number | undefined; - - beforeEach(() => { - // eslint-disable-next-line unused-imports/no-unused-vars-ts - toBeDecorated.mockImplementation((_, __) => 42); - - returnValue = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("does not log error", () => { - expect(logErrorMock).not.toHaveBeenCalled(); - }); - - it("returns the value", () => { - expect(returnValue).toBe(42); - }); - }); - - describe("when function does not throw and returns no value", () => { - let returnValue: number | undefined; - - beforeEach(() => { - // eslint-disable-next-line unused-imports/no-unused-vars-ts - toBeDecorated.mockImplementation((_, __) => undefined); - - returnValue = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("does not log error", () => { - expect(logErrorMock).not.toHaveBeenCalled(); - }); - - it("returns nothing", () => { - expect(returnValue).toBeUndefined(); - }); - }); - - describe("when function throws", () => { - let error: Error; - - beforeEach(() => { - // eslint-disable-next-line unused-imports/no-unused-vars-ts - toBeDecorated.mockImplementation((_, __) => { - throw new Error("some-error"); - }); - - try { - decorated("some-parameter", "some-other-parameter"); - } catch (e: any) { - error = e; - } - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("logs the error", () => { - expect(logErrorMock).toHaveBeenCalledWith("some-error-message-for-some-error", error); - }); - - it("throws", () => { - expect(error.message).toBe("some-error"); - }); - }); - }); - - describe("given decorated async function", () => { - let decorated: (a: string, b: string) => Promise; - let toBeDecorated: AsyncFnMock; - let logErrorMock: jest.Mock; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - logErrorMock = jest.fn(); - - di.override(logErrorInjectable, () => logErrorMock); - - const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); - - toBeDecorated = asyncFn(); - - decorated = pipeline( - toBeDecorated, - - withErrorLoggingFor( - (error: any) => - `some-error-message-for-${error.message || error.someProperty}`, - ), - ); - }); - - describe("when called", () => { - let returnValuePromise: Promise; - - beforeEach(() => { - returnValuePromise = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("does not log error yet", () => { - expect(logErrorMock).not.toHaveBeenCalled(); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(returnValuePromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - describe("when call rejects with error instance", () => { - beforeEach(() => { - toBeDecorated.reject(new Error("some-error")); - }); - - it("logs the error", async () => { - let error: unknown; - - try { - await returnValuePromise; - } catch (e) { - error = e; - } - - expect(logErrorMock).toHaveBeenCalledWith("some-error-message-for-some-error", error); - }); - - it("rejects", () => { - return expect(returnValuePromise).rejects.toThrow("some-error"); - }); - }); - - describe("when call rejects with something else than error instance", () => { - let error: unknown; - - beforeEach(async () => { - toBeDecorated.reject({ someProperty: "some-rejection" }); - - try { - await returnValuePromise; - } catch (e) { - error = e; - } - }); - - it("logs the rejection", () => { - expect(logErrorMock).toHaveBeenCalledWith( - "some-error-message-for-some-rejection", - error, - ); - }); - - it("rejects", () => { - return expect(returnValuePromise).rejects.toEqual({ someProperty: "some-rejection" }); - }); - }); - - describe("when call resolves with value", () => { - beforeEach(async () => { - await toBeDecorated.resolve(42); - }); - - it("does not log error", () => { - expect(logErrorMock).not.toHaveBeenCalled(); - }); - - it("resolves with the value", async () => { - const returnValue = await returnValuePromise; - - expect(returnValue).toBe(42); - }); - }); - - describe("when call resolves without value", () => { - beforeEach(async () => { - await toBeDecorated.resolve(undefined); - }); - - it("does not log error", () => { - expect(logErrorMock).not.toHaveBeenCalled(); - }); - - it("resolves without value", async () => { - const returnValue = await returnValuePromise; - - expect(returnValue).toBeUndefined(); - }); - }); - }); - }); -}); diff --git a/src/common/utils/with-error-suppression/with-error-suppression.test.ts b/src/common/utils/with-error-suppression/with-error-suppression.test.ts deleted file mode 100644 index 12d6a60441..0000000000 --- a/src/common/utils/with-error-suppression/with-error-suppression.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { getPromiseStatus } from "../../test-utils/get-promise-status"; -import { withErrorSuppression } from "./with-error-suppression"; - -describe("with-error-suppression", () => { - describe("given decorated sync function", () => { - let toBeDecorated: jest.Mock; - let decorated: (a: string, b: string) => void; - - beforeEach(() => { - toBeDecorated = jest.fn(); - - decorated = withErrorSuppression(toBeDecorated); - }); - - describe("when function does not throw", () => { - let returnValue: void; - - beforeEach(() => { - returnValue = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("returns nothing", () => { - expect(returnValue).toBeUndefined(); - }); - }); - - describe("when function throws", () => { - let returnValue: void; - - beforeEach(() => { - // eslint-disable-next-line unused-imports/no-unused-vars-ts - toBeDecorated.mockImplementation((_, __) => { - throw new Error("some-error"); - }); - - returnValue = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("returns nothing", () => { - expect(returnValue).toBeUndefined(); - }); - }); - }); - - describe("given decorated async function", () => { - let decorated: (a: string, b: string) => Promise | Promise; - let toBeDecorated: AsyncFnMock<(a: string, b: string) => Promise>; - - beforeEach(() => { - toBeDecorated = asyncFn(); - - decorated = withErrorSuppression(toBeDecorated); - }); - - describe("when called", () => { - let returnValuePromise: Promise | Promise; - - beforeEach(() => { - returnValuePromise = decorated("some-parameter", "some-other-parameter"); - }); - - it("passes arguments to decorated function", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter"); - }); - - it("does not resolve yet", async () => { - const promiseStatus = await getPromiseStatus(returnValuePromise); - - expect(promiseStatus.fulfilled).toBe(false); - }); - - it("when call rejects, resolves with nothing", async () => { - await toBeDecorated.reject(new Error("some-error")); - - const returnValue = await returnValuePromise; - - expect(returnValue).toBeUndefined(); - }); - - it("when call resolves, resolves with the value", async () => { - await toBeDecorated.resolve(42); - - const returnValue = await returnValuePromise; - - expect(returnValue).toBe(42); - }); - }); - }); -}); diff --git a/src/common/utils/with-error-suppression/with-error-suppression.ts b/src/common/utils/with-error-suppression/with-error-suppression.ts deleted file mode 100644 index 657ed13c16..0000000000 --- a/src/common/utils/with-error-suppression/with-error-suppression.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { noop } from "lodash/fp"; - -export function withErrorSuppression Promise>(toBeDecorated: TDecorated): (...args: Parameters) => ReturnType | Promise; -export function withErrorSuppression any>(toBeDecorated: TDecorated): (...args: Parameters) => ReturnType | void; - -export function withErrorSuppression(toBeDecorated: any) { - return (...args: any[]) => { - try { - const returnValue = toBeDecorated(...args); - - if (isPromise(returnValue)) { - return returnValue.catch(noop); - } - - return returnValue; - } catch (e) { - return undefined; - } - }; -} - -function isPromise(reference: any): reference is Promise { - return !!reference?.then; -} diff --git a/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts b/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts deleted file mode 100644 index 42e6cb9a61..0000000000 --- a/src/common/utils/with-orphan-promise/with-orphan-promise.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 withErrorLoggingInjectable from "../with-error-logging/with-error-logging.injectable"; -import { withErrorSuppression } from "../with-error-suppression/with-error-suppression"; -import { pipeline } from "@ogre-tools/fp"; - -const withOrphanPromiseInjectable = getInjectable({ - id: "with-orphan-promise", - - instantiate: (di) => { - const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); - - return Promise>(toBeDecorated: T) => - (...args: Parameters): void => { - const decorated = pipeline( - toBeDecorated, - withErrorLoggingFor(() => "Orphan promise rejection encountered"), - withErrorSuppression, - ); - - decorated(...args); - }; - }, -}); - -export default withOrphanPromiseInjectable; diff --git a/src/common/utils/with-orphan-promise/with-orphan-promise.test.ts b/src/common/utils/with-orphan-promise/with-orphan-promise.test.ts deleted file mode 100644 index 51ebc18e13..0000000000 --- a/src/common/utils/with-orphan-promise/with-orphan-promise.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { AsyncFnMock } from "@async-fn/jest"; -import asyncFn from "@async-fn/jest"; -import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; -import withOrphanPromiseInjectable from "./with-orphan-promise.injectable"; -import logErrorInjectable from "../../log-error.injectable"; - -describe("with orphan promise, when called", () => { - let toBeDecorated: AsyncFnMock<(arg1: string, arg2: string) => Promise>; - let actual: void; - let logErrorMock: jest.Mock; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - logErrorMock = jest.fn(); - - di.override(logErrorInjectable, () => logErrorMock); - - const withOrphanPromise = di.inject(withOrphanPromiseInjectable); - - toBeDecorated = asyncFn(); - - const decorated = withOrphanPromise(toBeDecorated); - - actual = decorated("some-argument", "some-other-argument"); - }); - - it("calls decorated with arguments", () => { - expect(toBeDecorated).toHaveBeenCalledWith("some-argument", "some-other-argument"); - }); - - it("given promise returned by decorated has not been fulfilled yet, already returns nothing", () => { - expect(actual).toBeUndefined(); - }); - - it("when decorated function resolves, nothing happens", async () => { - await toBeDecorated.resolve("irrelevant"); - // Note: there is no expect, test is here only for documentation. - }); - - describe("when decorated function rejects", () => { - beforeEach(async () => { - await toBeDecorated.reject("some-error"); - }); - - it("logs the rejection", () => { - expect(logErrorMock).toHaveBeenCalledWith("Orphan promise rejection encountered", "some-error"); - }); - - it("nothing else happens", () => { - // Note: there is no expect, test is here only for documentation. - }); - }); -}); diff --git a/src/common/vars.ts b/src/common/vars.ts deleted file mode 100644 index 8f53d6354c..0000000000 --- a/src/common/vars.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// App's common configuration for any process (main, renderer, build pipeline, etc.) -import type { ThemeId } from "../renderer/themes/lens-theme"; - -export const publicPath = "/build/" as string; -export const defaultThemeId: ThemeId = "lens-dark"; -export const defaultFontSize = 12; -export const defaultTerminalFontFamily = "RobotoMono"; -export const defaultEditorFontFamily = "RobotoMono"; - -// Apis -export const apiPrefix = "/api"; // local router apis -export const apiKubePrefix = "/api-kube"; // k8s cluster apis - -// Links -export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string; -export const slackUrl = "https://k8slens.dev/slack.html" as string; -export const supportUrl = "https://docs.k8slens.dev/support/" as string; -export const docsUrl = "https://docs.k8slens.dev" as string; diff --git a/src/common/vars/app-name.injectable.ts b/src/common/vars/app-name.injectable.ts deleted file mode 100644 index 4d6d87421c..0000000000 --- a/src/common/vars/app-name.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 isDevelopmentInjectable from "./is-development.injectable"; -import productNameInjectable from "./product-name.injectable"; - -const appNameInjectable = getInjectable({ - id: "app-name", - - instantiate: (di) => { - const isDevelopment = di.inject(isDevelopmentInjectable); - const productName = di.inject(productNameInjectable); - - return `${productName}${isDevelopment ? "Dev" : ""}`; - }, -}); - -export default appNameInjectable; diff --git a/src/common/vars/application-copyright.injectable.ts b/src/common/vars/application-copyright.injectable.ts deleted file mode 100644 index 0233584225..0000000000 --- a/src/common/vars/application-copyright.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const applicationCopyrightInjectable = getInjectable({ - id: "application-copyright", - instantiate: (di) => di.inject(applicationInformationToken).copyright, -}); - -export default applicationCopyrightInjectable; diff --git a/src/common/vars/application-description.injectable.ts b/src/common/vars/application-description.injectable.ts deleted file mode 100644 index 5d63a67aa5..0000000000 --- a/src/common/vars/application-description.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const applicationDescriptionInjectable = getInjectable({ - id: "application-description", - instantiate: (di) => di.inject(applicationInformationToken).description, -}); - -export default applicationDescriptionInjectable; diff --git a/src/common/vars/application-information-injectable.ts b/src/common/vars/application-information-injectable.ts deleted file mode 100644 index a73e311233..0000000000 --- a/src/common/vars/application-information-injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 packageJson from "../../../package.json"; -import applicationInformationToken from "../../common/vars/application-information-token"; - -const applicationInformationInjectable = getInjectable({ - id: "application-information", - injectionToken: applicationInformationToken, - instantiate: () => { - const { version, config, productName, build, copyright, description, name } = packageJson; - - return { version, config, productName, build, copyright, description, name }; - }, - causesSideEffects: true, -}); - -export default applicationInformationInjectable; diff --git a/src/common/vars/application-information-token.ts b/src/common/vars/application-information-token.ts deleted file mode 100644 index dcd56a3146..0000000000 --- a/src/common/vars/application-information-token.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type packageJson from "../../../package.json"; - -export type ApplicationInformation = Pick & { - build: Partial & { publish?: unknown[] }; -}; - -const applicationInformationToken = getInjectionToken({ - id: "application-information-token", -}); - -export default applicationInformationToken; diff --git a/src/common/vars/application-information.global-override-for-injectable.ts b/src/common/vars/application-information.global-override-for-injectable.ts deleted file mode 100644 index e0b886fa65..0000000000 --- a/src/common/vars/application-information.global-override-for-injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import applicationInformationInjectable from "./application-information-injectable"; - -export default getGlobalOverride(applicationInformationInjectable, () => ({ - name: "some-product-name", - productName: "some-product-name", - version: "6.0.0", - build: {} as any, - config: { - k8sProxyVersion: "0.2.1", - bundledKubectlVersion: "1.23.3", - bundledHelmVersion: "3.7.2", - sentryDsn: "", - contentSecurityPolicy: "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", - welcomeRoute: "/welcome", - }, - copyright: "some-copyright-information", - description: "some-descriptive-text", -})); diff --git a/src/common/vars/base-bundled-binaries-dir.injectable.ts b/src/common/vars/base-bundled-binaries-dir.injectable.ts deleted file mode 100644 index 968ccc5c5b..0000000000 --- a/src/common/vars/base-bundled-binaries-dir.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 bundledResourcesDirectoryInjectable from "./bundled-resources-dir.injectable"; -import normalizedPlatformArchitectureInjectable from "./normalized-platform-architecture.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; - -const baseBundledBinariesDirectoryInjectable = getInjectable({ - id: "base-bundled-binaries-directory", - instantiate: (di) => { - const bundledResourcesDirectory = di.inject(bundledResourcesDirectoryInjectable); - const normalizedPlatformArchitecture = di.inject(normalizedPlatformArchitectureInjectable); - const joinPaths = di.inject(joinPathsInjectable); - - return joinPaths( - bundledResourcesDirectory, - normalizedPlatformArchitecture, - ); - }, -}); - -export default baseBundledBinariesDirectoryInjectable; diff --git a/src/common/vars/build-semantic-version.injectable.ts b/src/common/vars/build-semantic-version.injectable.ts deleted file mode 100644 index 2a49327480..0000000000 --- a/src/common/vars/build-semantic-version.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import { SemVer } from "semver"; -import type { InitializableState } from "../initializable-state/create"; -import { createInitializableState } from "../initializable-state/create"; -import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; - -export const buildVersionInjectionToken = getInjectionToken>({ - id: "build-version-token", -}); - -export const buildVersionChannel: RequestChannel = { - id: "build-version", -}; - -const buildSemanticVersionInjectable = createInitializableState({ - id: "build-semantic-version", - init: (di) => { - const buildVersion = di.inject(buildVersionInjectionToken); - - return new SemVer(buildVersion.get()); - }, -}); - -export default buildSemanticVersionInjectable; - diff --git a/src/common/vars/bundled-kubectl-version.injectable.ts b/src/common/vars/bundled-kubectl-version.injectable.ts deleted file mode 100644 index f5817426fb..0000000000 --- a/src/common/vars/bundled-kubectl-version.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const bundledKubectlVersionInjectable = getInjectable({ - id: "bundled-kubectl-version", - instantiate: (di) => di.inject(applicationInformationToken).config.bundledKubectlVersion, -}); - -export default bundledKubectlVersionInjectable; diff --git a/src/common/vars/bundled-resources-dir.injectable.ts b/src/common/vars/bundled-resources-dir.injectable.ts deleted file mode 100644 index 2058b1d5d3..0000000000 --- a/src/common/vars/bundled-resources-dir.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 isProductionInjectable from "./is-production.injectable"; -import normalizedPlatformInjectable from "./normalized-platform.injectable"; -import lensResourcesDirInjectable from "./lens-resources-dir.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; - -const bundledResourcesDirectoryInjectable = getInjectable({ - id: "bundled-resources-directory", - instantiate: (di) => { - const isProduction = di.inject(isProductionInjectable); - const normalizedPlatform = di.inject(normalizedPlatformInjectable); - const joinPaths = di.inject(joinPathsInjectable); - const lensResourcesDir = di.inject(lensResourcesDirInjectable); - - return isProduction - ? lensResourcesDir - : joinPaths(lensResourcesDir, "binaries", "client", normalizedPlatform); - }, -}); - -export default bundledResourcesDirectoryInjectable; diff --git a/src/common/vars/content-security-policy.injectable.ts b/src/common/vars/content-security-policy.injectable.ts deleted file mode 100644 index 01ccee0980..0000000000 --- a/src/common/vars/content-security-policy.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const contentSecurityPolicyInjectable = getInjectable({ - id: "content-security-policy", - instantiate: (di) => di.inject(applicationInformationToken).config.contentSecurityPolicy, -}); - -export default contentSecurityPolicyInjectable; diff --git a/src/common/vars/extension-api-version.global-override-for-injectable.ts b/src/common/vars/extension-api-version.global-override-for-injectable.ts deleted file mode 100644 index 6d473eb1de..0000000000 --- a/src/common/vars/extension-api-version.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import extensionApiVersionInjectable from "./extension-api-version.injectable"; - -export default getGlobalOverride(extensionApiVersionInjectable, () => "6.0.0"); diff --git a/src/common/vars/extension-api-version.injectable.ts b/src/common/vars/extension-api-version.injectable.ts deleted file mode 100644 index d4ddb92f00..0000000000 --- a/src/common/vars/extension-api-version.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 { SemVer } from "semver"; -import packageJson from "../../../package.json"; - -const extensionApiVersionInjectable = getInjectable({ - id: "extension-api-version", - instantiate: () => { - const { major, minor, patch } = new SemVer(packageJson.version); - - return `${major}.${minor}.${patch}`; - }, - causesSideEffects: true, -}); - -export default extensionApiVersionInjectable; diff --git a/src/common/vars/is-debugging.global-override-for-injectable.ts b/src/common/vars/is-debugging.global-override-for-injectable.ts deleted file mode 100644 index 7aa500ff2e..0000000000 --- a/src/common/vars/is-debugging.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import isDebuggingInjectable from "./is-debugging.injectable"; - -export default getGlobalOverride(isDebuggingInjectable, () => false); diff --git a/src/common/vars/is-debugging.injectable.ts b/src/common/vars/is-debugging.injectable.ts deleted file mode 100644 index 079086e628..0000000000 --- a/src/common/vars/is-debugging.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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"; - -const isDebuggingInjectable = getInjectable({ - id: "is-debugging", - instantiate: () => ["true", "1", "yes", "y", "on"].includes((process.env.DEBUG ?? "").toLowerCase()), - causesSideEffects: true, -}); - -export default isDebuggingInjectable; diff --git a/src/common/vars/is-development.injectable.ts b/src/common/vars/is-development.injectable.ts deleted file mode 100644 index af7aeb3b91..0000000000 --- a/src/common/vars/is-development.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 isProductionInjectable from "./is-production.injectable"; - -const isDevelopmentInjectable = getInjectable({ - id: "is-development", - instantiate: (di) => !di.inject(isProductionInjectable), -}); - -export default isDevelopmentInjectable; diff --git a/src/common/vars/is-integration-testing.injectable.ts b/src/common/vars/is-integration-testing.injectable.ts deleted file mode 100644 index 7d6d5ce24e..0000000000 --- a/src/common/vars/is-integration-testing.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 commandLineArgumentsInjectable from "../../main/utils/command-line-arguments.injectable"; - -const isIntegrationTestingInjectable = getInjectable({ - id: "is-integration-testing", - - instantiate: (di) => { - const commandLineArguments = di.inject(commandLineArgumentsInjectable); - - return commandLineArguments.includes("--integration-testing"); - }, -}); - -export default isIntegrationTestingInjectable; diff --git a/src/common/vars/is-linux.injectable.ts b/src/common/vars/is-linux.injectable.ts deleted file mode 100644 index d84165fad5..0000000000 --- a/src/common/vars/is-linux.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 platformInjectable from "./platform.injectable"; - -const isLinuxInjectable = getInjectable({ - id: "is-linux", - - instantiate: (di) => { - const platform = di.inject(platformInjectable); - - return platform === "linux"; - }, -}); - -export default isLinuxInjectable; diff --git a/src/common/vars/is-mac.injectable.ts b/src/common/vars/is-mac.injectable.ts deleted file mode 100644 index 67a6fda286..0000000000 --- a/src/common/vars/is-mac.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 platformInjectable from "./platform.injectable"; - -const isMacInjectable = getInjectable({ - id: "is-mac", - - instantiate: (di) => { - const platform = di.inject(platformInjectable); - - return platform === "darwin"; - }, -}); - -export default isMacInjectable; diff --git a/src/common/vars/is-production.injectable.ts b/src/common/vars/is-production.injectable.ts deleted file mode 100644 index 661cb397d1..0000000000 --- a/src/common/vars/is-production.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 nodeEnvInjectionToken from "./node-env-injection-token"; - -const isProductionInjectable = getInjectable({ - id: "is-production", - - instantiate: (di) => { - const nodeEnv = di.inject(nodeEnvInjectionToken); - - return nodeEnv === "production"; - }, -}); - -export default isProductionInjectable; diff --git a/src/common/vars/is-snap-package.global-override-for-injectable.ts b/src/common/vars/is-snap-package.global-override-for-injectable.ts deleted file mode 100644 index cb3ff0a6e9..0000000000 --- a/src/common/vars/is-snap-package.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import isSnapPackageInjectable from "./is-snap-package.injectable"; - -export default getGlobalOverride(isSnapPackageInjectable, () => false); diff --git a/src/common/vars/is-snap-package.injectable.ts b/src/common/vars/is-snap-package.injectable.ts deleted file mode 100644 index a2c545870b..0000000000 --- a/src/common/vars/is-snap-package.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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"; - -const isSnapPackageInjectable = getInjectable({ - id: "is-snap", - instantiate: () => Boolean(process.env.SNAP), - causesSideEffects: true, -}); - -export default isSnapPackageInjectable; diff --git a/src/common/vars/is-windows.injectable.ts b/src/common/vars/is-windows.injectable.ts deleted file mode 100644 index 8eb78dcb58..0000000000 --- a/src/common/vars/is-windows.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 platformInjectable from "./platform.injectable"; - -const isWindowsInjectable = getInjectable({ - id: "is-windows", - - instantiate: (di) => { - const platform = di.inject(platformInjectable); - - return platform === "win32"; - }, -}); - -export default isWindowsInjectable; diff --git a/src/common/vars/lens-resources-dir.global-override-for-injectable.ts b/src/common/vars/lens-resources-dir.global-override-for-injectable.ts deleted file mode 100644 index 1a72b0ccf7..0000000000 --- a/src/common/vars/lens-resources-dir.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import lensResourcesDirInjectable from "./lens-resources-dir.injectable"; - -export default getGlobalOverride(lensResourcesDirInjectable, () => "/irrelavent-dir-for-lens-resources"); diff --git a/src/common/vars/lens-resources-dir.injectable.ts b/src/common/vars/lens-resources-dir.injectable.ts deleted file mode 100644 index 9aebba19b3..0000000000 --- a/src/common/vars/lens-resources-dir.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 isProductionInjectable from "./is-production.injectable"; - -const lensResourcesDirInjectable = getInjectable({ - id: "lens-resources-dir", - - instantiate: (di) => { - const isProduction = di.inject(isProductionInjectable); - - return isProduction - ? process.resourcesPath - : process.cwd(); - }, - - causesSideEffects: true, -}); - -export default lensResourcesDirInjectable; diff --git a/src/common/vars/node-env-injection-token.ts b/src/common/vars/node-env-injection-token.ts deleted file mode 100644 index 9de463c1cb..0000000000 --- a/src/common/vars/node-env-injection-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; - -const nodeEnvInjectionToken = getInjectionToken({ - id: "node-env-injection-token", -}); - -export default nodeEnvInjectionToken; diff --git a/src/common/vars/normalized-platform-architecture.injectable.ts b/src/common/vars/normalized-platform-architecture.injectable.ts deleted file mode 100644 index 6b98856268..0000000000 --- a/src/common/vars/normalized-platform-architecture.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 processArchInjectable from "./process-arch.injectable"; - -const normalizedPlatformArchitectureInjectable = getInjectable({ - id: "normalized-platform-architecture", - instantiate: (di) => { - const platformArch = di.inject(processArchInjectable); - - switch (platformArch) { - case "arm64": - return "arm64"; - case "x64": - case "amd64": - return "x64"; - case "386": - case "x32": - case "ia32": - return "ia32"; - default: - throw new Error(`arch=${platformArch} is unsupported`); - } - }, -}); - -export default normalizedPlatformArchitectureInjectable; diff --git a/src/common/vars/normalized-platform.injectable.ts b/src/common/vars/normalized-platform.injectable.ts deleted file mode 100644 index ee1bf7fb74..0000000000 --- a/src/common/vars/normalized-platform.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 platformInjectable from "./platform.injectable"; - -export type NormalizedPlatform = "darwin" | "linux" | "windows"; - -const normalizedPlatformInjectable = getInjectable({ - id: "normalized-platform", - - instantiate: (di): NormalizedPlatform => { - const platform = di.inject(platformInjectable); - - switch (platform) { - case "darwin": - return "darwin"; - case "linux": - return "linux"; - case "win32": - return "windows"; - default: - throw new Error(`platform=${platform} is unsupported`); - } - }, -}); - -export default normalizedPlatformInjectable; diff --git a/src/common/vars/platform.global-override-for-injectable.ts b/src/common/vars/platform.global-override-for-injectable.ts deleted file mode 100644 index 4bb06dec5e..0000000000 --- a/src/common/vars/platform.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import platformInjectable from "./platform.injectable"; - -export default getGlobalOverride(platformInjectable, () => "darwin"); diff --git a/src/common/vars/platform.injectable.ts b/src/common/vars/platform.injectable.ts deleted file mode 100644 index 00d6e42aca..0000000000 --- a/src/common/vars/platform.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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"; - -export const allPlatforms = ["win32", "darwin", "linux"] as const; - -const platformInjectable = getInjectable({ - id: "platform", - instantiate: () => process.platform as typeof allPlatforms[number], - causesSideEffects: true, -}); - -export default platformInjectable; diff --git a/src/common/vars/process-arch.global-override-for-injectable.ts b/src/common/vars/process-arch.global-override-for-injectable.ts deleted file mode 100644 index 42d74d4ec8..0000000000 --- a/src/common/vars/process-arch.global-override-for-injectable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getGlobalOverride } from "../test-utils/get-global-override"; -import processArchInjectable from "./process-arch.injectable"; - -export default getGlobalOverride(processArchInjectable, () => "x64"); diff --git a/src/common/vars/process-arch.injectable.ts b/src/common/vars/process-arch.injectable.ts deleted file mode 100644 index 5504855341..0000000000 --- a/src/common/vars/process-arch.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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"; - -const processArchInjectable = getInjectable({ - id: "process-arch", - instantiate: () => process.arch, - causesSideEffects: true, -}); - -export default processArchInjectable; diff --git a/src/common/vars/product-name.injectable.ts b/src/common/vars/product-name.injectable.ts deleted file mode 100644 index 7a5ba73f2d..0000000000 --- a/src/common/vars/product-name.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const productNameInjectable = getInjectable({ - id: "product-name", - instantiate: (di) => di.inject(applicationInformationToken).productName, -}); - -export default productNameInjectable; diff --git a/src/common/vars/release-channel.injectable.ts b/src/common/vars/release-channel.injectable.ts deleted file mode 100644 index 6554fbc0ac..0000000000 --- a/src/common/vars/release-channel.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { createInitializableState } from "../initializable-state/create"; -import buildSemanticVersionInjectable from "./build-semantic-version.injectable"; -import type { ReleaseChannel } from "../../features/application-update/common/update-channels"; - -const releaseChannelInjectable = createInitializableState({ - id: "release-channel", - init: (di): ReleaseChannel => { - const buildSemanticVersion = di.inject(buildSemanticVersionInjectable); - const currentReleaseChannel = buildSemanticVersion.get().prerelease[0]; - - switch (currentReleaseChannel) { - case "latest": - case "beta": - case "alpha": - return currentReleaseChannel; - default: - return "latest"; - } - }, -}); - -export default releaseChannelInjectable; diff --git a/src/common/vars/sentry-dsn-url.injectable.ts b/src/common/vars/sentry-dsn-url.injectable.ts deleted file mode 100644 index 7fd138ab0a..0000000000 --- a/src/common/vars/sentry-dsn-url.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const sentryDataSourceNameInjectable = getInjectable({ - id: "sentry-data-source-name", - instantiate: (di) => di.inject(applicationInformationToken).config.sentryDsn, -}); - -export default sentryDataSourceNameInjectable; diff --git a/src/common/vars/static-files-directory.global-override-for-injectable.ts b/src/common/vars/static-files-directory.global-override-for-injectable.ts deleted file mode 100644 index 3b8ec43046..0000000000 --- a/src/common/vars/static-files-directory.global-override-for-injectable.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getGlobalOverride } from "../test-utils/get-global-override"; -import staticFilesDirectoryInjectable from "./static-files-directory.injectable"; - -export default getGlobalOverride(staticFilesDirectoryInjectable, () => "/some-static-directory"); diff --git a/src/common/vars/static-files-directory.injectable.ts b/src/common/vars/static-files-directory.injectable.ts deleted file mode 100644 index 8ed9967119..0000000000 --- a/src/common/vars/static-files-directory.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 appPathsInjectable from "../app-paths/app-paths.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; - -const staticFilesDirectoryInjectable = getInjectable({ - id: "static-files-directory", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const currentAppDir = di.inject(appPathsInjectable).currentApp; - - return joinPaths(currentAppDir, "static"); - }, -}); - -export default staticFilesDirectoryInjectable; diff --git a/src/common/vars/store-migration-version.injectable.ts b/src/common/vars/store-migration-version.injectable.ts deleted file mode 100644 index eb2b7aa8cc..0000000000 --- a/src/common/vars/store-migration-version.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 applicationInformationToken from "./application-information-token"; - -const storeMigrationVersionInjectable = getInjectable({ - id: "store-migration-version", - instantiate: (di) => di.inject(applicationInformationToken).version, -}); - -export default storeMigrationVersionInjectable; diff --git a/src/common/weblinks-store/migration-token.ts b/src/common/weblinks-store/migration-token.ts deleted file mode 100644 index d1cea9334b..0000000000 --- a/src/common/weblinks-store/migration-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; - -export const weblinkStoreMigrationInjectionToken = getInjectionToken({ - id: "weblink-store-migration-token", -}); diff --git a/src/common/weblinks-store/weblink-store.injectable.ts b/src/common/weblinks-store/weblink-store.injectable.ts deleted file mode 100644 index cf793a2e58..0000000000 --- a/src/common/weblinks-store/weblink-store.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import { weblinkStoreMigrationInjectionToken } from "./migration-token"; -import { WeblinkStore } from "./weblink-store"; - -const weblinkStoreInjectable = getInjectable({ - id: "weblink-store", - instantiate: (di) => new WeblinkStore({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, weblinkStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default weblinkStoreInjectable; diff --git a/src/common/weblinks-store/weblink-store.ts b/src/common/weblinks-store/weblink-store.ts deleted file mode 100644 index 2044ca08c1..0000000000 --- a/src/common/weblinks-store/weblink-store.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, comparer, observable, makeObservable } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import * as uuid from "uuid"; -import { toJS } from "../utils"; - -export interface WeblinkData { - id: string; - name: string; - url: string; -} - -export interface WeblinkCreateOptions { - id?: string; - name: string; - url: string; -} - - -export interface WeblinkStoreModel { - weblinks: WeblinkData[]; -} - -export class WeblinkStore extends BaseStore { - @observable weblinks: WeblinkData[] = []; - - constructor(deps: BaseStoreDependencies) { - super(deps, { - configName: "lens-weblink-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - makeObservable(this); - this.load(); - } - - @action - protected fromStore(data: Partial = {}) { - this.weblinks = data.weblinks || []; - } - - add(data: WeblinkCreateOptions) { - const { - id = uuid.v4(), - name, - url, - } = data; - const weblink: WeblinkData = { id, name, url }; - - this.weblinks.push(weblink); - - return weblink; - } - - @action - removeById(id: string) { - this.weblinks = this.weblinks.filter((w) => w.id !== id); - } - - toJSON(): WeblinkStoreModel { - const model: WeblinkStoreModel = { - weblinks: this.weblinks, - }; - - return toJS(model); - } -} diff --git a/src/extensions/__tests__/extension-loader.test.ts b/src/extensions/__tests__/extension-loader.test.ts deleted file mode 100644 index 1c010f7640..0000000000 --- a/src/extensions/__tests__/extension-loader.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ExtensionLoader } from "../extension-loader"; -import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; -import { runInAction } from "mobx"; -import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable"; -import { delay } from "../../renderer/utils"; -import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; -import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable"; -import type { IpcRenderer } from "electron"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import currentlyInClusterFrameInjectable from "../../renderer/routes/currently-in-cluster-frame.injectable"; - -const manifestPath = "manifest/path"; -const manifestPath2 = "manifest/path2"; -const manifestPath3 = "manifest/path3"; - -describe("ExtensionLoader", () => { - let extensionLoader: ExtensionLoader; - let updateExtensionStateMock: jest.Mock; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - di.override(currentlyInClusterFrameInjectable, () => false); - - di.override(ipcRendererInjectable, () => ({ - invoke: jest.fn(async (channel: string) => { - if (channel === "extension-loader:main:state") { - return [ - [ - manifestPath, - { - manifest: { - name: "TestExtension", - version: "1.0.0", - }, - id: manifestPath, - absolutePath: "/test/1", - manifestPath, - isBundled: false, - isEnabled: true, - }, - ], - [ - manifestPath2, - { - manifest: { - name: "TestExtension2", - version: "2.0.0", - }, - id: manifestPath2, - absolutePath: "/test/2", - manifestPath: manifestPath2, - isBundled: false, - isEnabled: true, - }, - ], - ]; - } - - return []; - }), - - on: (channel: string, listener: (event: any, ...args: any[]) => void) => { - if (channel === "extension-loader:main:state") { - // First initialize with extensions 1 and 2 - // and then broadcast event to remove extension 2 and add extension number 3 - setTimeout(() => { - listener({}, [ - [ - manifestPath, - { - manifest: { - name: "TestExtension", - version: "1.0.0", - }, - id: manifestPath, - absolutePath: "/test/1", - manifestPath, - isBundled: false, - isEnabled: true, - }, - ], - [ - manifestPath3, - { - manifest: { - name: "TestExtension3", - version: "3.0.0", - }, - id: manifestPath3, - absolutePath: "/test/3", - manifestPath: manifestPath3, - isBundled: false, - isEnabled: true, - }, - ], - ]); - }, 10); - } - }, - }) as unknown as IpcRenderer); - - updateExtensionStateMock = jest.fn(); - - di.override(updateExtensionsStateInjectable, () => updateExtensionStateMock); - - extensionLoader = di.inject(extensionLoaderInjectable); - }); - - it("renderer updates extension after ipc broadcast", async () => { - expect(extensionLoader.userExtensions).toMatchInlineSnapshot(`Map {}`); - - await extensionLoader.init(); - await delay(10); - - // Assert the extensions after the extension broadcast event - expect(extensionLoader.userExtensions).toMatchInlineSnapshot(` - Map { - "manifest/path" => Object { - "absolutePath": "/test/1", - "id": "manifest/path", - "isBundled": false, - "isEnabled": true, - "manifest": Object { - "name": "TestExtension", - "version": "1.0.0", - }, - "manifestPath": "manifest/path", - }, - "manifest/path3" => Object { - "absolutePath": "/test/3", - "id": "manifest/path3", - "isBundled": false, - "isEnabled": true, - "manifest": Object { - "name": "TestExtension3", - "version": "3.0.0", - }, - "manifestPath": "manifest/path3", - }, - } - `); - }); - - it("updates ExtensionsStore after isEnabled is changed", async () => { - await extensionLoader.init(); - - expect(updateExtensionStateMock).not.toHaveBeenCalled(); - - runInAction(() => { - extensionLoader.setIsEnabled("manifest/path", false); - }); - - expect(updateExtensionStateMock).toHaveBeenCalledWith( - expect.objectContaining({ - "manifest/path": { - enabled: false, - name: "TestExtension", - }, - - "manifest/path2": { - enabled: true, - name: "TestExtension2", - }, - }), - ); - }); -}); diff --git a/src/extensions/__tests__/is-compatible-extension.test.ts b/src/extensions/__tests__/is-compatible-extension.test.ts deleted file mode 100644 index f2ca144681..0000000000 --- a/src/extensions/__tests__/is-compatible-extension.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { isCompatibleExtension } from "../extension-discovery/is-compatible-extension/is-compatible-extension"; -import type { LensExtensionManifest } from "../lens-extension"; - -describe("Extension/App versions compatibility checks", () => { - it("is compatible with exact version matching", () => { - expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.5.0" })).toBeTruthy(); - }); - - it("is compatible with upper %PATCH versions of base app", () => { - expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.5.5" })).toBeTruthy(); - }); - - it("is compatible with higher %MINOR version of base app", () => { - expect(isCompatible({ extLensEngineVersion: "5.5.0", extensionApiVersion: "5.6.0" })).toBeTruthy(); - }); - - it("is not compatible with higher %MAJOR version of base app", () => { - expect(isCompatible({ extLensEngineVersion: "5.6.0", extensionApiVersion: "6.0.0" })).toBeFalsy(); // extension for lens@5 not compatible with lens@6 - expect(isCompatible({ extLensEngineVersion: "6.0.0", extensionApiVersion: "5.6.0" })).toBeFalsy(); - }); - - it("supports short version format for manifest.engines.lens", () => { - expect(isCompatible({ extLensEngineVersion: "5.5", extensionApiVersion: "5.5.1" })).toBeTruthy(); - }); - - it("throws for incorrect or not supported version format", () => { - expect(() => isCompatible({ - extLensEngineVersion: ">=2.0", - extensionApiVersion: "2.0", - })).toThrow(/Invalid format/i); - - expect(() => isCompatible({ - extLensEngineVersion: "~2.0", - extensionApiVersion: "2.0", - })).toThrow(/Invalid format/i); - - expect(() => isCompatible({ - extLensEngineVersion: "*", - extensionApiVersion: "1.0", - })).toThrow(/Invalid format/i); - }); -}); - -function isCompatible({ extLensEngineVersion = "^1.0", extensionApiVersion = "1.0" } = {}): boolean { - const extensionManifestMock = getExtensionManifestMock(extLensEngineVersion); - - return isCompatibleExtension({ extensionApiVersion })(extensionManifestMock); -} - -function getExtensionManifestMock(lensEngine = "1.0"): LensExtensionManifest { - return { - name: "some-extension", - version: "1.0", - engines: { - lens: lensEngine, - }, - }; -} diff --git a/src/extensions/__tests__/lens-extension.test.ts b/src/extensions/__tests__/lens-extension.test.ts deleted file mode 100644 index 7cb90a548b..0000000000 --- a/src/extensions/__tests__/lens-extension.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { LensExtension } from "../lens-extension"; -import { Console } from "console"; -import { stdout, stderr } from "process"; - -console = new Console(stdout, stderr); - -let ext: LensExtension; - -describe("lens extension", () => { - beforeEach(async () => { - ext = new LensExtension({ - manifest: { - name: "foo-bar", - version: "0.1.1", - engines: { lens: "^5.5.0" }, - }, - id: "/this/is/fake/package.json", - absolutePath: "/absolute/fake/", - manifestPath: "/this/is/fake/package.json", - isBundled: false, - isEnabled: true, - isCompatible: true, - }); - }); - - describe("name", () => { - it("returns name", () => { - expect(ext.name).toBe("foo-bar"); - }); - }); -}); diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api.ts deleted file mode 100644 index e0b4ea3223..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; -import type { Inject } from "@ogre-tools/injectable"; - -export const asLegacyGlobalFunctionForExtensionApi = (( - injectableKey, - instantiationParameter, -) => - (...args: any[]) => { - const injected = getLegacyGlobalDiForExtensionApi().inject( - injectableKey, - instantiationParameter, - ) as unknown as (...args: any[]) => any; - - return injected(...args); - }) as Inject; diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts deleted file mode 100644 index ebfc77f40e..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { - DiContainer, - Injectable } from "@ogre-tools/injectable"; -import { - createContainer, - getInjectable, -} from "@ogre-tools/injectable"; -import { Environments, setLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; -import { asLegacyGlobalObjectForExtensionApiWithModifications } from "./as-legacy-global-object-for-extension-api-with-modifications"; - -describe("asLegacyGlobalObjectForExtensionApiWithModifications", () => { - describe("given legacy global object", () => { - let di: DiContainer; - let someInjectable: Injectable<{ someProperty: string }, unknown, void>; - let actual: { someProperty: string } & { - someModificationProperty: string; - }; - - beforeEach(() => { - di = createContainer("irrelevant"); - - jest.spyOn(di, "inject"); - - setLegacyGlobalDiForExtensionApi(di, Environments.renderer); - - someInjectable = getInjectable({ - id: "some-injectable", - instantiate: () => ({ - someProperty: "some-property-value", - }), - }); - - di.register(someInjectable); - - actual = asLegacyGlobalObjectForExtensionApiWithModifications( - someInjectable, - { someModificationProperty: "some-modification-value" }, - ); - }); - - it("when not accessed, does not inject yet", () => { - expect(di.inject).not.toHaveBeenCalled(); - }); - - describe("when a property of global is accessed, ", () => { - let actualPropertyValue: string; - - beforeEach(() => { - actualPropertyValue = actual.someProperty; - }); - - it("injects the injectable for global", () => { - expect(di.inject).toHaveBeenCalledWith(someInjectable, undefined); - }); - - it("global has property of injectable", () => { - expect(actualPropertyValue).toBe("some-property-value"); - }); - }); - - describe("when a property of modification is accessed, ", () => { - let actualModificationValue: string; - - beforeEach(() => { - actualModificationValue = actual.someModificationProperty; - }); - - it("injects the injectable for global", () => { - expect(di.inject).toHaveBeenCalledWith(someInjectable, undefined); - }); - - it("global has property of modification", () => { - expect(actualModificationValue).toBe("some-modification-value"); - }); - }); - }); -}); diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts deleted file mode 100644 index a5261d2917..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { asLegacyGlobalForExtensionApi } from "./as-legacy-global-object-for-extension-api"; -import type { Injectable } from "@ogre-tools/injectable"; - -/** - * @deprecated use asLegacyGlobalForExtensionApi instead, and use proper implementations instead of "modifications". - */ -export const asLegacyGlobalObjectForExtensionApiWithModifications = < - InjectableInstance extends InjectionTokenInstance & object, - InjectionTokenInstance, - ModificationObject extends object, ->( - injectable: Injectable, - modificationObject: ModificationObject, - ) => - Object.assign( - asLegacyGlobalForExtensionApi(injectable), - modificationObject, - ); diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api.ts deleted file mode 100644 index 81d6f94b20..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Inject } from "@ogre-tools/injectable"; -import { getLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; - -export const asLegacyGlobalForExtensionApi = (( - injectable, - instantiationParameter, -) => - new Proxy( - {}, - - { - apply(target: any, thisArg, argArray) { - const fn = getLegacyGlobalDiForExtensionApi().inject( - injectable, - instantiationParameter, - ) as unknown as (...args: any[]) => any; - - return fn(...argArray); - }, - - get(target, propertyName) { - if (propertyName === "$$typeof") { - return undefined; - } - - const instance: any = getLegacyGlobalDiForExtensionApi().inject( - injectable, - instantiationParameter, - ); - - const propertyValue = instance[propertyName] ?? target[propertyName]; - - if (typeof propertyValue === "function") { - return function (...args: any[]) { - return propertyValue.apply(instance, args); - }; - } - - return propertyValue; - }, - }, - )) as Inject; diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api.ts deleted file mode 100644 index f9016f045b..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; -import { asLegacyGlobalForExtensionApi } from "./as-legacy-global-object-for-extension-api"; -import { getLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; -import loggerInjectable from "../../common/logger.injectable"; - -export interface LegacySingleton { - createInstance(): T; - getInstance(): T; - resetInstance(): void; -} - -export function asLegacyGlobalSingletonForExtensionApi(injectable: Injectable): LegacySingleton; -export function asLegacyGlobalSingletonForExtensionApi(injectable: Injectable, param: InstantiationParameter): LegacySingleton; - -export function asLegacyGlobalSingletonForExtensionApi( - injectable: Injectable, - instantiationParameter?: InstantiationParameter, -): LegacySingleton { - const instance = asLegacyGlobalForExtensionApi( - injectable as never, - instantiationParameter, - ) as Instance; - - return { - createInstance: () => instance, - - getInstance: () => instance, - - resetInstance: () => { - const di = getLegacyGlobalDiForExtensionApi(); - const logger = di.inject(loggerInjectable); - - logger.warn( - `resetInstance() for a legacy global singleton of "${injectable.id}" does nothing.`, - ); - }, - }; -} diff --git a/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts deleted file mode 100644 index 4d7d9ca8f5..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { DiContainer } from "@ogre-tools/injectable"; - -const legacyGlobalDis = new Map(); - -export enum Environments { - renderer, - main, -} - -export const setLegacyGlobalDiForExtensionApi = ( - di: DiContainer, - environment: Environments, -) => { - legacyGlobalDis.set(environment, di); -}; - -export const getLegacyGlobalDiForExtensionApi = () => { - if (legacyGlobalDis.size > 1) { - throw new Error("Tried to get DI container using legacy globals where there is multiple containers available."); - } - - const [di] = [...legacyGlobalDis.values()]; - - if (!di) { - throw new Error("Tried to get DI container using legacy globals where there is no containers available."); - } - - return di; -}; - -export function getEnvironmentSpecificLegacyGlobalDiForExtensionApi(environment: Environments) { - const di = legacyGlobalDis.get(environment); - - if (!di) { - throw new Error("Tried to get DI container using legacy globals in environment which doesn't exist"); - } - - return di; -} diff --git a/src/extensions/common-api/app.ts b/src/extensions/common-api/app.ts deleted file mode 100644 index 92ecd19ef9..0000000000 --- a/src/extensions/common-api/app.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import appNameInjectable from "../../common/vars/app-name.injectable"; -import isLinuxInjectable from "../../common/vars/is-linux.injectable"; -import isMacInjectable from "../../common/vars/is-mac.injectable"; -import isSnapPackageInjectable from "../../common/vars/is-snap-package.injectable"; -import isWindowsInjectable from "../../common/vars/is-windows.injectable"; -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import getEnabledExtensionsInjectable from "./get-enabled-extensions/get-enabled-extensions.injectable"; -import type { UserPreferenceExtensionItems } from "./user-preferences"; -import { Preferences } from "./user-preferences"; -import { slackUrl, issuesTrackerUrl } from "../../common/vars"; -import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable"; - -export interface AppExtensionItems { - readonly Preferences: UserPreferenceExtensionItems; - readonly version: string; - readonly appName: string; - readonly slackUrl: string; - readonly issuesTrackerUrl: string; - readonly isSnap: boolean; - readonly isWindows: boolean; - readonly isMac: boolean; - readonly isLinux: boolean; - getEnabledExtensions: () => string[]; -} - -export const App: AppExtensionItems = { - Preferences, - getEnabledExtensions: asLegacyGlobalFunctionForExtensionApi(getEnabledExtensionsInjectable), - get version() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(buildVersionInjectionToken).get(); - }, - get appName() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(appNameInjectable); - }, - get isSnap() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(isSnapPackageInjectable); - }, - get isWindows() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(isWindowsInjectable); - }, - get isMac() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(isMacInjectable); - }, - get isLinux() { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(isLinuxInjectable); - }, - slackUrl, - issuesTrackerUrl, -}; diff --git a/src/extensions/common-api/catalog.ts b/src/extensions/common-api/catalog.ts deleted file mode 100644 index b31059d2c1..0000000000 --- a/src/extensions/common-api/catalog.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import kubernetesClusterCategoryInjectable from "../../common/catalog/categories/kubernetes-cluster.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; - -export { - KubernetesCluster, - GeneralEntity, - WebLink, -} from "../../common/catalog-entities"; - -export const kubernetesClusterCategory = asLegacyGlobalForExtensionApi(kubernetesClusterCategoryInjectable); - -export type { - KubernetesClusterPrometheusMetrics, - KubernetesClusterSpec, - KubernetesClusterMetadata, - WebLinkSpec, - WebLinkStatus, - WebLinkStatusPhase, - KubernetesClusterStatusPhase, - KubernetesClusterStatus, -} from "../../common/catalog-entities"; - -export * from "../../common/catalog/catalog-entity"; diff --git a/src/extensions/common-api/event-bus.ts b/src/extensions/common-api/event-bus.ts deleted file mode 100644 index d95e3f49d4..0000000000 --- a/src/extensions/common-api/event-bus.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import appEventBusInjectable from "../../common/app-event-bus/app-event-bus.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; - -export type { AppEvent } from "../../common/app-event-bus/event-bus"; - -export const appEventBus = asLegacyGlobalForExtensionApi(appEventBusInjectable); diff --git a/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts b/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts deleted file mode 100644 index 747dfa4f42..0000000000 --- a/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable"; - -const getEnabledExtensionsInjectable = getInjectable({ - id: "get-enabled-extensions", - - instantiate: (di) => () => - di.inject(extensionsStoreInjectable).enabledExtensions, -}); - -export default getEnabledExtensionsInjectable; diff --git a/src/extensions/common-api/index.ts b/src/extensions/common-api/index.ts deleted file mode 100644 index 01e41ca11c..0000000000 --- a/src/extensions/common-api/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// APIs -import { App } from "./app"; -import * as EventBus from "./event-bus"; -import * as Store from "./stores"; -import { Util } from "./utils"; -import * as Catalog from "./catalog"; -import * as Types from "./types"; -import * as Proxy from "./proxy"; -import loggerInjectable from "../../common/logger.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; - -const logger = asLegacyGlobalForExtensionApi(loggerInjectable); - -export { - App, - EventBus, - Catalog, - Store, - Types, - Util, - logger, - Proxy, -}; diff --git a/src/extensions/common-api/k8s-api.ts b/src/extensions/common-api/k8s-api.ts deleted file mode 100644 index d4b0ab673f..0000000000 --- a/src/extensions/common-api/k8s-api.ts +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// NOTE: this file is not currently exported as part of `Common`, but should be. -// It is here to consolidate the common parts which are exported to `Main` -// and to `Renderer` - -import apiManagerInjectable from "../../common/k8s-api/api-manager/manager.injectable"; -import createKubeApiForClusterInjectable from "../../common/k8s-api/create-kube-api-for-cluster.injectable"; -import createKubeApiForRemoteClusterInjectable from "../../common/k8s-api/create-kube-api-for-remote-cluster.injectable"; -import createResourceStackInjectable from "../../common/k8s/create-resource-stack.injectable"; -import type { ResourceApplyingStack } from "../../common/k8s/resource-stack"; -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import type { KubernetesCluster } from "./catalog"; -import type { KubeApiDataFrom, KubeObjectStoreOptions } from "../../common/k8s-api/kube-object.store"; -import { KubeObjectStore as InternalKubeObjectStore } from "../../common/k8s-api/kube-object.store"; -import type { KubeJsonApiDataFor, KubeObject } from "../../common/k8s-api/kube-object"; -import type { DerivedKubeApiOptions, KubeApiDependencies, KubeApiOptions } from "../../common/k8s-api/kube-api"; -import { KubeApi as InternalKubeApi } from "../../common/k8s-api/kube-api"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../renderer/cluster-frame-context/for-namespaced-resources.injectable"; -import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context"; -import loggerInjectable from "../../common/logger.injectable"; -import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import maybeKubeApiInjectable from "../../common/k8s-api/maybe-kube-api.injectable"; -import { DeploymentApi as InternalDeploymentApi, IngressApi as InternalIngressApi, NodeApi, PersistentVolumeClaimApi, PodApi } from "../../common/k8s-api/endpoints"; -import { storesAndApisCanBeCreatedInjectionToken } from "../../common/k8s-api/stores-apis-can-be-created.token"; -import type { JsonApiConfig } from "../../common/k8s-api/json-api"; -import type { KubeJsonApi as InternalKubeJsonApi } from "../../common/k8s-api/kube-json-api"; -import createKubeJsonApiInjectable from "../../common/k8s-api/create-kube-json-api.injectable"; -import type { RequestInit } from "node-fetch"; -import createKubeJsonApiForClusterInjectable from "../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; - -export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable); -export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable); -export const forRemoteCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForRemoteClusterInjectable); -export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable); - -const getKubeApiDeps = (): KubeApiDependencies => { - const di = getLegacyGlobalDiForExtensionApi(); - - return { - logger: di.inject(loggerInjectable), - maybeKubeApi: di.inject(maybeKubeApiInjectable), - }; -}; - -// NOTE: this is done to preserve `instanceOf` behaviour -function KubeApiCstr< - Object extends KubeObject = KubeObject, - Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, ->(opts: KubeApiOptions) { - const api = new InternalKubeApi(getKubeApiDeps(), opts); - - const di = getLegacyGlobalDiForExtensionApi(); - const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken); - - if (storesAndApisCanBeCreated) { - apiManager.registerApi(api); - } - - return api; -} - -export type KubeApi< - Object extends KubeObject = KubeObject, - Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, -> = InternalKubeApi; - -export const KubeApi = KubeApiCstr as unknown as new< - Object extends KubeObject = KubeObject, - Data extends KubeJsonApiDataFor = KubeJsonApiDataFor, ->(opts: KubeApiOptions) => InternalKubeApi; - -/** - * @deprecated Switch to using `Common.createResourceStack` instead - */ -export class ResourceStack implements ResourceApplyingStack { - private readonly impl: ResourceApplyingStack; - - constructor(cluster: KubernetesCluster, name: string) { - this.impl = createResourceStack(cluster, name); - } - - kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[] | undefined): Promise { - return this.impl.kubectlApplyFolder(folderPath, templateContext, extraArgs); - } - - kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[] | undefined): Promise { - return this.impl.kubectlDeleteFolder(folderPath, templateContext, extraArgs); - } -} - -/** - * @deprecated This type is unused - */ -export interface IKubeApiCluster { - metadata: { - uid: string; - }; -} - -export type { CreateKubeApiForRemoteClusterConfig as IRemoteKubeApiConfig } from "../../common/k8s-api/create-kube-api-for-remote-cluster.injectable"; -export type { CreateKubeApiForLocalClusterConfig as ILocalKubeApiConfig } from "../../common/k8s-api/create-kube-api-for-cluster.injectable"; - -export { - KubeObject, - KubeStatus, - type OwnerReference, - type KubeObjectMetadata, - type NamespaceScopedMetadata, - type ClusterScopedMetadata, - type BaseKubeJsonApiObjectMetadata, - type KubeJsonApiObjectMetadata, - type KubeStatusData, -} from "../../common/k8s-api/kube-object"; - -export type { - KubeJsonApiData, -} from "../../common/k8s-api/kube-json-api"; - -function KubeJsonApiCstr(config: JsonApiConfig, reqInit?: RequestInit) { - const di = getLegacyGlobalDiForExtensionApi(); - const createKubeJsonApi = di.inject(createKubeJsonApiInjectable); - - return createKubeJsonApi(config, reqInit); -} - -export type KubeJsonApi = InternalKubeJsonApi; - -export const KubeJsonApi = Object.assign( - KubeJsonApiCstr as unknown as new (config: JsonApiConfig, reqInit?: RequestInit) => InternalKubeJsonApi, - { - forCluster: asLegacyGlobalForExtensionApi(createKubeJsonApiForClusterInjectable), - }, -); - -export abstract class KubeObjectStore< - K extends KubeObject = KubeObject, - A extends InternalKubeApi = InternalKubeApi>, - D extends KubeJsonApiDataFor = KubeApiDataFrom, -> extends InternalKubeObjectStore { - /** - * @deprecated This is no longer used and shouldn't have been every really used - */ - static readonly context = { - set: (ctx: ClusterContext) => { - console.warn("Setting KubeObjectStore.context is no longer supported"); - void ctx; - }, - get: () => asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable), - }; - - get context() { - return this.dependencies.context; - } - - constructor(api: A, opts?: KubeObjectStoreOptions); - /** - * @deprecated Supply API instance through constructor - */ - constructor(); - constructor(api?: A, opts?: KubeObjectStoreOptions) { - const di = getLegacyGlobalDiForExtensionApi(); - - super( - { - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - api!, - opts, - ); - } -} - -export { - type JsonPatch, - type KubeObjectStoreLoadAllParams, - type KubeObjectStoreLoadingParams, - type KubeObjectStoreSubscribeParams, -} from "../../common/k8s-api/kube-object.store"; - -/** - * @deprecated This type is only present for backwards compatable typescript support - */ -export interface IgnoredKubeApiOptions { - /** - * @deprecated this option is overridden and should not be used - */ - objectConstructor?: any; - /** - * @deprecated this option is overridden and should not be used - */ - kind?: any; - /** - * @deprecated this option is overridden and should not be used - */ - isNamespaces?: any; - /** - * @deprecated this option is overridden and should not be used - */ - apiBase?: any; -} - -// NOTE: these *Constructor functions MUST be `function` to work with `new X()` -function PodsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { - return new PodApi(getKubeApiDeps(), opts); -} - -export type PodsApi = PodApi; -export const PodsApi = PodsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PodApi; - -function NodesApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { - return new NodeApi(getKubeApiDeps(), opts); -} - -export type NodesApi = NodeApi; -export const NodesApi = NodesApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => NodeApi; - -function DeploymentApiConstructor(opts?: DerivedKubeApiOptions) { - return new InternalDeploymentApi(getKubeApiDeps(), opts); -} - -export type DeploymentApi = InternalDeploymentApi; -export const DeploymentApi = DeploymentApiConstructor as unknown as new (opts?: DerivedKubeApiOptions) => InternalDeploymentApi; - -function IngressApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { - return new InternalIngressApi(getKubeApiDeps(), opts); -} - -export type IngressApi = InternalIngressApi; -export const IngressApi = IngressApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => InternalIngressApi; - -function PersistentVolumeClaimsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) { - return new PersistentVolumeClaimApi(getKubeApiDeps(), opts); -} - -export type PersistentVolumeClaimsApi = PersistentVolumeClaimApi; -export const PersistentVolumeClaimsApi = PersistentVolumeClaimsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PersistentVolumeClaimApi; - -export { - type Container as IPodContainer, - type PodContainerStatus as IPodContainerStatus, - Pod, - Node, - Deployment, - DaemonSet, - StatefulSet, - Job, - CronJob, - ConfigMap, - type SecretReference as ISecretRef, - Secret, - ReplicaSet, - ResourceQuota, - LimitRange, - HorizontalPodAutoscaler, - PodDisruptionBudget, - PriorityClass, - Service, - Endpoints as Endpoint, - Ingress, - NetworkPolicy, - PersistentVolume, - PersistentVolumeClaim, - StorageClass, - Namespace, - KubeEvent, - ServiceAccount, - Role, - RoleBinding, - ClusterRole, - ClusterRoleBinding, - CustomResourceDefinition, -} from "../../common/k8s-api/endpoints"; diff --git a/src/extensions/common-api/proxy.ts b/src/extensions/common-api/proxy.ts deleted file mode 100644 index bf0cd8e626..0000000000 --- a/src/extensions/common-api/proxy.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import { resolveSystemProxyInjectionToken } from "../../common/utils/resolve-system-proxy/resolve-system-proxy-injection-token"; - -/** - * Resolves URL-specific proxy information from system. See more here: https://www.electronjs.org/docs/latest/api/session#sesresolveproxyurl - * @param url - The URL for proxy information - * @returns Promise for proxy information as string - */ -export const resolveSystemProxy = asLegacyGlobalFunctionForExtensionApi(resolveSystemProxyInjectionToken); diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts deleted file mode 100644 index 3592d7950e..0000000000 --- a/src/extensions/common-api/registrations.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export type { StatusBarRegistration } from "../../renderer/components/status-bar/status-bar-registration"; -export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/kube-object-menu-registration"; -export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-registration"; -export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../../renderer/components/kube-object-details/kube-object-detail-registration"; -export type { KubeObjectStatusRegistration } from "../../renderer/components/kube-object-status-icon/kube-object-status-registration"; -export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../../renderer/routes/page-registration"; -export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../../renderer/components/layout/cluster-page-menu"; -export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../../common/protocol-handler/registration"; -export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCategoryViewRegistration } from "../../renderer/components/+catalog/custom-views"; -export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; -export type { KubeObjectContextMenuItem, KubeObjectOnContextMenuOpenContext, KubeObjectOnContextMenuOpen, KubeObjectHandlers, KubeObjectHandlerRegistration } from "../../renderer/kube-object/handler"; -export type { TrayMenuRegistration } from "../../main/tray/tray-menu-registration"; -export type { MenuRegistration } from "../../features/application-menu/main/menu-registration"; diff --git a/src/extensions/common-api/stores.ts b/src/extensions/common-api/stores.ts deleted file mode 100644 index b369579d70..0000000000 --- a/src/extensions/common-api/stores.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export { ExtensionStore } from "../extension-store"; diff --git a/src/extensions/common-api/types.ts b/src/extensions/common-api/types.ts deleted file mode 100644 index febb2c4e56..0000000000 --- a/src/extensions/common-api/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export type IpcMainInvokeEvent = Electron.IpcMainInvokeEvent; -export type IpcRendererEvent = Electron.IpcRendererEvent; -export type IpcMainEvent = Electron.IpcMainEvent; - -export * from "./registrations"; diff --git a/src/extensions/common-api/user-preferences.ts b/src/extensions/common-api/user-preferences.ts deleted file mode 100644 index ed925bd05d..0000000000 --- a/src/extensions/common-api/user-preferences.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -export interface UserPreferenceExtensionItems { - /** - * Get the configured kubectl binaries path. - */ - getKubectlPath: () => string | undefined; -} - -const userStore = asLegacyGlobalForExtensionApi(userStoreInjectable); - -export const Preferences: UserPreferenceExtensionItems = { - getKubectlPath: () => userStore.kubectlBinariesPath, -}; diff --git a/src/extensions/common-api/utils.ts b/src/extensions/common-api/utils.ts deleted file mode 100644 index a6dfdee447..0000000000 --- a/src/extensions/common-api/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser.injectable"; -import buildVersionInjectable from "../../main/vars/build-version/build-version.injectable"; -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import { Singleton } from "../../common/utils"; -import { prevDefault, stopPropagation } from "../../renderer/utils/prevDefault"; -import type { IClassName } from "../../renderer/utils/cssNames"; -import { cssNames } from "../../renderer/utils/cssNames"; - -export interface UtilsExtensionItems { - Singleton: typeof Singleton; - prevDefault: (callback: (evt: E) => R) => (evt: E) => R; - stopPropagation: (evt: Event | React.SyntheticEvent) => void; - cssNames: (...classNames: IClassName[]) => string; - openExternal: (url: string) => Promise; - openBrowser: (url: string) => Promise; - getAppVersion: () => string; -} - -export const Util: UtilsExtensionItems = { - Singleton, - prevDefault, - stopPropagation, - cssNames, - openExternal: asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable), - openBrowser: asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable), - getAppVersion: () => { - const di = getLegacyGlobalDiForExtensionApi(); - - return di.inject(buildVersionInjectable).get(); - }, -}; diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts deleted file mode 100644 index b5165851dc..0000000000 --- a/src/extensions/extension-api.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Extensions-api types bundle (main + renderer) -// Available for lens-extensions via NPM-package "@k8slens/extensions" - -import * as Common from "./common-api"; -import * as Renderer from "./renderer-api"; -import * as Main from "./main-api"; - -export { - Common, - Renderer, - Main, -}; diff --git a/src/extensions/extension-discovery/bundled-extension-token.ts b/src/extensions/extension-discovery/bundled-extension-token.ts deleted file mode 100644 index 1a1a40f9fa..0000000000 --- a/src/extensions/extension-discovery/bundled-extension-token.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { LensExtensionConstructor, LensExtensionManifest } from "../lens-extension"; - -export interface BundledExtension { - readonly manifest: LensExtensionManifest; - main: () => LensExtensionConstructor | null; - renderer: () => LensExtensionConstructor | null; -} - -export const bundledExtensionInjectionToken = getInjectionToken({ - id: "bundled-extension-path", -}); diff --git a/src/extensions/extension-discovery/extension-discovery.injectable.ts b/src/extensions/extension-discovery/extension-discovery.injectable.ts deleted file mode 100644 index 378f519bb7..0000000000 --- a/src/extensions/extension-discovery/extension-discovery.injectable.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 { ExtensionDiscovery } from "./extension-discovery"; -import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; -import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable"; -import extensionsStoreInjectable from "../extensions-store/extensions-store.injectable"; -import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable"; -import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable"; -import extensionPackageRootDirectoryInjectable from "../extension-installer/extension-package-root-directory/extension-package-root-directory.injectable"; -import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import pathExistsInjectable from "../../common/fs/path-exists.injectable"; -import watchInjectable from "../../common/fs/watch/watch.injectable"; -import accessPathInjectable from "../../common/fs/access-path.injectable"; -import copyInjectable from "../../common/fs/copy.injectable"; -import ensureDirInjectable from "../../common/fs/ensure-dir.injectable"; -import isProductionInjectable from "../../common/vars/is-production.injectable"; -import lstatInjectable from "../../common/fs/lstat.injectable"; -import readDirectoryInjectable from "../../common/fs/read-directory.injectable"; -import fileSystemSeparatorInjectable from "../../common/path/separator.injectable"; -import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; -import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; -import getRelativePathInjectable from "../../common/path/get-relative-path.injectable"; -import joinPathsInjectable from "../../common/path/join-paths.injectable"; -import removePathInjectable from "../../common/fs/remove.injectable"; -import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable"; -import lensResourcesDirInjectable from "../../common/vars/lens-resources-dir.injectable"; - -const extensionDiscoveryInjectable = getInjectable({ - id: "extension-discovery", - - instantiate: (di) => new ExtensionDiscovery({ - extensionLoader: di.inject(extensionLoaderInjectable), - extensionsStore: di.inject(extensionsStoreInjectable), - extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), - isCompatibleExtension: di.inject(isCompatibleExtensionInjectable), - installExtension: di.inject(installExtensionInjectable), - extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable), - resourcesDirectory: di.inject(lensResourcesDirInjectable), - readJsonFile: di.inject(readJsonFileInjectable), - pathExists: di.inject(pathExistsInjectable), - watch: di.inject(watchInjectable), - logger: di.inject(loggerInjectable), - accessPath: di.inject(accessPathInjectable), - copy: di.inject(copyInjectable), - removePath: di.inject(removePathInjectable), - ensureDirectory: di.inject(ensureDirInjectable), - isProduction: di.inject(isProductionInjectable), - lstat: di.inject(lstatInjectable), - readDirectory: di.inject(readDirectoryInjectable), - fileSystemSeparator: di.inject(fileSystemSeparatorInjectable), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - getDirnameOfPath: di.inject(getDirnameOfPathInjectable), - getRelativePath: di.inject(getRelativePathInjectable), - joinPaths: di.inject(joinPathsInjectable), - homeDirectoryPath: di.inject(homeDirectoryPathInjectable), - }), -}); - -export default extensionDiscoveryInjectable; diff --git a/src/extensions/extension-discovery/extension-discovery.test.ts b/src/extensions/extension-discovery/extension-discovery.test.ts deleted file mode 100644 index d71f8c5292..0000000000 --- a/src/extensions/extension-discovery/extension-discovery.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { FSWatcher } from "chokidar"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import extensionDiscoveryInjectable from "../extension-discovery/extension-discovery.injectable"; -import type { ExtensionDiscovery } from "../extension-discovery/extension-discovery"; -import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { delay } from "../../renderer/utils"; -import { observable, runInAction, when } from "mobx"; -import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; -import pathExistsInjectable from "../../common/fs/path-exists.injectable"; -import watchInjectable from "../../common/fs/watch/watch.injectable"; -import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable"; -import removePathInjectable from "../../common/fs/remove.injectable"; -import type { JoinPaths } from "../../common/path/join-paths.injectable"; -import joinPathsInjectable from "../../common/path/join-paths.injectable"; -import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable"; -import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable"; -import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable"; -import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable"; - -describe("ExtensionDiscovery", () => { - let extensionDiscovery: ExtensionDiscovery; - let readJsonFileMock: jest.Mock; - let pathExistsMock: jest.Mock; - let watchMock: jest.Mock; - let joinPaths: JoinPaths; - let homeDirectoryPath: string; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - di.override(installExtensionInjectable, () => () => Promise.resolve()); - di.override(extensionApiVersionInjectable, () => "5.0.0"); - di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); }); - di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); }); - di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); }); - - joinPaths = di.inject(joinPathsInjectable); - homeDirectoryPath = di.inject(homeDirectoryPathInjectable); - - readJsonFileMock = jest.fn(); - di.override(readJsonFileInjectable, () => readJsonFileMock); - - pathExistsMock = jest.fn(() => Promise.resolve(true)); - di.override(pathExistsInjectable, () => pathExistsMock); - - watchMock = jest.fn(); - di.override(watchInjectable, () => watchMock); - - di.override(removePathInjectable, () => async () => {}); // allow deleting files for now - - extensionDiscovery = di.inject(extensionDiscoveryInjectable); - }); - - it("emits add for added extension", async () => { - const letTestFinish = observable.box(false); - let addHandler!: (filePath: string) => void; - - readJsonFileMock.mockImplementation((p) => { - expect(p).toBe(joinPaths(homeDirectoryPath, ".k8slens/extensions/my-extension/package.json")); - - return { - name: "my-extension", - version: "1.0.0", - engines: { - lens: "5.0.0", - }, - }; - }); - - const mockWatchInstance = { - on: jest.fn((event: string, handler: typeof addHandler) => { - if (event === "add") { - addHandler = handler; - } - - return mockWatchInstance; - }), - } as unknown as FSWatcher; - - watchMock.mockImplementationOnce(() => mockWatchInstance); - - // Need to force isLoaded to be true so that the file watching is started - extensionDiscovery.isLoaded = true; - - await extensionDiscovery.watchExtensions(); - - extensionDiscovery.events.on("add", extension => { - expect(extension).toEqual({ - absolutePath: expect.any(String), - id: "/some-directory-for-user-data/node_modules/my-extension/package.json", - isBundled: false, - isEnabled: false, - isCompatible: true, - manifest: { - name: "my-extension", - version: "1.0.0", - engines: { - lens: "5.0.0", - }, - }, - manifestPath: "/some-directory-for-user-data/node_modules/my-extension/package.json", - }); - runInAction(() => letTestFinish.set(true)); - }); - - addHandler(joinPaths(extensionDiscovery.localFolderPath, "/my-extension/package.json")); - await when(() => letTestFinish.get()); - }); - - it("doesn't emit add for added file under extension", async () => { - let addHandler!: (filePath: string) => void; - - const mockWatchInstance = { - on: jest.fn((event: string, handler: typeof addHandler) => { - if (event === "add") { - addHandler = handler; - } - - return mockWatchInstance; - }), - } as unknown as FSWatcher; - - watchMock.mockImplementationOnce(() => mockWatchInstance); - - // Need to force isLoaded to be true so that the file watching is started - extensionDiscovery.isLoaded = true; - - await extensionDiscovery.watchExtensions(); - - const onAdd = jest.fn(); - - extensionDiscovery.events.on("add", onAdd); - - addHandler(joinPaths(extensionDiscovery.localFolderPath, "/my-extension/node_modules/dep/package.json")); - - await delay(10); - - expect(onAdd).not.toHaveBeenCalled(); - }); -}); diff --git a/src/extensions/extension-discovery/extension-discovery.ts b/src/extensions/extension-discovery/extension-discovery.ts deleted file mode 100644 index c9646a1c6c..0000000000 --- a/src/extensions/extension-discovery/extension-discovery.ts +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { ipcRenderer } from "electron"; -import { EventEmitter } from "events"; -import { makeObservable, observable, reaction, when } from "mobx"; -import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc"; -import { isErrnoException, toJS } from "../../common/utils"; -import type { ExtensionsStore } from "../extensions-store/extensions-store"; -import type { ExtensionLoader } from "../extension-loader"; -import type { LensExtensionId, LensExtensionManifest } from "../lens-extension"; -import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store"; -import { extensionDiscoveryStateChannel } from "../../common/ipc/extension-handling"; -import { requestInitialExtensionDiscovery } from "../../renderer/ipc"; -import type { ReadJson } from "../../common/fs/read-json-file.injectable"; -import type { Logger } from "../../common/logger"; -import type { PathExists } from "../../common/fs/path-exists.injectable"; -import type { Watch } from "../../common/fs/watch/watch.injectable"; -import type { Stats } from "fs"; -import type { LStat } from "../../common/fs/lstat.injectable"; -import type { ReadDirectory } from "../../common/fs/read-directory.injectable"; -import type { EnsureDirectory } from "../../common/fs/ensure-dir.injectable"; -import type { AccessPath } from "../../common/fs/access-path.injectable"; -import type { Copy } from "../../common/fs/copy.injectable"; -import type { JoinPaths } from "../../common/path/join-paths.injectable"; -import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; -import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; -import type { GetRelativePath } from "../../common/path/get-relative-path.injectable"; -import type { RemovePath } from "../../common/fs/remove.injectable"; -import type TypedEventEmitter from "typed-emitter"; - -interface Dependencies { - readonly extensionLoader: ExtensionLoader; - readonly extensionsStore: ExtensionsStore; - readonly extensionInstallationStateStore: ExtensionInstallationStateStore; - readonly extensionPackageRootDirectory: string; - readonly resourcesDirectory: string; - readonly logger: Logger; - readonly isProduction: boolean; - readonly fileSystemSeparator: string; - readonly homeDirectoryPath: string; - isCompatibleExtension: (manifest: LensExtensionManifest) => boolean; - installExtension: (name: string) => Promise; - readJsonFile: ReadJson; - pathExists: PathExists; - removePath: RemovePath; - lstat: LStat; - watch: Watch; - readDirectory: ReadDirectory; - ensureDirectory: EnsureDirectory; - accessPath: AccessPath; - copy: Copy; - joinPaths: JoinPaths; - getBasenameOfPath: GetBasenameOfPath; - getDirnameOfPath: GetDirnameOfPath; - getRelativePath: GetRelativePath; -} - -export interface InstalledExtension { - id: LensExtensionId; - - readonly manifest: LensExtensionManifest; - - // Absolute path to the non-symlinked source folder, - // e.g. "/Users/user/.k8slens/extensions/helloworld" - readonly absolutePath: string; - - // Absolute to the symlinked package.json file - readonly manifestPath: string; - readonly isBundled: boolean; // defined in project root's package.json - readonly isCompatible: boolean; - isEnabled: boolean; -} - -const logModule = "[EXTENSION-DISCOVERY]"; - -export const manifestFilename = "package.json"; - -interface ExtensionDiscoveryChannelMessage { - isLoaded: boolean; -} - -/** - * Returns true if the lstat is for a directory-like file (e.g. isDirectory or symbolic link) - * @param lstat the stats to compare - */ -const isDirectoryLike = (lstat: Stats) => lstat.isDirectory() || lstat.isSymbolicLink(); - -interface LoadFromFolderOptions { - isBundled?: boolean; -} - -interface ExtensionDiscoveryEvents { - add: (ext: InstalledExtension) => void; - remove: (extId: LensExtensionId) => void; -} - -/** - * Discovers installed bundled and local extensions from the filesystem. - * Also watches for added and removed local extensions by watching the directory. - * Uses ExtensionInstaller to install dependencies for all of the extensions. - * This is also done when a new extension is copied to the local extensions directory. - * .init() must be called to start the directory watching. - * The class emits events for added and removed extensions: - * - "add": When extension is added. The event is of type InstalledExtension - * - "remove": When extension is removed. The event is of type LensExtensionId - */ -export class ExtensionDiscovery { - protected bundledFolderPath!: string; - - private loadStarted = false; - private extensions: Map = new Map(); - - // True if extensions have been loaded from the disk after app startup - @observable isLoaded = false; - - get whenLoaded() { - return when(() => this.isLoaded); - } - - public readonly events: TypedEventEmitter = new EventEmitter(); - - constructor(protected readonly dependencies: Dependencies) { - makeObservable(this); - } - - get localFolderPath(): string { - return this.dependencies.joinPaths(this.dependencies.homeDirectoryPath, ".k8slens", "extensions"); - } - - get packageJsonPath(): string { - return this.dependencies.joinPaths(this.dependencies.extensionPackageRootDirectory, manifestFilename); - } - - get nodeModulesPath(): string { - return this.dependencies.joinPaths(this.dependencies.extensionPackageRootDirectory, "node_modules"); - } - - /** - * Initializes the class and setups the file watcher for added/removed local extensions. - */ - async init(): Promise { - if (ipcRenderer) { - await this.initRenderer(); - } else { - await this.initMain(); - } - } - - async initRenderer(): Promise { - const onMessage = ({ isLoaded }: ExtensionDiscoveryChannelMessage) => { - this.isLoaded = isLoaded; - }; - - requestInitialExtensionDiscovery().then(onMessage); - ipcRendererOn(extensionDiscoveryStateChannel, (_event, message: ExtensionDiscoveryChannelMessage) => { - onMessage(message); - }); - } - - async initMain(): Promise { - ipcMainHandle(extensionDiscoveryStateChannel, () => this.toJSON()); - - reaction(() => this.toJSON(), () => { - this.broadcast(); - }); - } - - /** - * Watches for added/removed local extensions. - * Dependencies are installed automatically after an extension folder is copied. - */ - async watchExtensions(): Promise { - this.dependencies.logger.info(`${logModule} watching extension add/remove in ${this.localFolderPath}`); - - // Wait until .load() has been called and has been resolved - await this.whenLoaded; - - this.dependencies.watch(this.localFolderPath, { - // For adding and removing symlinks to work, the depth has to be 1. - depth: 1, - ignoreInitial: true, - // Try to wait until the file has been completely copied. - // The OS might emit an event for added file even it's not completely written to the file-system. - awaitWriteFinish: { - // Wait 300ms until the file size doesn't change to consider the file written. - // For a small file like package.json this should be plenty of time. - stabilityThreshold: 300, - }, - }) - // Extension add is detected by watching "/package.json" add - .on("add", this.handleWatchFileAdd) - // Extension remove is detected by watching "" unlink - .on("unlinkDir", this.handleWatchUnlinkEvent) - // Extension remove is detected by watching "" unlink - .on("unlink", this.handleWatchUnlinkEvent); - } - - handleWatchFileAdd = async (manifestPath: string): Promise => { - // e.g. "foo/package.json" - const relativePath = this.dependencies.getRelativePath(this.localFolderPath, manifestPath); - - // Converts "foo/package.json" to ["foo", "package.json"], where length of 2 implies - // that the added file is in a folder under local folder path. - // This safeguards against a file watch being triggered under a sub-directory which is not an extension. - const isUnderLocalFolderPath = relativePath.split(this.dependencies.fileSystemSeparator).length === 2; - - if (this.dependencies.getBasenameOfPath(manifestPath) === manifestFilename && isUnderLocalFolderPath) { - try { - this.dependencies.extensionInstallationStateStore.setInstallingFromMain(manifestPath); - const absPath = this.dependencies.getDirnameOfPath(manifestPath); - - // this.loadExtensionFromPath updates this.packagesJson - const extension = await this.loadExtensionFromFolder(absPath); - - if (extension) { - // Remove a broken symlink left by a previous installation if it exists. - await this.dependencies.removePath(extension.manifestPath); - - // Install dependencies for the new extension - await this.dependencies.installExtension(extension.absolutePath); - - this.extensions.set(extension.id, extension); - this.dependencies.logger.info(`${logModule} Added extension ${extension.manifest.name}`); - this.events.emit("add", extension); - } - } catch (error) { - this.dependencies.logger.error(`${logModule}: failed to add extension: ${error}`, { error }); - } finally { - this.dependencies.extensionInstallationStateStore.clearInstallingFromMain(manifestPath); - } - } - }; - - /** - * Handle any unlink event, filtering out non-package.json links so the delete code - * only happens once per extension. - * @param filePath The absolute path to either a folder or file in the extensions folder - */ - handleWatchUnlinkEvent = async (filePath: string): Promise => { - // Check that the removed path is directly under this.localFolderPath - // Note that the watcher can create unlink events for subdirectories of the extension - const extensionFolderName = this.dependencies.getBasenameOfPath(filePath); - const expectedPath = this.dependencies.getRelativePath(this.localFolderPath, filePath); - - if (expectedPath !== extensionFolderName) { - return; - } - - for (const extension of this.extensions.values()) { - if (extension.absolutePath !== filePath) { - continue; - } - - const extensionName = extension.manifest.name; - - // If the extension is deleted manually while the application is running, also remove the symlink - await this.removeSymlinkByPackageName(extensionName); - - // The path to the manifest file is the lens extension id - // Note: that we need to use the symlinked path - const lensExtensionId = extension.manifestPath; - - this.extensions.delete(extension.id); - this.dependencies.logger.info(`${logModule} removed extension ${extensionName}`); - this.events.emit("remove", lensExtensionId); - - return; - } - - this.dependencies.logger.warn(`${logModule} extension ${extensionFolderName} not found, can't remove`); - }; - - /** - * Remove the symlink under node_modules if exists. - * If we don't remove the symlink, the uninstall would leave a non-working symlink, - * which wouldn't be fixed if the extension was reinstalled, causing the extension not to work. - * @param name e.g. "@mirantis/lens-extension-cc" - */ - removeSymlinkByPackageName(name: string): Promise { - return this.dependencies.removePath(this.getInstalledPath(name)); - } - - /** - * Uninstalls extension. - * The application will detect the folder unlink and remove the extension from the UI automatically. - * @param extensionId The ID of the extension to uninstall. - */ - async uninstallExtension(extensionId: LensExtensionId): Promise { - const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtension(extensionId); - - if (!extension) { - return void this.dependencies.logger.warn(`${logModule} could not uninstall extension, not found`, { id: extensionId }); - } - - const { manifest, absolutePath } = extension; - - this.dependencies.logger.info(`${logModule} Uninstalling ${manifest.name}`); - - await this.removeSymlinkByPackageName(manifest.name); - - // fs.remove does nothing if the path doesn't exist anymore - await this.dependencies.removePath(absolutePath); - } - - async load(): Promise> { - if (this.loadStarted) { - // The class is simplified by only supporting .load() to be called once - throw new Error("ExtensionDiscovery.load() can be only be called once"); - } - - this.loadStarted = true; - - this.dependencies.logger.info( - `${logModule} loading extensions from ${this.dependencies.extensionPackageRootDirectory}`, - ); - - await this.dependencies.removePath(this.dependencies.joinPaths(this.dependencies.extensionPackageRootDirectory, "package-lock.json")); - await this.dependencies.ensureDirectory(this.nodeModulesPath); - await this.dependencies.ensureDirectory(this.localFolderPath); - - const extensions = await this.ensureExtensions(); - - this.isLoaded = true; - - return extensions; - } - - /** - * Returns the symlinked path to the extension folder, - * e.g. "/Users//Library/Application Support/Lens/node_modules/@publisher/extension" - */ - protected getInstalledPath(name: string): string { - return this.dependencies.joinPaths(this.nodeModulesPath, name); - } - - /** - * Returns the symlinked path to the package.json, - * e.g. "/Users//Library/Application Support/Lens/node_modules/@publisher/extension/package.json" - */ - protected getInstalledManifestPath(name: string): string { - return this.dependencies.joinPaths(this.getInstalledPath(name), manifestFilename); - } - - /** - * Returns InstalledExtension from path to package.json file. - * Also updates this.packagesJson. - */ - protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise { - try { - const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest; - const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name); - const isEnabled = this.dependencies.extensionsStore.isEnabled({ id, isBundled }); - const extensionDir = this.dependencies.getDirnameOfPath(manifestPath); - const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`); - const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage) - ? npmPackage - : extensionDir; - const isCompatible = isBundled || this.dependencies.isCompatibleExtension(manifest); - - return { - id, - absolutePath, - manifestPath: id, - manifest, - isBundled, - isEnabled, - isCompatible, - }; - } catch (error) { - if (isErrnoException(error) && error.code === "ENOTDIR") { - // ignore this error, probably from .DS_Store file - this.dependencies.logger.debug(`${logModule}: failed to load extension manifest through a not-dir-like at ${manifestPath}`); - } else { - this.dependencies.logger.error(`${logModule}: can't load extension manifest at ${manifestPath}: ${error}`); - } - - return null; - } - } - - async ensureExtensions(): Promise> { - const userExtensions = await this.loadFromFolder(this.localFolderPath); - - return this.extensions = new Map(userExtensions.map(extension => [extension.id, extension])); - } - - async loadFromFolder(folderPath: string): Promise { - const extensions: InstalledExtension[] = []; - const paths = await this.dependencies.readDirectory(folderPath); - - for (const fileName of paths) { - const absPath = this.dependencies.joinPaths(folderPath, fileName); - - try { - const lstat = await this.dependencies.lstat(absPath); - - // skip non-directories - if (!isDirectoryLike(lstat)) { - continue; - } - } catch (error) { - if (isErrnoException(error) && error.code === "ENOENT") { - continue; - } - - throw error; - } - - const extension = await this.loadExtensionFromFolder(absPath); - - if (extension) { - extensions.push(extension); - } - } - - this.dependencies.logger.debug(`${logModule}: ${extensions.length} extensions loaded`, { folderPath, extensions }); - - return extensions; - } - - /** - * Loads extension from absolute path, updates this.packagesJson to include it and returns the extension. - * @param folderPath Folder path to extension - */ - async loadExtensionFromFolder(folderPath: string, { isBundled = false }: LoadFromFolderOptions = {}): Promise { - const manifestPath = this.dependencies.joinPaths(folderPath, manifestFilename); - - return this.getByManifest(manifestPath, { isBundled }); - } - - toJSON(): ExtensionDiscoveryChannelMessage { - return toJS({ - isLoaded: this.isLoaded, - }); - } - - broadcast(): void { - broadcastMessage(extensionDiscoveryStateChannel, this.toJSON()); - } -} diff --git a/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.injectable.ts b/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.injectable.ts deleted file mode 100644 index de2fd4390f..0000000000 --- a/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 extensionApiVersionInjectable from "../../../common/vars/extension-api-version.injectable"; -import { isCompatibleExtension } from "./is-compatible-extension"; - -const isCompatibleExtensionInjectable = getInjectable({ - id: "is-compatible-extension", - instantiate: (di) => isCompatibleExtension({ - extensionApiVersion: di.inject(extensionApiVersionInjectable), - }), -}); - -export default isCompatibleExtensionInjectable; diff --git a/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.ts b/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.ts deleted file mode 100644 index 74cbb4fd0c..0000000000 --- a/src/extensions/extension-discovery/is-compatible-extension/is-compatible-extension.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import semver from "semver"; -import type { LensExtensionManifest } from "../../lens-extension"; - -interface Dependencies { - extensionApiVersion: string; -} - -export const isCompatibleExtension = ({ extensionApiVersion }: Dependencies): ((manifest: LensExtensionManifest) => boolean) => { - return (manifest: LensExtensionManifest): boolean => { - const manifestLensEngine = manifest.engines.lens; - const validVersion = manifestLensEngine.match(/^[\^0-9]\d*\.\d+\b/); // must start from ^ or number - - if (!validVersion) { - const errorInfo = [ - `Invalid format for "manifest.engines.lens"="${manifestLensEngine}"`, - `Range versions can only be specified starting with '^'.`, - `Otherwise it's recommended to use plain %MAJOR.%MINOR to match with supported Lens version.`, - ].join("\n"); - - throw new Error(errorInfo); - } - - const { major: extMajor, minor: extMinor } = semver.coerce(manifestLensEngine, { - loose: true, - }) as semver.SemVer; - const supportedVersionsByExtension = semver.validRange(`^${extMajor}.${extMinor}`) as string; - - return semver.satisfies(extensionApiVersion, supportedVersionsByExtension, { - loose: true, - includePrerelease: false, - }); - }; -}; diff --git a/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts b/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts deleted file mode 100644 index e7ad2285bc..0000000000 --- a/src/extensions/extension-installation-state-store/extension-installation-state-store.injectable.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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 loggerInjectable from "../../common/logger.injectable"; -import { ExtensionInstallationStateStore } from "./extension-installation-state-store"; - -const extensionInstallationStateStoreInjectable = getInjectable({ - id: "extension-installation-state-store", - instantiate: (di) => new ExtensionInstallationStateStore({ - logger: di.inject(loggerInjectable), - }), -}); - -export default extensionInstallationStateStoreInjectable; diff --git a/src/extensions/extension-installation-state-store/extension-installation-state-store.ts b/src/extensions/extension-installation-state-store/extension-installation-state-store.ts deleted file mode 100644 index 093c80934b..0000000000 --- a/src/extensions/extension-installation-state-store/extension-installation-state-store.ts +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, computed, observable } from "mobx"; -import { disposer } from "../../renderer/utils"; -import type { ExtendableDisposer } from "../../renderer/utils"; -import * as uuid from "uuid"; -import { broadcastMessage } from "../../common/ipc"; -import { ipcRenderer } from "electron"; -import type { Logger } from "../../common/logger"; - -export enum ExtensionInstallationState { - INSTALLING = "installing", - UNINSTALLING = "uninstalling", - IDLE = "idle", -} - -interface Dependencies { - readonly logger: Logger; -} - -const Prefix = "[ExtensionInstallationStore]"; - -const installingFromMainChannel = "extension-installation-state-store:install"; -const clearInstallingFromMainChannel = "extension-installation-state-store:clear-install"; - -export class ExtensionInstallationStateStore { - private readonly preInstallIds = observable.set(); - private readonly uninstallingExtensions = observable.set(); - private readonly installingExtensions = observable.set(); - - constructor(private readonly dependencies: Dependencies) {} - - bindIpcListeners = () => { - ipcRenderer - .on(installingFromMainChannel, (event, extId) => { - this.setInstalling(extId); - }) - - .on(clearInstallingFromMainChannel, (event, extId) => { - this.clearInstalling(extId); - }); - }; - - /** - * Strictly transitions an extension from not installing to installing - * @param extId the ID of the extension - * @throws if state is not IDLE - */ - @action setInstalling = (extId: string): void => { - this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as installing`); - - const curState = this.getInstallationState(extId); - - if (curState !== ExtensionInstallationState.IDLE) { - throw new Error( - `${Prefix}: cannot set ${extId} as installing. Is currently ${curState}.`, - ); - } - - this.installingExtensions.add(extId); - }; - - /** - * Broadcasts that an extension is being installed by the main process - * @param extId the ID of the extension - */ - setInstallingFromMain = (extId: string): void => { - broadcastMessage(installingFromMainChannel, extId); - }; - - /** - * Broadcasts that an extension is no longer being installed by the main process - * @param extId the ID of the extension - */ - clearInstallingFromMain = (extId: string): void => { - broadcastMessage(clearInstallingFromMainChannel, extId); - }; - - /** - * Marks the start of a pre-install phase of an extension installation. The - * part of the installation before the tarball has been unpacked and the ID - * determined. - * @returns a disposer which should be called to mark the end of the install phase - */ - @action startPreInstall = (): ExtendableDisposer => { - const preInstallStepId = uuid.v4(); - - this.dependencies.logger.debug( - `${Prefix}: starting a new preinstall phase: ${preInstallStepId}`, - ); - this.preInstallIds.add(preInstallStepId); - - return disposer(() => { - this.preInstallIds.delete(preInstallStepId); - this.dependencies.logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); - }); - }; - - /** - * Strictly transitions an extension from not uninstalling to uninstalling - * @param extId the ID of the extension - * @throws if state is not IDLE - */ - @action setUninstalling = (extId: string): void => { - this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as uninstalling`); - - const curState = this.getInstallationState(extId); - - if (curState !== ExtensionInstallationState.IDLE) { - throw new Error( - `${Prefix}: cannot set ${extId} as uninstalling. Is currently ${curState}.`, - ); - } - - this.uninstallingExtensions.add(extId); - }; - - /** - * Strictly clears the INSTALLING state of an extension - * @param extId The ID of the extension - * @throws if state is not INSTALLING - */ - @action clearInstalling = (extId: string): void => { - this.dependencies.logger.debug(`${Prefix}: trying to clear ${extId} as installing`); - - const curState = this.getInstallationState(extId); - - switch (curState) { - case ExtensionInstallationState.INSTALLING: - return void this.installingExtensions.delete(extId); - default: - throw new Error( - `${Prefix}: cannot clear INSTALLING state for ${extId}, it is currently ${curState}`, - ); - } - }; - - /** - * Strictly clears the UNINSTALLING state of an extension - * @param extId The ID of the extension - * @throws if state is not UNINSTALLING - */ - @action clearUninstalling = (extId: string): void => { - this.dependencies.logger.debug(`${Prefix}: trying to clear ${extId} as uninstalling`); - - const curState = this.getInstallationState(extId); - - switch (curState) { - case ExtensionInstallationState.UNINSTALLING: - return void this.uninstallingExtensions.delete(extId); - default: - throw new Error( - `${Prefix}: cannot clear UNINSTALLING state for ${extId}, it is currently ${curState}`, - ); - } - }; - - /** - * Returns the current state of the extension. IDLE is default value. - * @param extId The ID of the extension - */ - getInstallationState = (extId: string): ExtensionInstallationState => { - if (this.installingExtensions.has(extId)) { - return ExtensionInstallationState.INSTALLING; - } - - if (this.uninstallingExtensions.has(extId)) { - return ExtensionInstallationState.UNINSTALLING; - } - - return ExtensionInstallationState.IDLE; - }; - - /** - * Returns true if the extension is currently INSTALLING - * @param extId The ID of the extension - */ - isExtensionInstalling = (extId: string): boolean => - this.getInstallationState(extId) === ExtensionInstallationState.INSTALLING; - - /** - * Returns true if the extension is currently UNINSTALLING - * @param extId The ID of the extension - */ - isExtensionUninstalling = (extId: string): boolean => - this.getInstallationState(extId) === - ExtensionInstallationState.UNINSTALLING; - - /** - * Returns true if the extension is currently IDLE - * @param extId The ID of the extension - */ - isExtensionIdle = (extId: string): boolean => - this.getInstallationState(extId) === ExtensionInstallationState.IDLE; - - /** - * The current number of extensions installing - */ - @computed get installing(): number { - return this.installingExtensions.size; - } - - /** - * The current number of extensions uninstalling - */ - get uninstalling(): number { - return this.uninstallingExtensions.size; - } - - /** - * If there is at least one extension currently installing - */ - get anyInstalling(): boolean { - return this.installing > 0; - } - - /** - * If there is at least one extension currently uninstalling - */ - get anyUninstalling(): boolean { - return this.uninstalling > 0; - } - - /** - * The current number of extensions preinstalling - */ - get preinstalling(): number { - return this.preInstallIds.size; - } - - /** - * If there is at least one extension currently downloading - */ - get anyPreinstalling(): boolean { - return this.preinstalling > 0; - } - - /** - * If there is at least one installing or preinstalling step taking place - */ - get anyPreInstallingOrInstalling(): boolean { - return this.anyInstalling || this.anyPreinstalling; - } -} diff --git a/src/extensions/extension-installer/extension-installer.injectable.ts b/src/extensions/extension-installer/extension-installer.injectable.ts deleted file mode 100644 index 92b4436701..0000000000 --- a/src/extensions/extension-installer/extension-installer.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 pathToNpmCliInjectable from "../../common/app-paths/path-to-npm-cli.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import { ExtensionInstaller } from "./extension-installer"; -import extensionPackageRootDirectoryInjectable from "./extension-package-root-directory/extension-package-root-directory.injectable"; - -const extensionInstallerInjectable = getInjectable({ - id: "extension-installer", - - instantiate: (di) => new ExtensionInstaller({ - extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable), - logger: di.inject(loggerInjectable), - pathToNpmCli: di.inject(pathToNpmCliInjectable), - }), -}); - -export default extensionInstallerInjectable; diff --git a/src/extensions/extension-installer/extension-installer.ts b/src/extensions/extension-installer/extension-installer.ts deleted file mode 100644 index 223477d0c4..0000000000 --- a/src/extensions/extension-installer/extension-installer.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import AwaitLock from "await-lock"; -import child_process from "child_process"; -import type { Logger } from "../../common/logger"; - -const logModule = "[EXTENSION-INSTALLER]"; - -interface Dependencies { - readonly extensionPackageRootDirectory: string; - readonly logger: Logger; - readonly pathToNpmCli: string; -} - -const baseNpmInstallArgs = [ - "install", - "--audit=false", - "--fund=false", - // NOTE: we do not omit the `optional` dependencies because that is how we specify the non-bundled extensions - "--omit=dev", - "--omit=peer", - "--prefer-offline", -]; - -/** - * Installs dependencies for extensions - */ -export class ExtensionInstaller { - private readonly installLock = new AwaitLock(); - - constructor(private readonly dependencies: Dependencies) {} - - /** - * Install single package using npm - */ - installPackage = async (name: string): Promise => { - // Mutual exclusion to install packages in sequence - await this.installLock.acquireAsync(); - - try { - this.dependencies.logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); - await this.npm(...baseNpmInstallArgs, name); - this.dependencies.logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); - } finally { - this.installLock.release(); - } - }; - - private npm(...args: string[]): Promise { - return new Promise((resolve, reject) => { - const child = child_process.fork(this.dependencies.pathToNpmCli, args, { - cwd: this.dependencies.extensionPackageRootDirectory, - silent: true, - env: {}, - }); - let stderr = ""; - - child.stderr?.on("data", data => { - stderr += String(data); - }); - - child.on("close", (code) => { - if (code !== 0) { - reject(new Error(stderr)); - } else { - resolve(); - } - }); - - child.on("error", error => { - reject(error); - }); - }); - } -} diff --git a/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts b/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts deleted file mode 100644 index 72bd0ad8c2..0000000000 --- a/src/extensions/extension-installer/extension-package-root-directory/extension-package-root-directory.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; - -const extensionPackageRootDirectoryInjectable = getInjectable({ - id: "extension-package-root-directory", - - instantiate: (di) => di.inject(directoryForUserDataInjectable), -}); - -export default extensionPackageRootDirectoryInjectable; diff --git a/src/extensions/extension-installer/install-extension/install-extension.injectable.ts b/src/extensions/extension-installer/install-extension/install-extension.injectable.ts deleted file mode 100644 index 940c5987a5..0000000000 --- a/src/extensions/extension-installer/install-extension/install-extension.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 extensionInstallerInjectable from "../extension-installer.injectable"; - -const installExtensionInjectable = getInjectable({ - id: "install-extension", - instantiate: (di) => di.inject(extensionInstallerInjectable).installPackage, -}); - -export default installExtensionInjectable; diff --git a/src/extensions/extension-loader/create-extension-instance.token.ts b/src/extensions/extension-loader/create-extension-instance.token.ts deleted file mode 100644 index d7680b018b..0000000000 --- a/src/extensions/extension-loader/create-extension-instance.token.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { InstalledExtension } from "../extension-discovery/extension-discovery"; -import type { LensExtension, LensExtensionConstructor } from "../lens-extension"; - -export type CreateExtensionInstance = (ExtensionClass: LensExtensionConstructor, extension: InstalledExtension) => LensExtension; - -export const createExtensionInstanceInjectionToken = getInjectionToken({ - id: "create-extension-instance-token", -}); diff --git a/src/extensions/extension-loader/entry-point-name.ts b/src/extensions/extension-loader/entry-point-name.ts deleted file mode 100644 index 390da4a34a..0000000000 --- a/src/extensions/extension-loader/entry-point-name.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const extensionEntryPointNameInjectionToken = getInjectionToken<"main" | "renderer">({ - id: "extension-entry-point-name-token", -}); diff --git a/src/extensions/extension-loader/extension-instances.injectable.ts b/src/extensions/extension-loader/extension-instances.injectable.ts deleted file mode 100644 index 08e7ad4cc5..0000000000 --- a/src/extensions/extension-loader/extension-instances.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { LensExtensionId, LensExtension } from "../lens-extension"; - -const extensionInstancesInjectable = getInjectable({ - id: "extension-instances", - instantiate: () => observable.map(), -}); - -export default extensionInstancesInjectable; diff --git a/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts b/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts deleted file mode 100644 index 23e1c87561..0000000000 --- a/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 type { KubernetesCluster } from "../../common/catalog-entities"; -import type { LensRendererExtension } from "../lens-renderer-extension"; - -interface ExtensionIsEnabledForCluster { - extension: LensRendererExtension; - cluster: KubernetesCluster; -} - -const extensionIsEnabledForClusterInjectable = getInjectable({ - id: "extension-is-enabled-for-cluster", - - instantiate: async ( - di, - { extension, cluster }: ExtensionIsEnabledForCluster, - ) => (await extension.isEnabledForCluster(cluster)) as boolean, - - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: ( - di, - { extension, cluster }: ExtensionIsEnabledForCluster, - ) => `${extension.sanitizedExtensionId}-${cluster.getId()}`, - }), -}); - -export default extensionIsEnabledForClusterInjectable; diff --git a/src/extensions/extension-loader/extension-loader.injectable.ts b/src/extensions/extension-loader/extension-loader.injectable.ts deleted file mode 100644 index 67f8434043..0000000000 --- a/src/extensions/extension-loader/extension-loader.injectable.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 { ExtensionLoader } from "./extension-loader"; -import updateExtensionsStateInjectable from "./update-extensions-state/update-extensions-state.injectable"; -import { createExtensionInstanceInjectionToken } from "./create-extension-instance.token"; -import extensionInstancesInjectable from "./extension-instances.injectable"; -import type { LensExtension } from "../lens-extension"; -import extensionInjectable from "./extension/extension.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import joinPathsInjectable from "../../common/path/join-paths.injectable"; -import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; -import { bundledExtensionInjectionToken } from "../extension-discovery/bundled-extension-token"; -import { extensionEntryPointNameInjectionToken } from "./entry-point-name"; - -const extensionLoaderInjectable = getInjectable({ - id: "extension-loader", - - instantiate: (di) => new ExtensionLoader({ - updateExtensionsState: di.inject(updateExtensionsStateInjectable), - createExtensionInstance: di.inject(createExtensionInstanceInjectionToken), - extensionInstances: di.inject(extensionInstancesInjectable), - getExtension: (instance: LensExtension) => di.inject(extensionInjectable, instance), - bundledExtensions: di.injectMany(bundledExtensionInjectionToken), - extensionEntryPointName: di.inject(extensionEntryPointNameInjectionToken), - logger: di.inject(loggerInjectable), - joinPaths: di.inject(joinPathsInjectable), - getDirnameOfPath: di.inject(getDirnameOfPathInjectable), - }), -}); - -export default extensionLoaderInjectable; diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts deleted file mode 100644 index 5dce874e54..0000000000 --- a/src/extensions/extension-loader/extension-loader.ts +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { ipcMain, ipcRenderer } from "electron"; -import { isEqual } from "lodash"; -import type { ObservableMap } from "mobx"; -import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx"; -import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc"; -import { isDefined, toJS } from "../../common/utils"; -import type { InstalledExtension } from "../extension-discovery/extension-discovery"; -import type { LensExtension, LensExtensionConstructor, LensExtensionId } from "../lens-extension"; -import type { LensExtensionState } from "../extensions-store/extensions-store"; -import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling"; -import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; -import assert from "assert"; -import { EventEmitter } from "../../common/event-emitter"; -import type { CreateExtensionInstance } from "./create-extension-instance.token"; -import type { Extension } from "./extension/extension.injectable"; -import type { Logger } from "../../common/logger"; -import type { JoinPaths } from "../../common/path/join-paths.injectable"; -import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; -import type { BundledExtension } from "../extension-discovery/bundled-extension-token"; - -const logModule = "[EXTENSIONS-LOADER]"; - -interface Dependencies { - readonly extensionInstances: ObservableMap; - readonly bundledExtensions: BundledExtension[]; - readonly logger: Logger; - readonly extensionEntryPointName: "main" | "renderer"; - updateExtensionsState: (extensionsState: Record) => void; - createExtensionInstance: CreateExtensionInstance; - getExtension: (instance: LensExtension) => Extension; - joinPaths: JoinPaths; - getDirnameOfPath: GetDirnameOfPath; -} - -interface ExtensionBeingActivated { - instance: LensExtension; - installedExtension: InstalledExtension; - activated: Promise; -} - -export interface ExtensionLoading { - isBundled: boolean; - loaded: Promise; -} - -/** - * Loads installed extensions to the Lens application - */ -export class ExtensionLoader { - protected readonly extensions = observable.map(); - - /** - * This is the set of extensions that don't come with either - * - Main.LensExtension when running in the main process - * - Renderer.LensExtension when running in the renderer process - */ - protected readonly nonInstancesByName = observable.set(); - - /** - * This is updated by the `observe` in the constructor. DO NOT write directly to it - */ - protected readonly instancesByName = observable.map(); - - private readonly onRemoveExtensionId = new EventEmitter<[string]>(); - - @observable isLoaded = false; - - get whenLoaded() { - return when(() => this.isLoaded); - } - - constructor(protected readonly dependencies: Dependencies) { - makeObservable(this); - - observe(this.dependencies.extensionInstances, change => { - switch (change.type) { - case "add": - if (this.instancesByName.has(change.newValue.name)) { - throw new TypeError("Extension names must be unique"); - } - - this.instancesByName.set(change.newValue.name, change.newValue); - break; - case "delete": - this.instancesByName.delete(change.oldValue.name); - break; - case "update": - throw new Error("Extension instances shouldn't be updated"); - } - }); - } - - @computed get userExtensions(): Map { - const extensions = this.toJSON(); - - extensions.forEach((ext, extId) => { - if (ext.isBundled) { - extensions.delete(extId); - } - }); - - return extensions; - } - - /** - * Get the extension instance by its manifest name - * @param name The name of the extension - * @returns one of the following: - * - the instance of `Main.LensExtension` on the main process if created - * - the instance of `Renderer.LensExtension` on the renderer process if created - * - `null` if no class definition is provided for the current process - * - `undefined` if the name is not known about - */ - getInstanceByName(name: string): LensExtension | null | undefined { - if (this.nonInstancesByName.has(name)) { - return null; - } - - return this.instancesByName.get(name); - } - - // Transform userExtensions to a state object for storing into ExtensionsStore - @computed get storeState() { - return Object.fromEntries( - Array.from(this.userExtensions) - .map(([extId, extension]) => [extId, { - enabled: extension.isEnabled, - name: extension.manifest.name, - }]), - ); - } - - @action - async init() { - if (ipcMain) { - await this.initMain(); - } else { - await this.initRenderer(); - } - - await Promise.all([this.whenLoaded]); - - // broadcasting extensions between main/renderer processes - reaction(() => this.toJSON(), () => this.broadcastExtensions(), { - fireImmediately: true, - }); - - reaction( - () => this.storeState, - - (state) => { - this.dependencies.updateExtensionsState(state); - }, - ); - } - - initExtensions(extensions: Map) { - this.extensions.replace(extensions); - } - - addExtension(extension: InstalledExtension) { - this.extensions.set(extension.id, extension); - } - - @action - removeInstance(lensExtensionId: LensExtensionId) { - this.dependencies.logger.info(`${logModule} deleting extension instance ${lensExtensionId}`); - const instance = this.dependencies.extensionInstances.get(lensExtensionId); - - if (!instance) { - return; - } - - try { - instance.disable(); - - const extension = this.dependencies.getExtension(instance); - - extension.deregister(); - - this.onRemoveExtensionId.emit(instance.id); - this.dependencies.extensionInstances.delete(lensExtensionId); - this.nonInstancesByName.delete(instance.name); - } catch (error) { - this.dependencies.logger.error(`${logModule}: deactivation extension error`, { lensExtensionId, error }); - } - } - - removeExtension(lensExtensionId: LensExtensionId) { - this.removeInstance(lensExtensionId); - - if (!this.extensions.delete(lensExtensionId)) { - throw new Error(`Can't remove extension ${lensExtensionId}, doesn't exist.`); - } - } - - setIsEnabled(lensExtensionId: LensExtensionId, isEnabled: boolean) { - const extension = this.extensions.get(lensExtensionId); - - assert(extension, `Must register extension ${lensExtensionId} with before enabling it`); - - extension.isEnabled = isEnabled; - } - - protected async initMain() { - this.isLoaded = true; - await this.autoInitExtensions(); - - ipcMainHandle(extensionLoaderFromMainChannel, () => { - return Array.from(this.toJSON()); - }); - - ipcMainOn(extensionLoaderFromRendererChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { - this.syncExtensions(extensions); - }); - } - - protected async initRenderer() { - const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => { - this.isLoaded = true; - this.syncExtensions(extensions); - - const receivedExtensionIds = extensions.map(([lensExtensionId]) => lensExtensionId); - - // Remove deleted extensions in renderer side only - this.extensions.forEach((_, lensExtensionId) => { - if (!receivedExtensionIds.includes(lensExtensionId)) { - this.removeExtension(lensExtensionId); - } - }); - }; - - requestExtensionLoaderInitialState().then(extensionListHandler); - ipcRendererOn(extensionLoaderFromMainChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { - extensionListHandler(extensions); - }); - } - - broadcastExtensions() { - const channel = ipcRenderer - ? extensionLoaderFromRendererChannel - : extensionLoaderFromMainChannel; - - broadcastMessage(channel, Array.from(this.extensions)); - } - - syncExtensions(extensions: [LensExtensionId, InstalledExtension][]) { - extensions.forEach(([lensExtensionId, extension]) => { - if (!isEqual(this.extensions.get(lensExtensionId), extension)) { - this.extensions.set(lensExtensionId, extension); - } - }); - } - - protected async loadBundledExtensions() { - return this.dependencies.bundledExtensions - .map(extension => { - try { - const LensExtensionClass = extension[this.dependencies.extensionEntryPointName](); - - if (!LensExtensionClass) { - return null; - } - - const installedExtension: InstalledExtension = { - absolutePath: "irrelevant", - id: extension.manifest.name, - isBundled: true, - isCompatible: true, - isEnabled: true, - manifest: extension.manifest, - manifestPath: "irrelevant", - }; - const instance = this.dependencies.createExtensionInstance( - LensExtensionClass, - installedExtension, - ); - - this.dependencies.extensionInstances.set(extension.manifest.name, instance); - - return { - instance, - installedExtension, - activated: instance.activate(), - } as ExtensionBeingActivated; - } catch (err) { - this.dependencies.logger.error(`${logModule}: error loading extension`, { ext: extension, err }); - - return null; - } - }) - .filter(isDefined); - } - - protected async loadExtensions(extensions: ExtensionBeingActivated[]): Promise { - // We first need to wait until each extension's `onActivate` is resolved or rejected, - // as this might register new catalog categories. Afterwards we can safely .enable the extension. - await Promise.all( - extensions.map(extension => - // If extension activation fails, log error - extension.activated.catch((error) => { - this.dependencies.logger.error(`${logModule}: activation extension error`, { ext: extension.installedExtension, error }); - }), - ), - ); - - extensions.forEach(({ instance }) => { - const extension = this.dependencies.getExtension(instance); - - extension.register(); - }); - - return extensions.map(extension => { - const loaded = extension.instance.enable().catch((err) => { - this.dependencies.logger.error(`${logModule}: failed to enable`, { ext: extension, err }); - }); - - return { - isBundled: extension.installedExtension.isBundled, - loaded, - }; - }); - } - - protected async loadUserExtensions(installedExtensions: Map) { - // Steps of the function: - // 1. require and call .activate for each Extension - // 2. Wait until every extension's onActivate has been resolved - // 3. Call .enable for each extension - // 4. Return ExtensionLoading[] - - return [...installedExtensions.entries()] - .map(([extId, extension]) => { - const alreadyInit = this.dependencies.extensionInstances.has(extId) || this.nonInstancesByName.has(extension.manifest.name); - - if (extension.isCompatible && extension.isEnabled && !alreadyInit) { - try { - const LensExtensionClass = this.requireExtension(extension); - - if (!LensExtensionClass) { - this.nonInstancesByName.add(extension.manifest.name); - - return null; - } - - const instance = this.dependencies.createExtensionInstance( - LensExtensionClass, - extension, - ); - - this.dependencies.extensionInstances.set(extId, instance); - - return { - instance, - installedExtension: extension, - activated: instance.activate(), - } as ExtensionBeingActivated; - } catch (err) { - this.dependencies.logger.error(`${logModule}: error loading extension`, { ext: extension, err }); - } - } else if (!extension.isEnabled && alreadyInit) { - this.removeInstance(extId); - } - - return null; - }) - .filter(isDefined); - } - - async autoInitExtensions() { - this.dependencies.logger.info(`${logModule}: auto initializing extensions`); - - const bundledExtensions = await this.loadBundledExtensions(); - const userExtensions = await this.loadUserExtensions(this.toJSON()); - const loadedExtensions = await this.loadExtensions([ - ...bundledExtensions, - ...userExtensions, - ]); - - // Setup reaction to load extensions on JSON changes - reaction(() => this.toJSON(), installedExtensions => { - void (async () => { - const userExtensions = await this.loadUserExtensions(installedExtensions); - - await this.loadExtensions(userExtensions); - })(); - }); - - return loadedExtensions; - } - - protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null { - const extRelativePath = extension.manifest[this.dependencies.extensionEntryPointName]; - - if (!extRelativePath) { - return null; - } - - const extAbsolutePath = this.dependencies.joinPaths(this.dependencies.getDirnameOfPath(extension.manifestPath), extRelativePath); - - try { - return require(/* webpackIgnore: true */ extAbsolutePath).default; - } catch (error) { - const message = (error instanceof Error ? error.stack : undefined) || error; - - this.dependencies.logger.error(`${logModule}: can't load ${this.dependencies.extensionEntryPointName} for "${extension.manifest.name}": ${message}`, { extension }); - } - - return null; - } - - getExtension(extId: LensExtensionId) { - return this.extensions.get(extId); - } - - getInstanceById(extId: LensExtensionId) { - return this.dependencies.extensionInstances.get(extId); - } - - toJSON(): Map { - return toJS(this.extensions); - } -} diff --git a/src/extensions/extension-loader/extension-registrator-injection-token.ts b/src/extensions/extension-loader/extension-registrator-injection-token.ts deleted file mode 100644 index 295d3b67a4..0000000000 --- a/src/extensions/extension-loader/extension-registrator-injection-token.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import type { LensExtension } from "../lens-extension"; - -export type ExtensionRegistrator = (extension: LensExtension) => - Injectable[] | IComputedValue[]>; - -export const extensionRegistratorInjectionToken = getInjectionToken({ - id: "extension-registrator-token", -}); diff --git a/src/extensions/extension-loader/extension/extension.injectable.ts b/src/extensions/extension-loader/extension/extension.injectable.ts deleted file mode 100644 index 6b9424cea4..0000000000 --- a/src/extensions/extension-loader/extension/extension.injectable.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { difference, find, map } from "lodash"; -import { reaction, runInAction } from "mobx"; -import { disposer } from "../../../common/utils/disposer"; -import type { LensExtension } from "../../lens-extension"; -import { extensionRegistratorInjectionToken } from "../extension-registrator-injection-token"; - -export interface Extension { - register: () => void; - deregister: () => void; -} - -const idsToInjectables = (ids: string[], injectables: Injectable[]) => ids.map(id => { - const injectable = find(injectables, { id }); - - if (!injectable) { - throw new Error(`Injectable ${id} not found`); - } - - return injectable; -}); - -const extensionInjectable = getInjectable({ - id: "extension", - - instantiate: (parentDi, instance: LensExtension): Extension => { - const extensionInjectable = getInjectable({ - id: `extension-${instance.sanitizedExtensionId}`, - - instantiate: (childDi) => { - const extensionRegistrators = childDi.injectMany(extensionRegistratorInjectionToken); - const reactionDisposer = disposer(); - - return { - register: () => { - extensionRegistrators.forEach((getInjectablesOfExtension) => { - const injectables = getInjectablesOfExtension(instance); - - reactionDisposer.push( - // injectables is either an array or a computed array, in which case - // we need to update the registered injectables with a reaction every time they change - reaction( - () => Array.isArray(injectables) ? injectables : injectables.get(), - (currentInjectables, previousInjectables = []) => { - // Register new injectables and deregister removed injectables by id - const currentIds = map(currentInjectables, "id"); - const previousIds = map(previousInjectables, "id"); - const idsToAdd = difference(currentIds, previousIds); - const idsToRemove = previousIds.filter(previousId => !currentIds.includes(previousId)); - - if (idsToRemove.length > 0) { - childDi.deregister(...idsToInjectables(idsToRemove, previousInjectables)); - } - - if (idsToAdd.length > 0) { - childDi.register(...idsToInjectables(idsToAdd, currentInjectables)); - } - }, { - fireImmediately: true, - }, - )); - }); - }, - - deregister: () => { - reactionDisposer(); - - runInAction(() => { - parentDi.deregister(extensionInjectable); - }); - }, - }; - }, - }); - - runInAction(() => { - parentDi.register(extensionInjectable); - }); - - return parentDi.inject(extensionInjectable); - }, - - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, instance: LensExtension) => instance, - }), -}); - -export default extensionInjectable; diff --git a/src/extensions/extension-loader/file-system-provisioner-store/directory-for-extension-data.injectable.ts b/src/extensions/extension-loader/file-system-provisioner-store/directory-for-extension-data.injectable.ts deleted file mode 100644 index 469ed85949..0000000000 --- a/src/extensions/extension-loader/file-system-provisioner-store/directory-for-extension-data.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import joinPathsInjectable from "../../../common/path/join-paths.injectable"; - -const directoryForExtensionDataInjectable = getInjectable({ - id: "directory-for-extension-data", - - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const directoryForUserData = di.inject(directoryForUserDataInjectable); - - return joinPaths(directoryForUserData, "extension_data"); - }, -}); - -export default directoryForExtensionDataInjectable; diff --git a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts b/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts deleted file mode 100644 index b511437da9..0000000000 --- a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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 { FileSystemProvisionerStore } from "./file-system-provisioner-store"; -import directoryForExtensionDataInjectable from "./directory-for-extension-data.injectable"; -import ensureDirectoryInjectable from "../../../common/fs/ensure-dir.injectable"; -import joinPathsInjectable from "../../../common/path/join-paths.injectable"; -import randomBytesInjectable from "../../../common/utils/random-bytes.injectable"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../../../common/base-store/save-to-file"; -import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../../../common/utils/channel/enlist-message-channel-listener-injection-token"; - -const fileSystemProvisionerStoreInjectable = getInjectable({ - id: "file-system-provisioner-store", - - instantiate: (di) => new FileSystemProvisionerStore({ - directoryForExtensionData: di.inject(directoryForExtensionDataInjectable), - ensureDirectory: di.inject(ensureDirectoryInjectable), - joinPaths: di.inject(joinPathsInjectable), - randomBytes: di.inject(randomBytesInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: {}, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default fileSystemProvisionerStoreInjectable; diff --git a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts b/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts deleted file mode 100644 index 5655a0cf06..0000000000 --- a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { SHA256 } from "crypto-js"; -import { action, makeObservable, observable } from "mobx"; -import type { BaseStoreDependencies } from "../../../common/base-store/base-store"; -import { BaseStore } from "../../../common/base-store/base-store"; -import type { LensExtensionId } from "../../lens-extension"; -import { getOrInsertWithAsync, toJS } from "../../../common/utils"; -import type { EnsureDirectory } from "../../../common/fs/ensure-dir.injectable"; -import type { JoinPaths } from "../../../common/path/join-paths.injectable"; -import type { RandomBytes } from "../../../common/utils/random-bytes.injectable"; - -interface FSProvisionModel { - extensions: Record; // extension names to paths -} - -interface Dependencies extends BaseStoreDependencies { - readonly directoryForExtensionData: string; - ensureDirectory: EnsureDirectory; - joinPaths: JoinPaths; - randomBytes: RandomBytes; -} - -export class FileSystemProvisionerStore extends BaseStore { - readonly registeredExtensions = observable.map(); - - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-filesystem-provisioner-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - }); - - makeObservable(this); - } - - /** - * This function retrieves the saved path to the folder which the extension - * can saves files to. If the folder is not present then it is created. - * @param extensionName the name of the extension requesting the path - * @returns path to the folder that the extension can safely write files to. - */ - async requestDirectory(extensionName: string): Promise { - const dirPath = await getOrInsertWithAsync(this.registeredExtensions, extensionName, async () => { - const salt = (await this.dependencies.randomBytes(32)).toString("hex"); - const hashedName = SHA256(`${extensionName}/${salt}`).toString(); - - return this.dependencies.joinPaths(this.dependencies.directoryForExtensionData, hashedName); - }); - - await this.dependencies.ensureDirectory(dirPath); - - return dirPath; - } - - @action - protected fromStore({ extensions }: FSProvisionModel = { extensions: {}}): void { - this.registeredExtensions.merge(extensions); - } - - toJSON(): FSProvisionModel { - return toJS({ - extensions: Object.fromEntries(this.registeredExtensions), - }); - } -} diff --git a/src/extensions/extension-loader/index.ts b/src/extensions/extension-loader/index.ts deleted file mode 100644 index a1dab46d3b..0000000000 --- a/src/extensions/extension-loader/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./extension-loader"; diff --git a/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts b/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts deleted file mode 100644 index 754375dd5a..0000000000 --- a/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable"; - -const updateExtensionsStateInjectable = getInjectable({ - id: "update-extensions-state", - instantiate: (di) => di.inject(extensionsStoreInjectable).mergeState, -}); - -export default updateExtensionsStateInjectable; diff --git a/src/extensions/extension-packages-root/extension-packages-root.injectable.ts b/src/extensions/extension-packages-root/extension-packages-root.injectable.ts deleted file mode 100644 index 81e91c5e27..0000000000 --- a/src/extensions/extension-packages-root/extension-packages-root.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 directoryForUserDataInjectable - from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; - -const extensionPackagesRootInjectable = getInjectable({ - id: "extension-packages-root", - instantiate: (di) => di.inject(directoryForUserDataInjectable), -}); - -export default extensionPackagesRootInjectable; diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts deleted file mode 100644 index 271175f0a4..0000000000 --- a/src/extensions/extension-store.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { BaseStoreParams } from "../common/base-store/base-store"; -import { BaseStore } from "../common/base-store/base-store"; -import * as path from "path"; -import type { LensExtension } from "./lens-extension"; -import assert from "assert"; -import type { StaticThis } from "../common/utils"; -import { getOrInsertWith } from "../common/utils"; -import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../common/logger.injectable"; -import storeMigrationVersionInjectable from "../common/vars/store-migration-version.injectable"; -import type { Migrations } from "conf/dist/source/types"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../common/base-store/save-to-file"; -import getBasenameOfPathInjectable from "../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../common/utils/channel/enlist-message-channel-listener-injection-token"; - -export interface ExtensionStoreParams extends BaseStoreParams { - migrations?: Migrations; -} - -export abstract class ExtensionStore extends BaseStore { - private static readonly instances = new WeakMap(); - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static createInstance(this: StaticThis, ...args: R): T { - return getOrInsertWith(ExtensionStore.instances, this, () => new this(...args)) as T; - } - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static getInstance(this: StaticThis, strict?: true): T; - static getInstance(this: StaticThis, strict: false): T | undefined; - static getInstance(this: StaticThis, strict = true): T | undefined { - if (!ExtensionStore.instances.has(this) && strict) { - throw new TypeError(`instance of ${this.name} is not created`); - } - - return ExtensionStore.instances.get(this) as (T | undefined); - } - - constructor({ migrations, ...params }: ExtensionStoreParams) { - const di = getLegacyGlobalDiForExtensionApi(); - - super({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: migrations as Migrations>, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }, params); - } - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static resetInstance() { - ExtensionStore.instances.delete(this); - } - - protected extension?: LensExtension; - - loadExtension(extension: LensExtension) { - this.extension = extension; - - this.params.projectVersion ??= this.extension.version; - - return super.load(); - } - - load() { - if (!this.extension) { return; } - - return super.load(); - } - - protected cwd() { - assert(this.extension, "must call this.load() first"); - - return path.join(super.cwd(), "extension-store", this.extension.name); - } -} diff --git a/src/extensions/extensions-store/extensions-store.injectable.ts b/src/extensions/extensions-store/extensions-store.injectable.ts deleted file mode 100644 index 9f5ff83270..0000000000 --- a/src/extensions/extensions-store/extensions-store.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file"; -import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "../../common/utils/channel/enlist-message-channel-listener-injection-token"; -import storeMigrationVersionInjectable from "../../common/vars/store-migration-version.injectable"; -import { ExtensionsStore } from "./extensions-store"; - -const extensionsStoreInjectable = getInjectable({ - id: "extensions-store", - instantiate: (di) => new ExtensionsStore({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: {}, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default extensionsStoreInjectable; diff --git a/src/extensions/extensions-store/extensions-store.ts b/src/extensions/extensions-store/extensions-store.ts deleted file mode 100644 index 3b2dc80eb1..0000000000 --- a/src/extensions/extensions-store/extensions-store.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LensExtensionId } from "../lens-extension"; -import { action, computed, makeObservable, observable } from "mobx"; -import { toJS } from "../../common/utils"; -import type { BaseStoreDependencies } from "../../common/base-store/base-store"; -import { BaseStore } from "../../common/base-store/base-store"; - -export interface LensExtensionsStoreModel { - extensions: Record; -} - -export interface LensExtensionState { - enabled?: boolean; - name: string; -} - -export interface IsEnabledExtensionDescriptor { - id: string; - isBundled: boolean; -} - -export class ExtensionsStore extends BaseStore { - constructor(deps: BaseStoreDependencies) { - super(deps, { - configName: "lens-extensions", - }); - makeObservable(this); - this.load(); - } - - @computed - get enabledExtensions() { - return Array.from(this.state.values()) - .filter(({ enabled }) => enabled) - .map(({ name }) => name); - } - - protected state = observable.map(); - - isEnabled({ id, isBundled }: IsEnabledExtensionDescriptor): boolean { - // By default false, so that copied extensions are disabled by default. - // If user installs the extension from the UI, the Extensions component will specifically enable it. - return isBundled || Boolean(this.state.get(id)?.enabled); - } - - mergeState = action((extensionsState: Record | [LensExtensionId, LensExtensionState][]) => { - this.state.merge(extensionsState); - }); - - @action - protected fromStore({ extensions }: LensExtensionsStoreModel) { - this.state.merge(extensions); - } - - toJSON(): LensExtensionsStoreModel { - return toJS({ - extensions: Object.fromEntries(this.state), - }); - } -} diff --git a/src/extensions/extensions.injectable.ts b/src/extensions/extensions.injectable.ts deleted file mode 100644 index 7cc019a318..0000000000 --- a/src/extensions/extensions.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 extensionInstancesInjectable from "./extension-loader/extension-instances.injectable"; - -const extensionsInjectable = getInjectable({ - id: "extensions", - instantiate: (di) => { - const extensionInstances = di.inject(extensionInstancesInjectable); - - return computed(() => [...extensionInstances.values()].filter(extension => extension.isEnabled)); - }, -}); - -export default extensionsInjectable; diff --git a/src/extensions/ipc/ipc-main.ts b/src/extensions/ipc/ipc-main.ts deleted file mode 100644 index cd18bcc56c..0000000000 --- a/src/extensions/ipc/ipc-main.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { ipcMain } from "electron"; -import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; -import { Disposers, lensExtensionDependencies } from "../lens-extension"; -import type { LensMainExtension } from "../lens-main-extension"; -import type { Disposer } from "../../common/utils"; -import { once } from "lodash"; -import { ipcMainHandle } from "../../common/ipc"; - -export abstract class IpcMain extends IpcRegistrar { - constructor(extension: LensMainExtension) { - super(extension); - - // Call the static method on the bottom child class. - extension[Disposers].push(() => (this.constructor as typeof IpcMain).resetInstance()); - } - - /** - * Listen for broadcasts within your extension - * @param channel The channel to listen for broadcasts on - * @param listener The function that will be called with the arguments of the broadcast - * @returns An optional disposer, Lens will cleanup when the extension is disabled or uninstalled even if this is not called - */ - listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { - const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - const cleanup = once(() => { - this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - - return ipcMain.removeListener(prefixedChannel, listener); - }); - - this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - ipcMain.addListener(prefixedChannel, listener); - this.extension[Disposers].push(cleanup); - - return cleanup; - } - - /** - * Declare a RPC over `channel`. Lens will cleanup when the extension is disabled or uninstalled - * @param channel The name of the RPC - * @param handler The remote procedure that is called - */ - handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { - const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - - this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - ipcMainHandle(prefixedChannel, handler); - this.extension[Disposers].push(() => { - this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - - return ipcMain.removeHandler(prefixedChannel); - }); - } -} diff --git a/src/extensions/ipc/ipc-registrar.ts b/src/extensions/ipc/ipc-registrar.ts deleted file mode 100644 index 04222bd64b..0000000000 --- a/src/extensions/ipc/ipc-registrar.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { Singleton } from "../../common/utils"; -import type { LensExtension } from "../lens-extension"; -import { createHash } from "crypto"; -import { broadcastMessage } from "../../common/ipc"; - -export const IpcPrefix = Symbol(); - -export abstract class IpcRegistrar extends Singleton { - readonly [IpcPrefix]: string; - - constructor(protected readonly extension: LensExtension) { - super(); - this[IpcPrefix] = createHash("sha256").update(extension.id).digest("hex"); - } - - /** - * - * @param channel The channel to broadcast to your whole extension, both `main` and `renderer` - * @param args The arguments passed to all listeners - */ - broadcast(channel: string, ...args: any[]): void { - broadcastMessage(`extensions@${this[IpcPrefix]}:${channel}`, ...args); - } -} diff --git a/src/extensions/ipc/ipc-renderer.ts b/src/extensions/ipc/ipc-renderer.ts deleted file mode 100644 index 71b67779f5..0000000000 --- a/src/extensions/ipc/ipc-renderer.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { ipcRenderer } from "electron"; -import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; -import { Disposers } from "../lens-extension"; -import type { LensRendererExtension } from "../lens-renderer-extension"; -import type { Disposer } from "../../common/utils"; -import { once } from "lodash"; - -export abstract class IpcRenderer extends IpcRegistrar { - constructor(extension: LensRendererExtension) { - super(extension); - - // Call the static method on the bottom child class. - extension[Disposers].push(() => (this.constructor as typeof IpcRenderer).resetInstance()); - } - - /** - * Listen for broadcasts within your extension. - * If the lifetime of the listener should be tied to the mounted lifetime of - * a component then putting the returned value in a `disposeOnUnmount` call will suffice. - * @param channel The channel to listen for broadcasts on - * @param listener The function that will be called with the arguments of the broadcast - * @returns An optional disposer, Lens will cleanup even if this is not called - */ - listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { - const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - const cleanup = once(() => { - console.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - - return ipcRenderer.removeListener(prefixedChannel, listener); - }); - - console.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); - ipcRenderer.addListener(prefixedChannel, listener); - this.extension[Disposers].push(cleanup); - - return cleanup; - } - - /** - * Request main to execute its function over the `channel` channel. - * This function only interacts with functions registered via `Ipc.IpcMain.handleRpc` - * An error will be thrown if no function has been registered on `main` with this channel ID. - * @param channel The channel to invoke a RPC on - * @param args The arguments to pass to the RPC - * @returns A promise of the resulting value - */ - invoke(channel: string, ...args: any[]): Promise { - const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - - return ipcRenderer.invoke(prefixedChannel, ...args); - } -} diff --git a/src/extensions/lens-extension-set-dependencies.ts b/src/extensions/lens-extension-set-dependencies.ts deleted file mode 100644 index 7b7c62597a..0000000000 --- a/src/extensions/lens-extension-set-dependencies.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IComputedValue } from "mobx"; -import type { CatalogCategoryRegistry } from "../common/catalog"; -import type { NavigateToRoute } from "../common/front-end-routing/navigate-to-route-injection-token"; -import type { Route } from "../common/front-end-routing/front-end-route-injection-token"; -import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog"; -import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../renderer/api/catalog/entity/registry"; -import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable"; -import type { FileSystemProvisionerStore } from "./extension-loader/file-system-provisioner-store/file-system-provisioner-store"; -import type { NavigateForExtension } from "../main/start-main-application/lens-window/navigate-for-extension.injectable"; -import type { Logger } from "../common/logger"; - -export interface LensExtensionDependencies { - readonly fileSystemProvisionerStore: FileSystemProvisionerStore; - readonly logger: Logger; -} - -export interface LensMainExtensionDependencies extends LensExtensionDependencies { - readonly entityRegistry: MainCatalogEntityRegistry; - readonly navigate: NavigateForExtension; -} - -export interface LensRendererExtensionDependencies extends LensExtensionDependencies { - navigateToRoute: NavigateToRoute; - getExtensionPageParameters: GetExtensionPageParameters; - readonly routes: IComputedValue[]>; - readonly entityRegistry: RendererCatalogEntityRegistry; - readonly categoryRegistry: CatalogCategoryRegistry; -} diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts deleted file mode 100644 index 831822fd2b..0000000000 --- a/src/extensions/lens-extension.ts +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { InstalledExtension } from "./extension-discovery/extension-discovery"; -import { action, computed, makeObservable, observable } from "mobx"; -import type { PackageJson } from "type-fest"; -import { disposer } from "../common/utils"; -import type { LensExtensionDependencies } from "./lens-extension-set-dependencies"; -import type { ProtocolHandlerRegistration } from "./common-api/registrations"; - -export type LensExtensionId = string; // path to manifest (package.json) -export type LensExtensionConstructor = new (...args: ConstructorParameters) => LensExtension; - -export interface LensExtensionManifest extends PackageJson { - name: string; - version: string; - main?: string; // path to %ext/dist/main.js - renderer?: string; // path to %ext/dist/renderer.js - /** - * Supported Lens version engine by extension could be defined in `manifest.engines.lens` - * Only MAJOR.MINOR version is taken in consideration. - */ - engines: { - lens: string; // "semver"-package format - npm?: string; - node?: string; - }; -} - -export const lensExtensionDependencies = Symbol("lens-extension-dependencies"); -export const Disposers = Symbol("disposers"); - -export class LensExtension { - readonly id: LensExtensionId; - readonly manifest: LensExtensionManifest; - readonly manifestPath: string; - readonly isBundled: boolean; - - get sanitizedExtensionId() { - return sanitizeExtensionName(this.name); - } - - protocolHandlers: ProtocolHandlerRegistration[] = []; - - @observable private _isEnabled = false; - - @computed get isEnabled() { - return this._isEnabled; - } - - [Disposers] = disposer(); - - constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) { - makeObservable(this); - this.id = id; - this.manifest = manifest; - this.manifestPath = manifestPath; - this.isBundled = !!isBundled; - } - - get name() { - return this.manifest.name; - } - - get version() { - return this.manifest.version; - } - - get description() { - return this.manifest.description; - } - - readonly [lensExtensionDependencies]!: Dependencies; - - /** - * getExtensionFileFolder returns the path to an already created folder. This - * folder is for the sole use of this extension. - * - * Note: there is no security done on this folder, only obfuscation of the - * folder name. - */ - async getExtensionFileFolder(): Promise { - return this[lensExtensionDependencies].fileSystemProvisionerStore.requestDirectory(this.id); - } - - @action - async enable() { - if (this._isEnabled) { - return; - } - - this._isEnabled = true; - this[lensExtensionDependencies].logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`); - } - - @action - async disable() { - if (!this._isEnabled) { - return; - } - - this._isEnabled = false; - - try { - await this.onDeactivate(); - this[Disposers](); - this[lensExtensionDependencies].logger.info(`[EXTENSION]: disabled ${this.name}@${this.version}`); - } catch (error) { - this[lensExtensionDependencies].logger.error(`[EXTENSION]: disabling ${this.name}@${this.version} threw an error: ${error}`); - } - } - - async activate(): Promise { - return this.onActivate(); - } - - protected onActivate(): Promise | void { - return; - } - - protected onDeactivate(): Promise | void { - return; - } -} - -export function sanitizeExtensionName(name: string) { - return name.replace("@", "").replace("/", "--"); -} - -export function getSanitizedPath(...parts: string[]) { - return parts - .filter(Boolean) - .join("/") - .replace(/\/+/g, "/") - .replace(/\/$/, ""); -} // normalize multi-slashes (e.g. coming from page.id) - -export function extensionDisplayName(name: string, version: string) { - return `${name}@${version}`; -} diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts deleted file mode 100644 index 63daab9ba4..0000000000 --- a/src/extensions/lens-main-extension.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { LensExtension, lensExtensionDependencies } from "./lens-extension"; -import type { CatalogEntity } from "../common/catalog"; -import type { IComputedValue, IObservableArray } from "mobx"; -import type { MenuRegistration } from "../features/application-menu/main/menu-registration"; -import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration"; -import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; -import type { LensMainExtensionDependencies } from "./lens-extension-set-dependencies"; - -export class LensMainExtension extends LensExtension { - appMenus: MenuRegistration[] | IComputedValue = []; - trayMenus: TrayMenuRegistration[] | IComputedValue = []; - - /** - * implement this to modify the shell environment that Lens terminals are opened with. The ShellEnvModifier type has the signature - * - * (ctx: ShellEnvContext, env: Record) => Record - * - * @param ctx the shell environment context, specifically the relevant catalog entity for the terminal. This can be used, for example, to get - * cluster-specific information that can be made available in the shell environment by the implementation of terminalShellEnvModifier - * - * @param env the current shell environment that the terminal will be opened with. The implementation should modify this as desired. - * - * @returns the modified shell environment that the terminal will be opened with. The implementation must return env as passed in, if it - * does not modify the shell environment - */ - terminalShellEnvModifier?: ShellEnvModifier; - - async navigate(pageId?: string, params?: Record, frameId?: number) { - await this[lensExtensionDependencies].navigate(this.id, pageId, params, frameId); - } - - addCatalogSource(id: string, source: IObservableArray) { - this[lensExtensionDependencies].entityRegistry.addObservableSource(`${this.name}:${id}`, source); - } - - removeCatalogSource(id: string) { - this[lensExtensionDependencies].entityRegistry.removeSource(`${this.name}:${id}`); - } -} diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts deleted file mode 100644 index b1b024c091..0000000000 --- a/src/extensions/lens-renderer-extension.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { Disposers, LensExtension, lensExtensionDependencies } from "./lens-extension"; -import type { CatalogEntity, CategoryFilter } from "../common/catalog"; -import type { Disposer } from "../common/utils"; -import type { EntityFilter } from "../renderer/api/catalog/entity/registry"; -import type { TopBarRegistration } from "../renderer/components/layout/top-bar/top-bar-registration"; -import type { KubernetesCluster } from "../common/catalog-entities"; -import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/welcome-menu-items/welcome-menu-registration"; -import type { WelcomeBannerRegistration } from "../renderer/components/+welcome/welcome-banner-items/welcome-banner-registration"; -import type { CommandRegistration } from "../renderer/components/command-palette/registered-commands/commands"; -import type { AppPreferenceRegistration } from "../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-registration"; -import type { AdditionalCategoryColumnRegistration } from "../renderer/components/+catalog/custom-category-columns"; -import type { CustomCategoryViewRegistration } from "../renderer/components/+catalog/custom-views"; -import type { StatusBarRegistration } from "../renderer/components/status-bar/status-bar-registration"; -import type { KubeObjectMenuRegistration } from "../renderer/components/kube-object-menu/kube-object-menu-registration"; -import type { WorkloadsOverviewDetailRegistration } from "../renderer/components/+workloads-overview/workloads-overview-detail-registration"; -import type { KubeObjectStatusRegistration } from "../renderer/components/kube-object-status-icon/kube-object-status-registration"; -import { fromPairs, map, matches, toPairs } from "lodash/fp"; -import { pipeline } from "@ogre-tools/fp"; -import { getExtensionRoutePath } from "../renderer/routes/for-extension"; -import type { LensRendererExtensionDependencies } from "./lens-extension-set-dependencies"; -import type { KubeObjectHandlerRegistration } from "../renderer/kube-object/handler"; -import type { AppPreferenceTabRegistration } from "../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-tab-registration"; -import type { KubeObjectDetailRegistration } from "../renderer/components/kube-object-details/kube-object-detail-registration"; -import type { ClusterFrameChildComponent } from "../renderer/frames/cluster-frame/cluster-frame-child-component-injection-token"; -import type { EntitySettingRegistration } from "../renderer/components/+entity-settings/extension-registrator.injectable"; -import type { CatalogEntityDetailRegistration } from "../renderer/components/+catalog/entity-details/token"; -import type { ClusterPageMenuRegistration, PageRegistration } from "./common-api/registrations"; - -export class LensRendererExtension extends LensExtension { - globalPages: PageRegistration[] = []; - clusterPages: PageRegistration[] = []; - clusterPageMenus: ClusterPageMenuRegistration[] = []; - clusterFrameComponents: ClusterFrameChildComponent[] = []; - kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []; - appPreferences: AppPreferenceRegistration[] = []; - appPreferenceTabs: AppPreferenceTabRegistration[] = []; - entitySettings: EntitySettingRegistration[] = []; - statusBarItems: StatusBarRegistration[] = []; - kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; - kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; - kubeWorkloadsOverviewItems: WorkloadsOverviewDetailRegistration[] = []; - commands: CommandRegistration[] = []; - welcomeMenus: WelcomeMenuRegistration[] = []; - welcomeBanners: WelcomeBannerRegistration[] = []; - catalogEntityDetailItems: CatalogEntityDetailRegistration[] = []; - topBarItems: TopBarRegistration[] = []; - additionalCategoryColumns: AdditionalCategoryColumnRegistration[] = []; - customCategoryViews: CustomCategoryViewRegistration[] = []; - kubeObjectHandlers: KubeObjectHandlerRegistration[] = []; - - async navigate(pageId?: string, params: object = {}) { - const routes = this[lensExtensionDependencies].routes.get(); - const targetRegistration = [...this.globalPages, ...this.clusterPages] - .find(registration => registration.id === (pageId || undefined)); - - if (!targetRegistration) { - return; - } - - const targetRoutePath = getExtensionRoutePath(this, targetRegistration.id); - const targetRoute = routes.find(matches({ path: targetRoutePath })); - - if (!targetRoute) { - return; - } - - const normalizedParams = this[lensExtensionDependencies].getExtensionPageParameters({ - extension: this, - registration: targetRegistration, - }); - const query = pipeline( - params, - toPairs, - map(([key, value]) => [ - key, - normalizedParams[key].stringify(value), - ]), - fromPairs, - ); - - this[lensExtensionDependencies].navigateToRoute(targetRoute, { - query, - }); - } - - /** - * Defines if extension is enabled for a given cluster. This method is only - * called when the extension is created within a cluster frame. - * - * The default implementation is to return `true` - * - * @deprecated Switch to using "enabled" or "visible" properties in each registration together with `activeCluster` - */ - async isEnabledForCluster(cluster: KubernetesCluster): Promise { - return (void cluster) || true; - } - - /** - * Add a filtering function for the catalog entities. This will be removed if the extension is disabled. - * @param fn The function which should return a truthy value for those entities which should be kept. - * @returns A function to clean up the filter - */ - addCatalogFilter(fn: EntityFilter): Disposer { - const dispose = this[lensExtensionDependencies].entityRegistry.addCatalogFilter(fn); - - this[Disposers].push(dispose); - - return dispose; - } - - /** - * Add a filtering function for the catalog categories. This will be removed if the extension is disabled. - * @param fn The function which should return a truthy value for those categories which should be kept. - * @returns A function to clean up the filter - */ - addCatalogCategoryFilter(fn: CategoryFilter): Disposer { - const dispose = this[lensExtensionDependencies].categoryRegistry.addCatalogCategoryFilter(fn); - - this[Disposers].push(dispose); - - return dispose; - } -} diff --git a/src/extensions/main-api/catalog.ts b/src/extensions/main-api/catalog.ts deleted file mode 100644 index 07adbe9d4b..0000000000 --- a/src/extensions/main-api/catalog.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CatalogEntity } from "../../common/catalog"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; -import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable"; - -export const catalogCategories = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable); -const catalogEntityRegistry = asLegacyGlobalForExtensionApi(catalogEntityRegistryInjectable); - -export interface CatalogEntityRegistry { - getItemsForApiKind(apiVersion: string, kind: string): CatalogEntity[]; - /** - * @deprecated use a cast instead of a unbounded type parameter - */ - getItemsForApiKind(apiVersion: string, kind: string): T[]; -} - -export const catalogEntities: CatalogEntityRegistry = { - getItemsForApiKind(apiVersion: string, kind: string) { - return catalogEntityRegistry.filterItemsForApiKind(apiVersion, kind); - }, -}; diff --git a/src/extensions/main-api/index.ts b/src/extensions/main-api/index.ts deleted file mode 100644 index 9f39a4c781..0000000000 --- a/src/extensions/main-api/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as Catalog from "./catalog"; -import * as Navigation from "./navigation"; -import * as K8sApi from "./k8s-api"; -import * as Power from "./power"; -import { IpcMain as Ipc } from "../ipc/ipc-main"; -import { LensMainExtension as LensExtension } from "../lens-main-extension"; - -export { - Catalog, - Navigation, - K8sApi, - Ipc, - LensExtension, - Power, -}; diff --git a/src/extensions/main-api/k8s-api.ts b/src/extensions/main-api/k8s-api.ts deleted file mode 100644 index 05cffffdbf..0000000000 --- a/src/extensions/main-api/k8s-api.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * @deprecated This function never works - * @returns false - */ -export function isAllowedResource(...args: any[]) { - return Boolean(void args); -} - -export * from "../common-api/k8s-api"; diff --git a/src/extensions/main-api/navigation.ts b/src/extensions/main-api/navigation.ts deleted file mode 100644 index 375d2bac74..0000000000 --- a/src/extensions/main-api/navigation.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { - Environments, - getEnvironmentSpecificLegacyGlobalDiForExtensionApi, -} from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; - -import navigateInjectable from "../../main/start-main-application/lens-window/navigate.injectable"; - -export function navigate(url: string) { - const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); - - const navigate = di.inject(navigateInjectable); - - return navigate(url); -} diff --git a/src/extensions/main-api/power.ts b/src/extensions/main-api/power.ts deleted file mode 100644 index 671308798a..0000000000 --- a/src/extensions/main-api/power.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { powerMonitor } from "electron"; -import type { Disposer } from "../../common/utils/disposer"; - -/** - * Event listener for system power events - */ -export type PowerEventListener = () => void; - -/** - * Adds event listener to system suspend events - * @param listener function which will be called on system suspend - * @returns function to remove event listener - */ -export const onSuspend = (listener: PowerEventListener): Disposer => { - powerMonitor.on("suspend", listener); - - return () => { - powerMonitor.off("suspend", listener); - }; -}; - -/** - * Adds event listener to system resume event - * @param listener function which will be called on system resume - * @returns function to remove event listener - */ -export const onResume = (listener: PowerEventListener): Disposer => { - powerMonitor.on("resume", listener); - - return () => { - powerMonitor.off("resume", listener); - }; -}; - -/** - * Adds event listener to the event which is emitted when - * the system is about to reboot or shut down - * @param listener function which will be called on system shutdown - * @returns function to remove event listener - */ -export const onShutdown = (listener: PowerEventListener): Disposer => { - powerMonitor.on("shutdown", listener); - - return () => { - powerMonitor.off("shutdown", listener); - }; -}; diff --git a/src/extensions/main-extensions.injectable.ts b/src/extensions/main-extensions.injectable.ts deleted file mode 100644 index 07010af010..0000000000 --- a/src/extensions/main-extensions.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 { IComputedValue } from "mobx"; -import extensionsInjectable from "./extensions.injectable"; -import type { LensMainExtension } from "./lens-main-extension"; - -const mainExtensionsInjectable = getInjectable({ - id: "main-extensions", - - instantiate: (di) => - di.inject(extensionsInjectable) as IComputedValue, -}); - -export default mainExtensionsInjectable; diff --git a/src/extensions/renderer-api/catalog.ts b/src/extensions/renderer-api/catalog.ts deleted file mode 100644 index 43b840a39a..0000000000 --- a/src/extensions/renderer-api/catalog.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -import type { CatalogCategory, CatalogEntity } from "../../common/catalog"; -import type { CatalogEntityOnBeforeRun } from "../../renderer/api/catalog/entity/registry"; -import type { Disposer } from "../../common/utils"; -import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable"; -import activeKubernetesClusterInjectable from "../../renderer/cluster-frame-context/active-kubernetes-cluster.injectable"; - -export const catalogCategories = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable); - -const internalEntityRegistry = asLegacyGlobalForExtensionApi(catalogEntityRegistryInjectable); - -export class CatalogEntityRegistry { - /** - * Currently active/visible entity - */ - get activeEntity() { - return internalEntityRegistry.activeEntity; - } - - get entities(): Map { - return internalEntityRegistry.entities; - } - - getById(id: string) { - return this.entities.get(id); - } - - getItemsForApiKind(apiVersion: string, kind: string): T[] { - return internalEntityRegistry.getItemsForApiKind(apiVersion, kind); - } - - getItemsForCategory(category: CatalogCategory): T[] { - return internalEntityRegistry.getItemsForCategory(category); - } - - /** - * Add a onBeforeRun hook to a catalog entities. If `onBeforeRun` was previously - * added then it will not be added again. - * @param onBeforeRun The function to be called with a `CatalogRunEvent` - * event target will be the catalog entity. onBeforeRun hook can call event.preventDefault() - * to stop run sequence - * @returns A function to remove that hook - */ - addOnBeforeRun(onBeforeRun: CatalogEntityOnBeforeRun): Disposer { - return internalEntityRegistry.addOnBeforeRun(onBeforeRun); - } -} - -export const catalogEntities = new CatalogEntityRegistry(); - -export const activeCluster = asLegacyGlobalForExtensionApi( - activeKubernetesClusterInjectable, -); diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts deleted file mode 100644 index e4102433bf..0000000000 --- a/src/extensions/renderer-api/components.ts +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import createTerminalTabInjectable from "../../renderer/components/dock/terminal/create-terminal-tab.injectable"; -import terminalStoreInjectable from "../../renderer/components/dock/terminal/store.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import logTabStoreInjectable from "../../renderer/components/dock/logs/tab-store.injectable"; - -import commandOverlayInjectable from "../../renderer/components/command-palette/command-overlay.injectable"; -import createPodLogsTabInjectable from "../../renderer/components/dock/logs/create-pod-logs-tab.injectable"; -import createWorkloadLogsTabInjectable from "../../renderer/components/dock/logs/create-workload-logs-tab.injectable"; -import sendCommandInjectable from "../../renderer/components/dock/terminal/send-command.injectable"; -import renameTabInjectable from "../../renderer/components/dock/dock/rename-tab.injectable"; -import { asLegacyGlobalObjectForExtensionApiWithModifications } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications"; -import { ConfirmDialog as _ConfirmDialog } from "../../renderer/components/confirm-dialog"; -import type { ConfirmDialogBooleanParams, ConfirmDialogParams, ConfirmDialogProps } from "../../renderer/components/confirm-dialog"; -import openConfirmDialogInjectable from "../../renderer/components/confirm-dialog/open.injectable"; -import confirmInjectable from "../../renderer/components/confirm-dialog/confirm.injectable"; -import notificationsStoreInjectable from "../../renderer/components/notifications/notifications-store.injectable"; -import podStoreInjectable from "../../renderer/components/+workloads-pods/store.injectable"; -import getDetailsUrlInjectable from "../../renderer/components/kube-detail-params/get-details-url.injectable"; -import showDetailsInjectable from "../../renderer/components/kube-detail-params/show-details.injectable"; -import showCheckedErrorNotificationInjectable from "../../renderer/components/notifications/show-checked-error.injectable"; -import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable"; -import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable"; -import showShortInfoNotificationInjectable from "../../renderer/components/notifications/show-short-info.injectable"; -import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable"; - -// layouts -export * from "../../renderer/components/layout/main-layout"; -export * from "../../renderer/components/layout/setting-layout"; -export * from "../../renderer/components/layout/page-layout"; -export * from "../../renderer/components/layout/wizard-layout"; -export * from "../../renderer/components/layout/tab-layout"; - -// form-controls -export * from "../../renderer/components/button"; -export * from "../../renderer/components/checkbox"; -export * from "../../renderer/components/radio"; -export * from "../../renderer/components/select"; -export * from "../../renderer/components/slider"; -export * from "../../renderer/components/switch"; -export * from "../../renderer/components/input/input"; - -// command-overlay -export const CommandOverlay = asLegacyGlobalForExtensionApi(commandOverlayInjectable); - -export type { - CategoryColumnRegistration, - AdditionalCategoryColumnRegistration, -} from "../../renderer/components/+catalog/custom-category-columns"; - -// other components -export type { - ConfirmDialogBooleanParams, - ConfirmDialogParams, - ConfirmDialogProps, -}; -export const ConfirmDialog = Object.assign(_ConfirmDialog, { - open: asLegacyGlobalFunctionForExtensionApi(openConfirmDialogInjectable), - confirm: asLegacyGlobalFunctionForExtensionApi(confirmInjectable), -}); - -export * from "../../renderer/components/icon"; -export * from "../../renderer/components/tooltip"; -export * from "../../renderer/components/tabs"; -export * from "../../renderer/components/table"; -export * from "../../renderer/components/badge"; -export * from "../../renderer/components/drawer"; -export * from "../../renderer/components/dialog"; -export * from "../../renderer/components/line-progress"; -export * from "../../renderer/components/menu"; - -export { - NotificationStatus, - type CreateNotificationOptions, - type Notification, - type NotificationId, - type NotificationMessage, - type ShowNotification, - type NotificationsStore, -} from "../../renderer/components/notifications"; - -export const Notifications = { - ok: asLegacyGlobalFunctionForExtensionApi(showSuccessNotificationInjectable), - error: asLegacyGlobalFunctionForExtensionApi(showErrorNotificationInjectable), - checkedError: asLegacyGlobalFunctionForExtensionApi(showCheckedErrorNotificationInjectable), - info: asLegacyGlobalFunctionForExtensionApi(showInfoNotificationInjectable), - shortInfo: asLegacyGlobalFunctionForExtensionApi(showShortInfoNotificationInjectable), -}; - -export * from "../../renderer/components/spinner"; -export * from "../../renderer/components/stepper"; -export * from "../../renderer/components/wizard"; -export * from "../../renderer/components/+workloads-pods/pod-details-list"; -export * from "../../renderer/components/+namespaces/namespace-select"; -export * from "../../renderer/components/+namespaces/namespace-select-filter"; -export * from "../../renderer/components/layout/sub-title"; -export * from "../../renderer/components/input/search-input"; -export * from "../../renderer/components/chart/bar-chart"; -export * from "../../renderer/components/chart/pie-chart"; -export { - MonacoEditor, - type MonacoEditorProps, type MonacoEditorId, - type MonacoTheme, type MonacoCustomTheme, -} from "../../renderer/components/monaco-editor"; - -/** - * @deprecated Use `Renderer.Navigation.getDetailsUrl` - */ -export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable); - -/** - * @deprecated Use `Renderer.Navigation.showDetails` - */ -export const showDetails = asLegacyGlobalFunctionForExtensionApi(showDetailsInjectable); - -// kube helpers -export * from "../../renderer/components/kube-object-details"; -export * from "../../renderer/components/kube-object-list-layout"; -export * from "../../renderer/components/kube-object-menu"; -export * from "../../renderer/components/kube-object-meta"; -export * from "../../renderer/components/+events/kube-event-details"; - -// specific exports -export * from "../../renderer/components/status-brick"; - -export const createTerminalTab = asLegacyGlobalFunctionForExtensionApi(createTerminalTabInjectable); - -export const terminalStore = asLegacyGlobalObjectForExtensionApiWithModifications( - terminalStoreInjectable, - { - sendCommand: asLegacyGlobalFunctionForExtensionApi(sendCommandInjectable), - }, -); - -const renameTab = asLegacyGlobalFunctionForExtensionApi(renameTabInjectable); -const podStore = asLegacyGlobalForExtensionApi(podStoreInjectable); - -export const logTabStore = asLegacyGlobalObjectForExtensionApiWithModifications( - logTabStoreInjectable, - { - createPodTab: asLegacyGlobalFunctionForExtensionApi(createPodLogsTabInjectable), - createWorkloadTab: asLegacyGlobalFunctionForExtensionApi(createWorkloadLogsTabInjectable), - renameTab: (tabId: string): void => { - const { selectedPodId } = logTabStore.getData(tabId) ?? {}; - const pod = selectedPodId && podStore.getById(selectedPodId); - - if (pod) { - renameTab(tabId, `Pod ${pod.getName()}`); - } - }, - tabs: undefined, - }, -); - -export class TerminalStore { - static getInstance() { - return terminalStore; - } - - static createInstance() { - return terminalStore; - } - - static resetInstance() { - console.warn("TerminalStore.resetInstance() does nothing"); - } -} - -export const notificationsStore = asLegacyGlobalForExtensionApi(notificationsStoreInjectable); diff --git a/src/extensions/renderer-api/index.ts b/src/extensions/renderer-api/index.ts deleted file mode 100644 index c58e278a75..0000000000 --- a/src/extensions/renderer-api/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Lens-extensions apis, required in renderer process runtime - -// APIs -import * as Catalog from "./catalog"; -import * as Component from "./components"; -import * as K8sApi from "./k8s-api"; -import * as Navigation from "./navigation"; -import * as Theme from "./theming"; -import { IpcRenderer as Ipc } from "../ipc/ipc-renderer"; -import { LensRendererExtension as LensExtension } from "../lens-renderer-extension"; - -export { - Catalog, - Component, - K8sApi, - Navigation, - Theme, - Ipc, - LensExtension, -}; diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts deleted file mode 100644 index 42b165602b..0000000000 --- a/src/extensions/renderer-api/k8s-api.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { KubeResource } from "../../common/rbac"; -import { apiResourceRecord } from "../../common/rbac"; -import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import clusterRoleBindingApiInjectable from "../../common/k8s-api/endpoints/cluster-role-binding.api.injectable"; -import clusterRoleApiInjectable from "../../common/k8s-api/endpoints/cluster-role.api.injectable"; -import serviceAccountApiInjectable from "../../common/k8s-api/endpoints/service-account.api.injectable"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import roleApiInjectable from "../../common/k8s-api/endpoints/role.api.injectable"; -import podApiInjectable from "../../common/k8s-api/endpoints/pod.api.injectable"; -import daemonSetApiInjectable from "../../common/k8s-api/endpoints/daemon-set.api.injectable"; -import replicaSetApiInjectable from "../../common/k8s-api/endpoints/replica-set.api.injectable"; -import statefulSetApiInjectable from "../../common/k8s-api/endpoints/stateful-set.api.injectable"; -import deploymentApiInjectable from "../../common/k8s-api/endpoints/deployment.api.injectable"; -import jobApiInjectable from "../../common/k8s-api/endpoints/job.api.injectable"; -import cronJobApiInjectable from "../../common/k8s-api/endpoints/cron-job.api.injectable"; -import nodeApiInjectable from "../../common/k8s-api/endpoints/node.api.injectable"; -import configMapApiInjectable from "../../common/k8s-api/endpoints/config-map.api.injectable"; -import secretApiInjectable from "../../common/k8s-api/endpoints/secret.api.injectable"; -import resourceQuotaApiInjectable from "../../common/k8s-api/endpoints/resource-quota.api.injectable"; -import limitRangeApiInjectable from "../../common/k8s-api/endpoints/limit-range.api.injectable"; -import horizontalPodAutoscalerApiInjectable from "../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable"; -import podDisruptionBudgetApiInjectable from "../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable"; -import priorityClassStoreApiInjectable from "../../common/k8s-api/endpoints/priority-class.api.injectable"; -import serviceApiInjectable from "../../common/k8s-api/endpoints/service.api.injectable"; -import endpointsApiInjectable from "../../common/k8s-api/endpoints/endpoint.api.injectable"; -import ingressApiInjectable from "../../common/k8s-api/endpoints/ingress.api.injectable"; -import networkPolicyApiInjectable from "../../common/k8s-api/endpoints/network-policy.api.injectable"; -import persistentVolumeApiInjectable from "../../common/k8s-api/endpoints/persistent-volume.api.injectable"; -import persistentVolumeClaimApiInjectable from "../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; -import storageClassApiInjectable from "../../common/k8s-api/endpoints/storage-class.api.injectable"; -import namespaceApiInjectable from "../../common/k8s-api/endpoints/namespace.api.injectable"; -import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable"; -import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable"; -import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; - -export function isAllowedResource(resources: KubeResource | KubeResource[]) { - const di = getLegacyGlobalDiForExtensionApi(); - - return [resources].flat().every((resourceName) => { - const resource = apiResourceRecord[resourceName]; - - if (!resource) { - return true; - } - - const _isAllowedResource = di.inject(shouldShowResourceInjectionToken, { - apiName: resourceName, - group: resource.group, - }); - - // Note: Legacy isAllowedResource does not advertise reactivity - return _isAllowedResource.get(); - }); -} - -export const serviceAccountsApi = asLegacyGlobalForExtensionApi(serviceAccountApiInjectable); -export const clusterRoleApi = asLegacyGlobalForExtensionApi(clusterRoleApiInjectable); -export const clusterRoleBindingApi = asLegacyGlobalForExtensionApi(clusterRoleBindingApiInjectable); -export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable); -export const podsApi = asLegacyGlobalForExtensionApi(podApiInjectable); -export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable); -export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable); -export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable); -export const deploymentApi = asLegacyGlobalForExtensionApi(deploymentApiInjectable); -export const jobApi = asLegacyGlobalForExtensionApi(jobApiInjectable); -export const cronJobApi = asLegacyGlobalForExtensionApi(cronJobApiInjectable); -export const nodesApi = asLegacyGlobalForExtensionApi(nodeApiInjectable); -export const secretsApi = asLegacyGlobalForExtensionApi(secretApiInjectable); -export const configMapApi = asLegacyGlobalForExtensionApi(configMapApiInjectable); -export const resourceQuotaApi = asLegacyGlobalForExtensionApi(resourceQuotaApiInjectable); -export const limitRangeApi = asLegacyGlobalForExtensionApi(limitRangeApiInjectable); -export const serviceApi = asLegacyGlobalForExtensionApi(serviceApiInjectable); -export const hpaApi = asLegacyGlobalForExtensionApi(horizontalPodAutoscalerApiInjectable); -export const pdbApi = asLegacyGlobalForExtensionApi(podDisruptionBudgetApiInjectable); -export const pcApi = asLegacyGlobalForExtensionApi(priorityClassStoreApiInjectable); -export const endpointApi = asLegacyGlobalForExtensionApi(endpointsApiInjectable); -export const ingressApi = asLegacyGlobalForExtensionApi(ingressApiInjectable); -export const networkPolicyApi = asLegacyGlobalForExtensionApi(networkPolicyApiInjectable); -export const persistentVolumeApi = asLegacyGlobalForExtensionApi(persistentVolumeApiInjectable); -export const pvcApi = asLegacyGlobalForExtensionApi(persistentVolumeClaimApiInjectable); -export const storageClassApi = asLegacyGlobalForExtensionApi(storageClassApiInjectable); -export const namespacesApi = asLegacyGlobalForExtensionApi(namespaceApiInjectable); -export const eventApi = asLegacyGlobalForExtensionApi(kubeEventApiInjectable); -export const roleBindingApi = asLegacyGlobalForExtensionApi(roleBindingApiInjectable); -export const crdApi = asLegacyGlobalForExtensionApi(customResourceDefinitionApiInjectable); - -export * from "../common-api/k8s-api"; - -export { - KubeObjectStatusLevel, - type KubeObjectStatus, -} from "../../common/k8s-api/kube-object-status"; - -// stores -export type { EventStore } from "../../renderer/components/+events/store"; -export type { PodStore as PodsStore } from "../../renderer/components/+workloads-pods/store"; -export type { NodeStore as NodesStore } from "../../renderer/components/+nodes/store"; -export type { DeploymentStore } from "../../renderer/components/+workloads-deployments/store"; -export type { DaemonSetStore } from "../../renderer/components/+workloads-daemonsets/store"; -export type { StatefulSetStore } from "../../renderer/components/+workloads-statefulsets/store"; -export type { JobStore } from "../../renderer/components/+workloads-jobs/store"; -export type { CronJobStore } from "../../renderer/components/+workloads-cronjobs/store"; -export type { ConfigMapStore as ConfigMapsStore } from "../../renderer/components/+config-maps/store"; -export type { SecretStore as SecretsStore } from "../../renderer/components/+config-secrets/store"; -export type { ReplicaSetStore } from "../../renderer/components/+workloads-replicasets/store"; -export type { ResourceQuotaStore as ResourceQuotasStore } from "../../renderer/components/+config-resource-quotas/store"; -export type { LimitRangeStore as LimitRangesStore } from "../../renderer/components/+config-limit-ranges/store"; -export type { HorizontalPodAutoscalerStore as HPAStore } from "../../renderer/components/+config-autoscalers/store"; -export type { PodDisruptionBudgetStore as PodDisruptionBudgetsStore } from "../../renderer/components/+config-pod-disruption-budgets/store"; -export type { PriorityClassStore as PriorityClassStoreStore } from "../../renderer/components/+config-priority-classes/store"; -export type { ServiceStore } from "../../renderer/components/+network-services/store"; -export type { EndpointsStore as EndpointStore } from "../../renderer/components/+network-endpoints/store"; -export type { IngressStore } from "../../renderer/components/+network-ingresses/ingress-store"; -export type { IngressClassStore } from "../../renderer/components/+network-ingresses/ingress-class-store"; -export type { NetworkPolicyStore } from "../../renderer/components/+network-policies/store"; -export type { PersistentVolumeStore as PersistentVolumesStore } from "../../renderer/components/+storage-volumes/store"; -export type { PersistentVolumeClaimStore as VolumeClaimStore } from "../../renderer/components/+storage-volume-claims/store"; -export type { StorageClassStore } from "../../renderer/components/+storage-classes/store"; -export type { NamespaceStore } from "../../renderer/components/+namespaces/store"; -export type { ServiceAccountStore as ServiceAccountsStore } from "../../renderer/components/+user-management/+service-accounts/store"; -export type { RoleStore as RolesStore } from "../../renderer/components/+user-management/+roles/store"; -export type { RoleBindingStore as RoleBindingsStore } from "../../renderer/components/+user-management/+role-bindings/store"; -export type { CustomResourceDefinitionStore as CRDStore } from "../../renderer/components/+custom-resources/definition.store"; -export type { CustomResourceStore as CRDResourceStore } from "../../common/k8s-api/api-manager/resource.store"; diff --git a/src/extensions/renderer-api/navigation.ts b/src/extensions/renderer-api/navigation.ts deleted file mode 100644 index ec7081c948..0000000000 --- a/src/extensions/renderer-api/navigation.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import getDetailsUrlInjectable from "../../renderer/components/kube-detail-params/get-details-url.injectable"; -import hideDetailsInjectable from "../../renderer/components/kube-detail-params/hide-details.injectable"; -import showDetailsInjectable from "../../renderer/components/kube-detail-params/show-details.injectable"; -import createPageParamInjectable from "../../renderer/navigation/create-page-param.injectable"; -import isActiveRouteInjectable from "../../renderer/navigation/is-route-active.injectable"; -import navigateInjectable from "../../renderer/navigation/navigate.injectable"; -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; - -export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param"; -export type { URLParams } from "../../common/utils/buildUrl"; - -export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable); -export const showDetails = asLegacyGlobalFunctionForExtensionApi(showDetailsInjectable); -export const hideDetails = asLegacyGlobalFunctionForExtensionApi(hideDetailsInjectable); -export const createPageParam = asLegacyGlobalFunctionForExtensionApi(createPageParamInjectable); -export const isActiveRoute = asLegacyGlobalFunctionForExtensionApi(isActiveRouteInjectable); -export const navigate = asLegacyGlobalFunctionForExtensionApi(navigateInjectable); diff --git a/src/extensions/renderer-api/theming.ts b/src/extensions/renderer-api/theming.ts deleted file mode 100644 index 3e4c9fe97b..0000000000 --- a/src/extensions/renderer-api/theming.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import activeThemeInjectable from "../../renderer/themes/active.injectable"; -import type { LensTheme } from "../../renderer/themes/lens-theme"; -import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; - -export const activeTheme = asLegacyGlobalForExtensionApi(activeThemeInjectable); - -/** - * @deprecated This hides the reactivity of active theme, use {@link activeTheme} instead - */ -export function getActiveTheme() { - return activeTheme.get(); -} - -export type { LensTheme }; diff --git a/src/extensions/renderer-extensions.injectable.ts b/src/extensions/renderer-extensions.injectable.ts deleted file mode 100644 index 92c3037e01..0000000000 --- a/src/extensions/renderer-extensions.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { IComputedValue } from "mobx"; -import extensionsInjectable from "./extensions.injectable"; -import type { LensRendererExtension } from "./lens-renderer-extension"; - -const rendererExtensionsInjectable = getInjectable({ - id: "renderer-extensions", - instantiate: (di) => di.inject(extensionsInjectable) as IComputedValue, -}); - -export default rendererExtensionsInjectable; diff --git a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap deleted file mode 100644 index ccf3a4153f..0000000000 --- a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ /dev/null @@ -1,488 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`extension special characters in page registrations renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
- -
-
-

- Welcome to some-product-name! -

-

- To get you started we have auto-detected your clusters in your - - kubeconfig file and added them to the catalog, your centralized - - view for managing all your cloud-native resources. -
-
- If you have any questions or feedback, please join our - - Lens Community slack channel - - . -

- -
-
-
-
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
- Some page -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; diff --git a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap b/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap deleted file mode 100644 index b66ac6c4b3..0000000000 --- a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap +++ /dev/null @@ -1,1148 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`navigate to extension page renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
- -
-
-

- Welcome to some-product-name! -

-

- To get you started we have auto-detected your clusters in your - - kubeconfig file and added them to the catalog, your centralized - - view for managing all your cloud-native resources. -
-
- If you have any questions or feedback, please join our - - Lens Community slack channel - - . -

- -
-
-
-
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`navigate to extension page when extension navigates to child route renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
- Child page -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`navigate to extension page when extension navigates to route with parameters renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
    -
  • - some-string-value-from-navigate -
  • -
  • - 126 -
  • -
  • - some-array-value-from-navigate -
  • -
- -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`navigate to extension page when extension navigates to route without parameters renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
    -
  • - some-string-value -
  • -
  • - 42 -
  • -
  • - some-array-value,some-other-array-value -
  • -
- -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`navigate to extension page when extension navigates to route without parameters when changing page parameters renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
    -
  • - some-changed-string-value -
  • -
  • - 84 -
  • -
  • - some-changed-array-value,some-other-changed-array-value -
  • -
- -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; diff --git a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap b/src/features/__snapshots__/navigating-between-routes.test.tsx.snap deleted file mode 100644 index 98b22c8ec3..0000000000 --- a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap +++ /dev/null @@ -1,412 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`navigating between routes given route with optional path parameters when navigating to route with path parameters renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-        {
-  "someParameter": "some-value",
-  "someOtherParameter": "some-other-value"
-}
-      
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`navigating between routes given route without path parameters when navigating to route renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
- Some component -
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; diff --git a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap b/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap deleted file mode 100644 index 28cdcc4b8d..0000000000 --- a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap +++ /dev/null @@ -1,572 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`add-cluster - navigation using application menu renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
- -
-
-

- Welcome to some-product-name! -

-

- To get you started we have auto-detected your clusters in your - - kubeconfig file and added them to the catalog, your centralized - - view for managing all your cloud-native resources. -
-
- If you have any questions or feedback, please join our - - Lens Community slack channel - - . -

- -
-
-
-
-
-
-
-
-
-
-
- Ca -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - arrow_left - - -
-
- 1 -
-
- - - arrow_right - - -
-
-
-
-
-
-
-
-
-`; - -exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = ` -
-
-
-
-
- - - home - - -
-
-
- - - arrow_back - - -
-
-
- - - arrow_forward - - -
-
-
-
-
-
-
-
-
-

- Add Clusters from Kubeconfig -

-

- Clusters added here are - - not - - merged into the - - ~/.kube/config - - file. - - Read more about adding clusters. - -

-
- -
-
-
-
-
-
- -`; - -exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given clicking the context menu for second namespace, when clicking to edit namespace renders 1`] = ` - -
-
-
-
-
- -
- - - close - - -
- Close -
-
-
-
-
-
-
-
-
warning.kind, - [sortBy.object]: warning => warning.getName(), - [sortBy.age]: warning => warning.ageMs, - }} - sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }} - sortSyncWithUrl={false} - getTableRow={this.getTableRow} - className={cssNames("box grow", this.props.activeTheme.get().type)} - > - - Message - Object - Type - Age - -
- - ); - } - - render() { - return ( -
- {this.renderContent()} -
- ); - } -} - -export const ClusterIssues = withInjectables(NonInjectedClusterIssues, { - getProps: (di, props) => ({ - ...props, - activeTheme: di.inject(activeThemeInjectable), - apiManager: di.inject(apiManagerInjectable), - eventStore: di.inject(eventStoreInjectable), - nodeStore: di.inject(nodeStoreInjectable), - kubeSelectedUrlParam: di.inject(kubeSelectedUrlParamInjectable), - toggleKubeDetailsPane: di.inject(toggleKubeDetailsPaneInjectable), - }), -}); diff --git a/src/renderer/components/+cluster/cluster-metric-switchers.tsx b/src/renderer/components/+cluster/cluster-metric-switchers.tsx deleted file mode 100644 index 7c072118dc..0000000000 --- a/src/renderer/components/+cluster/cluster-metric-switchers.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { observer } from "mobx-react"; -import type { NodeStore } from "../+nodes/store"; -import { Radio, RadioGroup } from "../radio"; -import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store"; -import { MetricNodeRole, MetricType } from "./cluster-overview-store/cluster-overview-store"; -import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import nodeStoreInjectable from "../+nodes/store.injectable"; -import { normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; - -interface Dependencies { - clusterOverviewStore: ClusterOverviewStore; - nodeStore: NodeStore; -} - -const NonInjectedClusterMetricSwitchers = observer(({ - clusterOverviewStore, - nodeStore, -}: Dependencies) => { - const { masterNodes, workerNodes } = nodeStore; - const { cpuUsage, memoryUsage } = clusterOverviewStore.metrics ?? {}; - const hasMasterNodes = masterNodes.length > 0; - const hasWorkerNodes = workerNodes.length > 0; - const hasCpuMetrics = normalizeMetrics(cpuUsage).data.result[0].values.length > 0; - const hasMemoryMetrics = normalizeMetrics(memoryUsage).data.result[0].values.length > 0; - - return ( -
-
- clusterOverviewStore.metricNodeRole = metric} - > - - - -
-
- clusterOverviewStore.metricType = value} - > - - - -
-
- ); -}); - -export const ClusterMetricSwitchers = withInjectables(NonInjectedClusterMetricSwitchers, { - getProps: (di) => ({ - clusterOverviewStore: di.inject(clusterOverviewStoreInjectable), - nodeStore: di.inject(nodeStoreInjectable), - }), -}); - diff --git a/src/renderer/components/+cluster/cluster-metrics.module.scss b/src/renderer/components/+cluster/cluster-metrics.module.scss deleted file mode 100644 index 8f45b01913..0000000000 --- a/src/renderer/components/+cluster/cluster-metrics.module.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - .ClusterMetrics { - position: relative; - min-height: 280px; - padding: calc(var(--margin) * 2); - background: var(--contentColor); - - .chart { - :global(.chart-container) { - width: 100%; - height: 100%; - } - } - - .empty { - text-align: center; - padding-bottom: 45px; - } -} diff --git a/src/renderer/components/+cluster/cluster-metrics.tsx b/src/renderer/components/+cluster/cluster-metrics.tsx deleted file mode 100644 index 028885e7f3..0000000000 --- a/src/renderer/components/+cluster/cluster-metrics.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./cluster-metrics.module.scss"; - -import React, { useState } from "react"; -import { observer } from "mobx-react"; -import type { ChartOptions, ChartPoint } from "chart.js"; -import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store"; -import { MetricType } from "./cluster-overview-store/cluster-overview-store"; -import { BarChart } from "../chart"; -import { bytesToUnits, cssNames } from "../../utils"; -import { Spinner } from "../spinner"; -import { ZebraStripesPlugin } from "../chart/zebra-stripes.plugin"; -import { ClusterNoMetrics } from "./cluster-no-metrics"; -import { ClusterMetricSwitchers } from "./cluster-metric-switchers"; -import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable"; - -interface Dependencies { - clusterOverviewStore: ClusterOverviewStore; -} - -const NonInjectedClusterMetrics = observer(({ clusterOverviewStore: { metricType, metricNodeRole, getMetricsValues, metricsLoaded, metrics }}: Dependencies) => { - const [plugins] = useState([new ZebraStripesPlugin()]); - const { memoryCapacity, cpuCapacity } = getMetricLastPoints(metrics ?? {}); - const metricValues = getMetricsValues(metrics ?? {}); - const colors = { cpu: "#3D90CE", memory: "#C93DCE" }; - const data = metricValues.map(value => ({ - x: value[0], - y: parseFloat(value[1]).toFixed(3), - })); - - const datasets = [{ - id: metricType + metricNodeRole, - label: `${metricType.toUpperCase()} usage`, - borderColor: colors[metricType], - data, - }]; - const cpuOptions: ChartOptions = { - scales: { - yAxes: [{ - ticks: { - suggestedMax: cpuCapacity, - callback: (value) => value, - }, - }], - }, - tooltips: { - callbacks: { - label: ({ index }, data) => { - if (!index) { - return ""; - } - - const value = data.datasets?.[0].data?.[index] as ChartPoint; - - return value.y?.toString() ?? ""; - }, - }, - }, - }; - const memoryOptions: ChartOptions = { - scales: { - yAxes: [{ - ticks: { - suggestedMax: memoryCapacity, - callback: (value: string) => !value ? 0 : bytesToUnits(parseInt(value)), - }, - }], - }, - tooltips: { - callbacks: { - label: ({ index }, data) => { - if (!index) { - return ""; - } - - const value = data.datasets?.[0].data?.[index] as ChartPoint; - - return bytesToUnits(parseInt(value.y as string), { precision: 3 }); - }, - }, - }, - }; - const options = metricType === MetricType.CPU ? cpuOptions : memoryOptions; - - const renderMetrics = () => { - if (!metricValues.length && !metricsLoaded) { - return ; - } - - if (!memoryCapacity || !cpuCapacity) { - return ; - } - - return ( - - ); - }; - - return ( -
- - {renderMetrics()} -
- ); -}); - -export const ClusterMetrics = withInjectables( - NonInjectedClusterMetrics, - - { - getProps: (di) => ({ - clusterOverviewStore: di.inject(clusterOverviewStoreInjectable), - }), - }, -); diff --git a/src/renderer/components/+cluster/cluster-no-metrics.module.scss b/src/renderer/components/+cluster/cluster-no-metrics.module.scss deleted file mode 100644 index 2ad031f434..0000000000 --- a/src/renderer/components/+cluster/cluster-no-metrics.module.scss +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ClusterNoMetrics { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - line-height: 1.7; - flex-grow: 1; - - .link { - color: var(--blue); - cursor: pointer; - } -} \ No newline at end of file diff --git a/src/renderer/components/+cluster/cluster-no-metrics.tsx b/src/renderer/components/+cluster/cluster-no-metrics.tsx deleted file mode 100644 index ec039835fc..0000000000 --- a/src/renderer/components/+cluster/cluster-no-metrics.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./cluster-no-metrics.module.scss"; - -import React from "react"; -import { Icon } from "../icon"; -import { cssNames } from "../../utils"; -import type { NavigateToEntitySettings } from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import navigateToEntitySettingsInjectable from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; - -export interface ClusterNoMetricsProps { - className: string; -} - -interface Dependencies { - navigateToEntitySettings: NavigateToEntitySettings; - clusterId: string | undefined; -} - -export function NonInjectedClusterNoMetrics({ className, navigateToEntitySettings, clusterId }: Dependencies & ClusterNoMetricsProps) { - function openMetricSettingsPage() { - if (clusterId) { - navigateToEntitySettings(clusterId, "metrics"); - } - } - - return ( -
- -

Metrics are not available due to missing or invalid Prometheus configuration.

-

Open cluster settings

-
- ); -} - -export const ClusterNoMetrics = withInjectables( - NonInjectedClusterNoMetrics, - - { - getProps: (di, props) => { - const cluster = di.inject(hostedClusterInjectable); - - return { - navigateToEntitySettings: di.inject(navigateToEntitySettingsInjectable), - clusterId: cluster?.id, - ...props, - }; - }, - }, -); diff --git a/src/renderer/components/+cluster/cluster-overview-route-component.injectable.ts b/src/renderer/components/+cluster/cluster-overview-route-component.injectable.ts deleted file mode 100644 index 5f4e5516cd..0000000000 --- a/src/renderer/components/+cluster/cluster-overview-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; -import { ClusterOverview } from "./cluster-overview"; -import clusterOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable"; - -const clusterOverviewRouteComponentInjectable = getInjectable({ - id: "cluster-overview-route-component", - - instantiate: (di) => ({ - route: di.inject(clusterOverviewRouteInjectable), - Component: ClusterOverview, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default clusterOverviewRouteComponentInjectable; diff --git a/src/renderer/components/+cluster/cluster-overview-sidebar-items.injectable.tsx b/src/renderer/components/+cluster/cluster-overview-sidebar-items.injectable.tsx deleted file mode 100644 index d3cf4e6814..0000000000 --- a/src/renderer/components/+cluster/cluster-overview-sidebar-items.injectable.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 { Icon } from "../icon"; -import React from "react"; -import type { - SidebarItemRegistration } from "../layout/sidebar-items.injectable"; -import { - sidebarItemsInjectionToken, -} from "../layout/sidebar-items.injectable"; -import { computed } from "mobx"; -import clusterOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToClusterOverviewInjectable from "../../../common/front-end-routing/routes/cluster/overview/navigate-to-cluster-overview.injectable"; - -const clusterOverviewSidebarItemsInjectable = getInjectable({ - id: "cluster-overview-sidebar-items", - - instantiate: (di) => { - const route = di.inject(clusterOverviewRouteInjectable); - const navigateToClusterOverview = di.inject(navigateToClusterOverviewInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed((): SidebarItemRegistration[] => [ - { - id: "cluster-overview", - parentId: null, - title: "Cluster", - getIcon: () => , - onClick: navigateToClusterOverview, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 10, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default clusterOverviewSidebarItemsInjectable; diff --git a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts deleted file mode 100644 index 1b64040d40..0000000000 --- a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { ClusterOverviewStorageState } from "./cluster-overview-store"; -import { ClusterOverviewStore, MetricNodeRole, MetricType } from "./cluster-overview-store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token"; -import clusterApiInjectable from "../../../../common/k8s-api/endpoints/cluster.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import assert from "assert"; -import nodeStoreInjectable from "../../+nodes/store.injectable"; -import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; - -const clusterOverviewStoreInjectable = getInjectable({ - id: "cluster-overview-store", - - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "clusterOverviewStore is only available in certain environments"); - const createStorage = di.inject(createStorageInjectable); - const clusterApi = di.inject(clusterApiInjectable); - - return new ClusterOverviewStore({ - storage: createStorage( - "cluster_overview", - { - metricType: MetricType.CPU, // setup defaults - metricNodeRole: MetricNodeRole.WORKER, - }, - ), - nodeStore: di.inject(nodeStoreInjectable), - requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, clusterApi); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default clusterOverviewStoreInjectable; diff --git a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts deleted file mode 100644 index 943b14dbf9..0000000000 --- a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, observable, reaction, when, makeObservable } from "mobx"; -import type { KubeObjectStoreDependencies } from "../../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; -import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints"; -import type { StorageLayer } from "../../../utils"; -import { autoBind } from "../../../utils"; -import type { NodeStore } from "../../+nodes/store"; -import type { ClusterMetricData, RequestClusterMetricsByNodeNames } from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; -import type { RequestMetricsParams } from "../../../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable"; -import { normalizeMetrics } from "../../../../common/k8s-api/endpoints/metrics.api"; - -export enum MetricType { - MEMORY = "memory", - CPU = "cpu", -} - -export enum MetricNodeRole { - MASTER = "master", - WORKER = "worker", -} - -export interface ClusterOverviewStorageState { - metricType: MetricType; - metricNodeRole: MetricNodeRole; -} - -interface ClusterOverviewStoreDependencies extends KubeObjectStoreDependencies { - readonly storage: StorageLayer; - readonly nodeStore: NodeStore; - requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames; -} - -export class ClusterOverviewStore extends KubeObjectStore implements ClusterOverviewStorageState { - @observable metrics: ClusterMetricData | undefined = undefined; - - get metricsLoaded() { - return !!this.metrics; - } - - get metricType(): MetricType { - return this.dependencies.storage.get().metricType; - } - - set metricType(value: MetricType) { - this.dependencies.storage.merge({ metricType: value }); - } - - get metricNodeRole(): MetricNodeRole { - return this.dependencies.storage.get().metricNodeRole; - } - - set metricNodeRole(value: MetricNodeRole) { - this.dependencies.storage.merge({ metricNodeRole: value }); - } - - constructor(protected readonly dependencies: ClusterOverviewStoreDependencies, api: ClusterApi) { - super(dependencies, api); - makeObservable(this); - autoBind(this); - - this.init(); - } - - private init() { - // TODO: refactor, seems not a correct place to be - // auto-refresh metrics on user-action - reaction(() => this.metricNodeRole, () => { - if (this.metrics) { - this.resetMetrics(); - this.loadMetrics(); - } - }); - - // check which node type to select - reaction(() => this.dependencies.nodeStore.items.length, () => { - const { masterNodes, workerNodes } = this.dependencies.nodeStore; - - if (!masterNodes.length) this.metricNodeRole = MetricNodeRole.WORKER; - if (!workerNodes.length) this.metricNodeRole = MetricNodeRole.MASTER; - }); - } - - @action - async loadMetrics(params?: RequestMetricsParams) { - await when(() => this.dependencies.nodeStore.isLoaded); - const { masterNodes, workerNodes } = this.dependencies.nodeStore; - const nodes = this.metricNodeRole === MetricNodeRole.MASTER && masterNodes.length ? masterNodes : workerNodes; - - this.metrics = await this.dependencies.requestClusterMetricsByNodeNames(nodes.map(node => node.getName()), params); - } - - getMetricsValues(source: Partial): [number, string][] { - switch (this.metricType) { - case MetricType.CPU: - return normalizeMetrics(source.cpuUsage).data.result[0].values; - case MetricType.MEMORY: - return normalizeMetrics(source.memoryUsage).data.result[0].values; - default: - return []; - } - } - - @action - resetMetrics() { - this.metrics = undefined; - } - - reset() { - super.reset(); - this.resetMetrics(); - this.dependencies.storage?.reset(); - } -} diff --git a/src/renderer/components/+cluster/cluster-overview.module.scss b/src/renderer/components/+cluster/cluster-overview.module.scss deleted file mode 100644 index a0f885582b..0000000000 --- a/src/renderer/components/+cluster/cluster-overview.module.scss +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ClusterOverview { - position: relative; - height: 100%; - min-height: 650px; - display: grid; - grid-gap: calc(var(--margin) * 2); - grid-template-areas: - "barcharts piechars" - "issues issues"; - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - grid-template-rows: - [row1-start] 1fr - [row2-start] 1fr; - - @media (max-width: 1150px) { - grid-template-columns: minmax(500px, 1fr); - grid-template-rows: 1fr; - grid-template-areas: none; - } -} diff --git a/src/renderer/components/+cluster/cluster-overview.tsx b/src/renderer/components/+cluster/cluster-overview.tsx deleted file mode 100644 index f1cb9ad461..0000000000 --- a/src/renderer/components/+cluster/cluster-overview.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./cluster-overview.module.scss"; - -import React from "react"; -import { reaction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import type { NodeStore } from "../+nodes/store"; -import type { PodStore } from "../+workloads-pods/store"; -import { interval } from "../../utils"; -import { TabLayout } from "../layout/tab-layout"; -import { Spinner } from "../spinner"; -import { ClusterIssues } from "./cluster-issues"; -import { ClusterMetrics } from "./cluster-metrics"; -import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store"; -import { ClusterPieCharts } from "./cluster-pie-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import type { Cluster } from "../../../common/cluster/cluster"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import assert from "assert"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import eventStoreInjectable from "../+events/store.injectable"; -import nodeStoreInjectable from "../+nodes/store.injectable"; - -interface Dependencies { - subscribeStores: SubscribeStores; - clusterOverviewStore: ClusterOverviewStore; - hostedCluster: Cluster; - podStore: PodStore; - eventStore: EventStore; - nodeStore: NodeStore; -} - -@observer -class NonInjectedClusterOverview extends React.Component { - private metricPoller = interval(60, () => this.loadMetrics()); - - loadMetrics() { - if (this.props.hostedCluster.available) { - this.props.clusterOverviewStore.loadMetrics(); - } - } - - componentDidMount() { - this.metricPoller.start(true); - - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.podStore, - this.props.eventStore, - this.props.nodeStore, - ]), - - reaction( - () => this.props.clusterOverviewStore.metricNodeRole, // Toggle Master/Worker node switcher - () => this.metricPoller.restart(true), - ), - ]); - } - - componentWillUnmount() { - this.metricPoller.stop(); - } - - renderMetrics(isMetricsHidden: boolean) { - if (isMetricsHidden) { - return null; - } - - return ( - <> - - - - ); - } - - renderClusterOverview(isLoaded: boolean, isMetricsHidden: boolean) { - if (!isLoaded) { - return ; - } - - return ( - <> - {this.renderMetrics(isMetricsHidden)} - - - ); - } - - render() { - const { eventStore, nodeStore, hostedCluster } = this.props; - const isLoaded = nodeStore.isLoaded && eventStore.isLoaded; - const isMetricHidden = hostedCluster.isMetricHidden(ClusterMetricsResourceType.Cluster); - - return ( - -
- {this.renderClusterOverview(isLoaded, isMetricHidden)} -
-
- ); - } -} - -export const ClusterOverview = withInjectables(NonInjectedClusterOverview, { - getProps: (di) => { - const hostedCluster = di.inject(hostedClusterInjectable); - - assert(hostedCluster, "Only allowed to renderer ClusterOverview within cluster frame"); - - return { - subscribeStores: di.inject(subscribeStoresInjectable), - clusterOverviewStore: di.inject(clusterOverviewStoreInjectable), - hostedCluster, - podStore: di.inject(podStoreInjectable), - eventStore: di.inject(eventStoreInjectable), - nodeStore: di.inject(nodeStoreInjectable), - }; - }, -}); diff --git a/src/renderer/components/+cluster/cluster-pie-charts.module.scss b/src/renderer/components/+cluster/cluster-pie-charts.module.scss deleted file mode 100644 index 9ec9564da0..0000000000 --- a/src/renderer/components/+cluster/cluster-pie-charts.module.scss +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.empty { - background: var(--contentColor); - min-height: 280px; - text-align: center; - padding: calc(var(--padding) * 2); -} - -.chart { - --flex-gap: calc(var(--padding) * 2); - background: var(--contentColor); - padding: calc(var(--padding) * 2) var(--padding); -} - -.noMetrics { - display: flex; - flex-grow: 1; -} diff --git a/src/renderer/components/+cluster/cluster-pie-charts.tsx b/src/renderer/components/+cluster/cluster-pie-charts.tsx deleted file mode 100644 index e0203dd878..0000000000 --- a/src/renderer/components/+cluster/cluster-pie-charts.tsx +++ /dev/null @@ -1,288 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./cluster-pie-charts.module.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store"; -import { MetricNodeRole } from "./cluster-overview-store/cluster-overview-store"; -import { Spinner } from "../spinner"; -import { Icon } from "../icon"; -import type { NodeStore } from "../+nodes/store"; -import type { PieChartData } from "../chart"; -import { PieChart } from "../chart"; -import { ClusterNoMetrics } from "./cluster-no-metrics"; -import { bytesToUnits, cssNames } from "../../utils"; -import type { LensTheme } from "../../themes/lens-theme"; -import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable"; -import nodeStoreInjectable from "../+nodes/store.injectable"; -import type { IComputedValue } from "mobx"; -import activeThemeInjectable from "../../themes/active.injectable"; -import type { ClusterMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable"; - -function createLabels(rawLabelData: [string, number | undefined][]): string[] { - return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`); -} - -const checkedBytesToUnits = (value: number | undefined) => ( - typeof value === "number" - ? bytesToUnits(value) - : "N/A" -); - -interface Dependencies { - clusterOverviewStore: ClusterOverviewStore; - nodeStore: NodeStore; - activeTheme: IComputedValue; -} - -const NonInjectedClusterPieCharts = observer(({ - clusterOverviewStore, - nodeStore, - activeTheme, -}: Dependencies) => { - const renderLimitWarning = () => { - return ( -
- -

Specified limits are higher than node capacity!

-
- ); - }; - - const renderCharts = (lastPoints: Partial>) => { - const { - memoryUsage, memoryRequests, memoryAllocatableCapacity, memoryCapacity, memoryLimits, - cpuUsage, cpuRequests, cpuAllocatableCapacity, cpuCapacity, cpuLimits, - podUsage, podAllocatableCapacity, podCapacity, - } = lastPoints; - - if ( - typeof cpuCapacity !== "number" || - typeof cpuAllocatableCapacity !== "number" || - typeof podCapacity !== "number" || - typeof podAllocatableCapacity !== "number" || - typeof memoryAllocatableCapacity !== "number" || - typeof memoryCapacity !== "number" || - typeof memoryUsage !== "number" || - typeof memoryRequests !== "number" - ) { - return null; - } - - const defaultColor = activeTheme.get().colors.pieChartDefaultColor; - - const cpuData: PieChartData = { - datasets: [ - { - data: [ - cpuUsage, - cpuUsage ? cpuAllocatableCapacity - cpuUsage : 1, - ], - backgroundColor: [ - "#c93dce", - defaultColor, - ], - id: "cpuUsage", - label: "Usage", - }, - { - data: [ - cpuRequests, - cpuRequests ? cpuAllocatableCapacity - cpuRequests : 1, - ], - backgroundColor: [ - "#4caf50", - defaultColor, - ], - id: "cpuRequests", - label: "Requests", - }, - { - data: [ - cpuLimits, - Math.max(0, cpuAllocatableCapacity - (cpuLimits ?? cpuAllocatableCapacity)), - ], - backgroundColor: [ - "#3d90ce", - defaultColor, - ], - id: "cpuLimits", - label: "Limits", - }, - ], - labels: createLabels([ - ["Usage", cpuUsage], - ["Requests", cpuRequests], - ["Limits", cpuLimits], - ["Allocatable Capacity", cpuAllocatableCapacity], - ["Capacity", cpuCapacity], - ]), - }; - const memoryData: PieChartData = { - datasets: [ - { - data: [ - memoryUsage, - memoryUsage ? memoryAllocatableCapacity - memoryUsage : 1, - ], - backgroundColor: [ - "#c93dce", - defaultColor, - ], - id: "memoryUsage", - label: "Usage", - }, - { - data: [ - memoryRequests, - memoryRequests ? memoryAllocatableCapacity - memoryRequests : 1, - ], - backgroundColor: [ - "#4caf50", - defaultColor, - ], - id: "memoryRequests", - label: "Requests", - }, - { - data: [ - memoryLimits, - Math.max(0, memoryAllocatableCapacity - (memoryLimits ?? memoryAllocatableCapacity)), - ], - backgroundColor: [ - "#3d90ce", - defaultColor, - ], - id: "memoryLimits", - label: "Limits", - }, - ], - labels: [ - `Usage: ${bytesToUnits(memoryUsage)}`, - `Requests: ${bytesToUnits(memoryRequests)}`, - `Limits: ${checkedBytesToUnits(memoryLimits)}`, - `Allocatable Capacity: ${bytesToUnits(memoryAllocatableCapacity)}`, - `Capacity: ${bytesToUnits(memoryCapacity)}`, - ], - }; - const podsData: PieChartData = { - datasets: [ - { - data: [ - podUsage, - podUsage ? podAllocatableCapacity - podUsage : 1, - ], - backgroundColor: [ - "#4caf50", - defaultColor, - ], - id: "podUsage", - label: "Usage", - tooltipLabels: [ - (percent) => `Usage: ${percent}`, - (percent) => `Available: ${percent}`, - ], - }, - ], - labels: [ - `Usage: ${podUsage || 0}`, - `Capacity: ${podAllocatableCapacity}`, - ], - }; - - return ( -
-
- - {((cpuLimits ?? cpuAllocatableCapacity) > cpuAllocatableCapacity) && renderLimitWarning()} -
-
- - {((memoryLimits ?? memoryAllocatableCapacity) > memoryAllocatableCapacity) && renderLimitWarning()} -
-
- -
-
- ); - }; - - const renderContent = ({ metricNodeRole, metrics }: ClusterOverviewStore) => { - const { masterNodes, workerNodes } = nodeStore; - const nodes = metricNodeRole === MetricNodeRole.MASTER ? masterNodes : workerNodes; - - if (!nodes.length) { - return ( -
- - No Nodes Available. -
- ); - } - - if (!metrics) { - return ( -
- -
- ); - } - - const lastPoints = getMetricLastPoints(metrics); - const { memoryCapacity, cpuCapacity, podCapacity } = lastPoints; - - if (!memoryCapacity || !cpuCapacity || !podCapacity) { - return ( -
- -
- ); - } - - return renderCharts(lastPoints); - }; - - return ( -
- {renderContent(clusterOverviewStore)} -
- ); -}); - -export const ClusterPieCharts = withInjectables(NonInjectedClusterPieCharts, { - getProps: (di) => ({ - clusterOverviewStore: di.inject(clusterOverviewStoreInjectable), - nodeStore: di.inject(nodeStoreInjectable), - activeTheme: di.inject(activeThemeInjectable), - }), -}); diff --git a/src/renderer/components/+config-autoscalers/autoscaler.mixins.scss b/src/renderer/components/+config-autoscalers/autoscaler.mixins.scss deleted file mode 100644 index 9f2c3e5eb3..0000000000 --- a/src/renderer/components/+config-autoscalers/autoscaler.mixins.scss +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -$hpa-status-colors: ( - abletoscale: var(--colorOk), - scalingactive: var(--colorInfo), - scalinglimited: var(--colorSoftError), -); - -@mixin hpa-status-bgc { - @each $status, $color in $hpa-status-colors { - &.#{$status} { - background: $color; - color: white; - } - } -} diff --git a/src/renderer/components/+config-autoscalers/horizontal-pod-auto-scalers-sidebar-items.injectable.tsx b/src/renderer/components/+config-autoscalers/horizontal-pod-auto-scalers-sidebar-items.injectable.tsx deleted file mode 100644 index 7298fd1a8b..0000000000 --- a/src/renderer/components/+config-autoscalers/horizontal-pod-auto-scalers-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import type { SidebarItemRegistration } from "../layout/sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToHorizontalPodAutoscalersInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable"; - -const horizontalPodAutoScalersSidebarItemsInjectable = getInjectable({ - id: "horizontal-pod-auto-scalers-sidebar-items", - - instantiate: (di) => { - const route = di.inject(horizontalPodAutoscalersRouteInjectable); - const navigateToHorizontalPodAutoscalers = di.inject(navigateToHorizontalPodAutoscalersInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed((): SidebarItemRegistration[] => [ - { - id: "horizontal-pod-auto-scalers", - parentId: configSidebarItemId, - title: "HPA", - onClick: navigateToHorizontalPodAutoscalers, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 50, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default horizontalPodAutoScalersSidebarItemsInjectable; diff --git a/src/renderer/components/+config-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts b/src/renderer/components/+config-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts deleted file mode 100644 index 991f6cab1b..0000000000 --- a/src/renderer/components/+config-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; -import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable"; -import { HorizontalPodAutoscalers } from "./hpa"; - -const horizontalPodAutoscalersRouteComponentInjectable = getInjectable({ - id: "horizontal-pod-autoscalers-route-component", - - instantiate: (di) => ({ - route: di.inject(horizontalPodAutoscalersRouteInjectable), - Component: HorizontalPodAutoscalers, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default horizontalPodAutoscalersRouteComponentInjectable; diff --git a/src/renderer/components/+config-autoscalers/hpa-details.scss b/src/renderer/components/+config-autoscalers/hpa-details.scss deleted file mode 100644 index 571d988b11..0000000000 --- a/src/renderer/components/+config-autoscalers/hpa-details.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@import "autoscaler.mixins"; - -.HpaDetails { - .status { - @include hpa-status-bgc; - } - - .metrics .Table { - margin: 0 (-$margin * 3); - - .TableCell { - word-break: break-word; - - &:first-child { - margin-left: $margin * 2; - } - - &:last-child { - margin-right: $margin * 2; - } - - &.name { - flex-grow: 2; - } - } - } -} diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx deleted file mode 100644 index 96c1a7e20b..0000000000 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./hpa-details.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { Link } from "react-router-dom"; -import { DrawerItem, DrawerTitle } from "../drawer"; -import { Badge } from "../badge"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { cssNames } from "../../utils"; -import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTarget } 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 type { ApiManager } from "../../../common/k8s-api/api-manager"; -import type { Logger } from "../../../common/logger"; -import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; -import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; -import { getMetricName } from "./get-hpa-metric-name"; - -export interface HpaDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - apiManager: ApiManager; - logger: Logger; - getDetailsUrl: GetDetailsUrl; - getMetrics: (hpa: HorizontalPodAutoscaler) => string[]; -} - -@observer -class NonInjectedHpaDetails extends React.Component { - private renderTargetLink(target: HorizontalPodAutoscalerMetricTarget | undefined) { - if (!target) { - return null; - } - - const { object: hpa, apiManager, getDetailsUrl } = this.props; - const { kind, name } = target; - const objectUrl = getDetailsUrl(apiManager.lookupApiLink(target, hpa)); - - return ( - <> - on - - {`${kind}/${name}`} - - - ); - } - - renderMetrics() { - const { object: hpa } = this.props; - - const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => { - const metricName = getMetricName(metric); - - switch (metric.type) { - case HpaMetricType.ContainerResource: - - // fallthrough - case HpaMetricType.Resource: { - const metricSpec = metric.resource ?? metric.containerResource; - - return `Resource ${metricSpec.name} on Pods`; - } - case HpaMetricType.Pods: - return `${metricName} on Pods`; - - case HpaMetricType.Object: { - return ( - <> - {metricName} - {" "} - {this.renderTargetLink(metric.object?.describedObject)} - - ); - } - case HpaMetricType.External: - return `${metricName} on ${JSON.stringify(metric.external.metricSelector ?? metric.external.metric?.selector)}`; - } - }; - - return ( - - - Name - Current / Target - - { - this.props.getMetrics(hpa) - .map((metrics, index) => ( - - {renderName(hpa.getMetrics()[index])} - {metrics} - - )) - } -
- ); - } - - render() { - const { object: hpa, apiManager, getDetailsUrl, logger } = this.props; - - if (!hpa) { - return null; - } - - if (!(hpa instanceof HorizontalPodAutoscaler)) { - logger.error("[HpaDetails]: passed object that is not an instanceof HorizontalPodAutoscaler", hpa); - - return null; - } - - const { scaleTargetRef } = hpa.spec; - - return ( -
- - {scaleTargetRef && ( - - {scaleTargetRef.kind} - / - {scaleTargetRef.name} - - )} - - - - {hpa.getMinPods()} - - - - {hpa.getMaxPods()} - - - - {hpa.getReplicas()} - - - - {hpa.getReadyConditions() - .map(({ type, tooltip, isReady }) => ( - - ))} - - - Metrics -
- {this.renderMetrics()} -
-
- ); - } -} - -export const HpaDetails = withInjectables(NonInjectedHpaDetails, { - getProps: (di, props) => ({ - ...props, - apiManager: di.inject(apiManagerInjectable), - getDetailsUrl: di.inject(getDetailsUrlInjectable), - logger: di.inject(loggerInjectable), - getMetrics: di.inject(getHorizontalPodAutoscalerMetrics), - }), -}); diff --git a/src/renderer/components/+config-autoscalers/hpa.scss b/src/renderer/components/+config-autoscalers/hpa.scss deleted file mode 100644 index 423693a61f..0000000000 --- a/src/renderer/components/+config-autoscalers/hpa.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@import "autoscaler.mixins"; - -.HorizontalPodAutoscalers { - .TableCell { - &.name { - flex: 1.5; - } - - &.warning { - @include table-cell-warning; - } - - &.metrics { - flex: 1.5; - } - - &.status { - flex: 1.5; - @include table-cell-labels-offsets; - @include hpa-status-bgc; - } - - &.age { - flex: .5; - } - } -} diff --git a/src/renderer/components/+config-autoscalers/hpa.tsx b/src/renderer/components/+config-autoscalers/hpa.tsx deleted file mode 100644 index e7a456b3f1..0000000000 --- a/src/renderer/components/+config-autoscalers/hpa.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./hpa.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import type { HorizontalPodAutoscaler } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; -import { Badge } from "../badge"; -import { cssNames } from "../../utils"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { HorizontalPodAutoscalerStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import horizontalPodAutoscalerStoreInjectable from "./store.injectable"; -import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - metrics = "metrics", - minPods = "min-pods", - maxPods = "max-pods", - replicas = "replicas", - age = "age", - status = "status", -} - -interface Dependencies { - horizontalPodAutoscalerStore: HorizontalPodAutoscalerStore; - getMetrics: (hpa: HorizontalPodAutoscaler) => string[]; -} - -@observer -class NonInjectedHorizontalPodAutoscalers extends React.Component { - getTargets(hpa: HorizontalPodAutoscaler) { - const metrics = hpa.getMetrics(); - - if (metrics.length === 0 && !hpa.spec?.targetCPUUtilizationPercentage) { - return

--

; - } - - const metricsRemain = metrics.length > 1 ? `+${metrics.length - 1} more...` : ""; - - return ( -

- {this.props.getMetrics(hpa)[0]} - {" "} - {metricsRemain} -

- ); - } - - render() { - return ( - - hpa.getName(), - [columnId.namespace]: hpa => hpa.getNs(), - [columnId.minPods]: hpa => hpa.getMinPods(), - [columnId.maxPods]: hpa => hpa.getMaxPods(), - [columnId.replicas]: hpa => hpa.getReplicas(), - [columnId.age]: hpa => -hpa.getCreationTimestamp(), - }} - searchFilters={[ - hpa => hpa.getSearchFields(), - ]} - renderHeaderTitle="Horizontal Pod Autoscalers" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Metrics", className: "metrics", id: columnId.metrics }, - { title: "Min Pods", className: "min-pods", sortBy: columnId.minPods, id: columnId.minPods }, - { title: "Max Pods", className: "max-pods", sortBy: columnId.maxPods, id: columnId.maxPods }, - { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - { title: "Status", className: "status scrollable", id: columnId.status }, - ]} - renderTableContents={hpa => [ - hpa.getName(), - , - , - this.getTargets(hpa), - hpa.getMinPods(), - hpa.getMaxPods(), - hpa.getReplicas(), - , - hpa.getConditions() - .filter(({ isReady }) => isReady) - .map(({ type, tooltip }) => ( - - )), - ]} - /> - - ); - } -} - -export const HorizontalPodAutoscalers = withInjectables(NonInjectedHorizontalPodAutoscalers, { - getProps: (di, props) => ({ - ...props, - horizontalPodAutoscalerStore: di.inject(horizontalPodAutoscalerStoreInjectable), - getMetrics: di.inject(getHorizontalPodAutoscalerMetrics), - }), -}); diff --git a/src/renderer/components/+config-autoscalers/index.ts b/src/renderer/components/+config-autoscalers/index.ts deleted file mode 100644 index a0b5231b2e..0000000000 --- a/src/renderer/components/+config-autoscalers/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./hpa"; -export * from "./hpa-details"; diff --git a/src/renderer/components/+config-autoscalers/store.injectable.ts b/src/renderer/components/+config-autoscalers/store.injectable.ts deleted file mode 100644 index 6b09568537..0000000000 --- a/src/renderer/components/+config-autoscalers/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import horizontalPodAutoscalerApiInjectable from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { HorizontalPodAutoscalerStore } from "./store"; - -const horizontalPodAutoscalerStoreInjectable = getInjectable({ - id: "horizontal-pod-autoscaler-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "horizontalPodAutoscalerStore is only available in certain environments"); - - const api = di.inject(horizontalPodAutoscalerApiInjectable); - - return new HorizontalPodAutoscalerStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default horizontalPodAutoscalerStoreInjectable; diff --git a/src/renderer/components/+config-autoscalers/store.ts b/src/renderer/components/+config-autoscalers/store.ts deleted file mode 100644 index ccee4ab943..0000000000 --- a/src/renderer/components/+config-autoscalers/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { HorizontalPodAutoscaler, HorizontalPodAutoscalerApi } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; - -export class HorizontalPodAutoscalerStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-leases/index.ts b/src/renderer/components/+config-leases/index.ts deleted file mode 100644 index dcef127fb2..0000000000 --- a/src/renderer/components/+config-leases/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./leases"; -export * from "./lease-details"; diff --git a/src/renderer/components/+config-leases/lease-details.scss b/src/renderer/components/+config-leases/lease-details.scss deleted file mode 100644 index 33628f74dd..0000000000 --- a/src/renderer/components/+config-leases/lease-details.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.LeaseDetails {} \ No newline at end of file diff --git a/src/renderer/components/+config-leases/lease-details.tsx b/src/renderer/components/+config-leases/lease-details.tsx deleted file mode 100644 index d86ecabaf8..0000000000 --- a/src/renderer/components/+config-leases/lease-details.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./lease-details.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { Lease } from "../../../common/k8s-api/endpoints"; - -export interface LeaseDetailsProps extends KubeObjectDetailsProps { -} - -@observer -export class LeaseDetails extends React.Component { - - render() { - const { object: lease } = this.props; - - return ( -
- - {lease.getHolderIdentity()} - - - - {lease.getLeaseDurationSeconds()} - - - - - - - - {lease.getRenewTime()} - - -
- ); - } -} diff --git a/src/renderer/components/+config-leases/leases-route-component.injectable.ts b/src/renderer/components/+config-leases/leases-route-component.injectable.ts deleted file mode 100644 index ef0ffbf37d..0000000000 --- a/src/renderer/components/+config-leases/leases-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Leases } from "./leases"; -import leasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/leases/leases-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const leasesRouteComponentInjectable = getInjectable({ - id: "leases-route-component", - - instantiate: (di) => ({ - route: di.inject(leasesRouteInjectable), - Component: Leases, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default leasesRouteComponentInjectable; diff --git a/src/renderer/components/+config-leases/leases-sidebar-items.injectable.tsx b/src/renderer/components/+config-leases/leases-sidebar-items.injectable.tsx deleted file mode 100644 index 178cf8a172..0000000000 --- a/src/renderer/components/+config-leases/leases-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 leasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/leases/leases-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToLeasesInjectable from "../../../common/front-end-routing/routes/cluster/config/leases/navigate-to-leases.injectable"; - -const leasesSidebarItemsInjectable = getInjectable({ - id: "leases-sidebar-items", - - instantiate: (di) => { - const route = di.inject(leasesRouteInjectable); - const navigateToLeases = di.inject(navigateToLeasesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "leases", - parentId: configSidebarItemId, - title: "Leases", - onClick: navigateToLeases, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 80, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default leasesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-leases/leases.scss b/src/renderer/components/+config-leases/leases.scss deleted file mode 100644 index 5cc62798a9..0000000000 --- a/src/renderer/components/+config-leases/leases.scss +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.Leases { - .TableCell { - &.name { - flex: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.keys { - flex: 2.5; - } - - &.age { - flex: .5; - } - - - } -} diff --git a/src/renderer/components/+config-leases/leases.tsx b/src/renderer/components/+config-leases/leases.tsx deleted file mode 100644 index 950c52b778..0000000000 --- a/src/renderer/components/+config-leases/leases.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./leases.scss"; - -import * as React from "react"; -import { observer } from "mobx-react"; -import type { Lease } from "../../../common/k8s-api/endpoints/lease.api"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import leaseStoreInjectable from "./store.injectable"; -import type { LeaseStore } from "./store"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - holder = "holder", - age = "age", -} - -export interface LeaseProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - leaseStore: LeaseStore; -} - -@observer -class NonInjectedLease extends React.Component { - render() { - const { leaseStore } = this.props; - - return ( - - lease.getName(), - [columnId.namespace]: lease => lease.getNs(), - [columnId.holder]: lease => lease.getHolderIdentity(), - [columnId.age]: lease => -lease.getCreationTimestamp(), - }} - searchFilters={[ - lease => lease.getSearchFields(), - ]} - renderHeaderTitle="Leases" - 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: "Holder", className: "holder", sortBy: columnId.holder, id: columnId.holder }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={lease => [ - lease.getName(), - , - , - lease.getHolderIdentity(), - , - ]} - /> - - ); - } -} - -export const Leases = withInjectables(NonInjectedLease, { - getProps: (di, props) => ({ - ...props, - leaseStore: di.inject(leaseStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-leases/store.injectable.ts b/src/renderer/components/+config-leases/store.injectable.ts deleted file mode 100644 index 26cf598aff..0000000000 --- a/src/renderer/components/+config-leases/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import leaseApiInjectable from "../../../common/k8s-api/endpoints/lease.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { LeaseStore } from "./store"; - -const leaseStoreInjectable = getInjectable({ - id: "lease-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "leaseStore is only available in certain environments"); - - const api = di.inject(leaseApiInjectable); - - return new LeaseStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default leaseStoreInjectable; diff --git a/src/renderer/components/+config-leases/store.ts b/src/renderer/components/+config-leases/store.ts deleted file mode 100644 index db19aed8dd..0000000000 --- a/src/renderer/components/+config-leases/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { Lease, LeaseApi } from "../../../common/k8s-api/endpoints/lease.api"; - -export class LeaseStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-limit-ranges/index.ts b/src/renderer/components/+config-limit-ranges/index.ts deleted file mode 100644 index 50e2e3043d..0000000000 --- a/src/renderer/components/+config-limit-ranges/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./limit-ranges"; -export * from "./limit-range-details"; diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.scss b/src/renderer/components/+config-limit-ranges/limit-range-details.scss deleted file mode 100644 index 1d9cf0f8ae..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.scss +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.LimitRangeDetails { - - .DrawerItem { - > .name { - font-weight: $font-weight-normal; - padding-left: 4px; - } - .DrawerItem { - padding-top: 4px; - } - } -} diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx deleted file mode 100644 index 79c3038a5d..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./limit-range-details.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-range.api"; -import { LimitPart, LimitRange, Resource } from "../../../common/k8s-api/endpoints/limit-range.api"; -import { DrawerItem } from "../drawer/drawer-item"; -import { Badge } from "../badge"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface LimitRangeDetailsProps extends KubeObjectDetailsProps { -} - -function renderLimit(limit: LimitRangeItem, part: LimitPart, resource: Resource) { - - const resourceLimit = limit[part]?.[resource]; - - if (!resourceLimit) { - return null; - } - - return ; -} - -function renderResourceLimits(limit: LimitRangeItem, resource: Resource) { - return ( - - {renderLimit(limit, LimitPart.MIN, resource)} - {renderLimit(limit, LimitPart.MAX, resource)} - {renderLimit(limit, LimitPart.DEFAULT, resource)} - {renderLimit(limit, LimitPart.DEFAULT_REQUEST, resource)} - {renderLimit(limit, LimitPart.MAX_LIMIT_REQUEST_RATIO, resource)} - - ); -} - -function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) { - - return resources.map(resource => ( - - { - limits.map(limit => renderResourceLimits(limit, resource)) - } - - )); -} - -interface Dependencies { - logger: Logger; -} - -@observer -class NonInjectedLimitRangeDetails extends React.Component { - render() { - const { object: limitRange, logger } = this.props; - - if (!limitRange) { - return null; - } - - if (!(limitRange instanceof LimitRange)) { - logger.error("[LimitRangeDetails]: passed object that is not an instanceof LimitRange", limitRange); - - return null; - } - - const containerLimits = limitRange.getContainerLimits(); - const podLimits = limitRange.getPodLimits(); - const pvcLimits = limitRange.getPVCLimits(); - - return ( -
- {containerLimits.length > 0 && ( - - { - renderLimitDetails(containerLimits, [Resource.CPU, Resource.MEMORY, Resource.EPHEMERAL_STORAGE]) - } - - )} - {podLimits.length > 0 && ( - - { - renderLimitDetails(podLimits, [Resource.CPU, Resource.MEMORY, Resource.EPHEMERAL_STORAGE]) - } - - )} - {pvcLimits.length > 0 && ( - - { - renderLimitDetails(pvcLimits, [Resource.STORAGE]) - } - - )} -
- ); - } -} - -export const LimitRangeDetails = withInjectables(NonInjectedLimitRangeDetails, { - getProps: (di, props) => ({ - ...props, - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges-route-component.injectable.ts b/src/renderer/components/+config-limit-ranges/limit-ranges-route-component.injectable.ts deleted file mode 100644 index b79ffaa8f6..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-ranges-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { LimitRanges } from "./limit-ranges"; -import limitRangesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const limitRangesRouteComponentInjectable = getInjectable({ - id: "limit-ranges-route-component", - - instantiate: (di) => ({ - route: di.inject(limitRangesRouteInjectable), - Component: LimitRanges, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default limitRangesRouteComponentInjectable; diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges-sidebar-items.injectable.tsx b/src/renderer/components/+config-limit-ranges/limit-ranges-sidebar-items.injectable.tsx deleted file mode 100644 index f172cd2e8d..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-ranges-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 limitRangesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToLimitRangesInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable"; - -const limitRangesSidebarItemsInjectable = getInjectable({ - id: "limit-ranges-sidebar-items", - - instantiate: (di) => { - const route = di.inject(limitRangesRouteInjectable); - const navigateToLimitRanges = di.inject(navigateToLimitRangesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "limit-ranges", - parentId: configSidebarItemId, - title: "Limit Ranges", - onClick: navigateToLimitRanges, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 40, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default limitRangesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges.scss b/src/renderer/components/+config-limit-ranges/limit-ranges.scss deleted file mode 100644 index 1e26724d5c..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-ranges.scss +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.LimitRanges { - .TableCell { - &.warning { - @include table-cell-warning; - } - } - - a.filterNamespace { - border-bottom: unset; - } -} diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx b/src/renderer/components/+config-limit-ranges/limit-ranges.tsx deleted file mode 100644 index b89c7478f9..0000000000 --- a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./limit-ranges.scss"; - -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import React from "react"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { LimitRangeStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import limitRangeStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - age = "age", -} - -interface Dependencies { - limitRangeStore: LimitRangeStore; -} - -@observer -class NonInjectedLimitRanges extends React.Component { - render() { - return ( - - limitRange.getName(), - [columnId.namespace]: limitRange => limitRange.getNs(), - [columnId.age]: limitRange => -limitRange.getCreationTimestamp(), - }} - searchFilters={[ - item => item.getName(), - item => item.getNs(), - ]} - renderHeaderTitle="Limit Ranges" - 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: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={limitRange => [ - limitRange.getName(), - , - , - , - ]} - /> - - ); - } -} - -export const LimitRanges = withInjectables(NonInjectedLimitRanges, { - getProps: (di, props) => ({ - ...props, - limitRangeStore: di.inject(limitRangeStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-limit-ranges/store.injectable.ts b/src/renderer/components/+config-limit-ranges/store.injectable.ts deleted file mode 100644 index 5109b25079..0000000000 --- a/src/renderer/components/+config-limit-ranges/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import limitRangeApiInjectable from "../../../common/k8s-api/endpoints/limit-range.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { LimitRangeStore } from "./store"; - -const limitRangeStoreInjectable = getInjectable({ - id: "limit-range-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "limitRangeStore is only available in certain environments"); - - const api = di.inject(limitRangeApiInjectable); - - return new LimitRangeStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default limitRangeStoreInjectable; diff --git a/src/renderer/components/+config-limit-ranges/store.ts b/src/renderer/components/+config-limit-ranges/store.ts deleted file mode 100644 index b38b0b30b5..0000000000 --- a/src/renderer/components/+config-limit-ranges/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { LimitRange, LimitRangeApi } from "../../../common/k8s-api/endpoints/limit-range.api"; - -export class LimitRangeStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-maps/config-map-details.scss b/src/renderer/components/+config-maps/config-map-details.scss deleted file mode 100644 index edd9db2e71..0000000000 --- a/src/renderer/components/+config-maps/config-map-details.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ConfigMapDetails { - .data { - margin-bottom: $margin * 2; - - .name { - color: var(--textColorSecondary); - font-weight: $font-weight-bold; - padding-bottom: $padding * 0.5; - } - } - - .save-btn { - margin-top: $margin; - } -} diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx deleted file mode 100644 index b03cdda2c9..0000000000 --- a/src/renderer/components/+config-maps/config-map-details.tsx +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./config-map-details.scss"; - -import React from "react"; -import { autorun, makeObservable, observable } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { DrawerTitle } from "../drawer"; -import type { ShowNotification } from "../notifications"; -import { Button } from "../button"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { ConfigMap } from "../../../common/k8s-api/endpoints"; -import type { Logger } from "../../../common/logger"; -import type { ConfigMapStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import configMapStoreInjectable from "./store.injectable"; -import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; -import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import { MonacoEditor } from "../monaco-editor"; - -export interface ConfigMapDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - configMapStore: ConfigMapStore; - logger: Logger; - showSuccessNotification: ShowNotification; - showErrorNotification: ShowNotification; -} - -@observer -class NonInjectedConfigMapDetails extends React.Component { - @observable isSaving = false; - @observable data = observable.map(); - - constructor(props: ConfigMapDetailsProps & Dependencies) { - super(props); - makeObservable(this); - } - - async componentDidMount() { - disposeOnUnmount(this, [ - autorun(() => { - const { object: configMap } = this.props; - - if (configMap) { - this.data.replace(configMap.data); // refresh - } - }), - ]); - } - - save = async () => { - const { object: configMap, configMapStore } = this.props; - - try { - this.isSaving = true; - await configMapStore.update(configMap, { - ...configMap, - data: Object.fromEntries(this.data), - }); - this.props.showSuccessNotification(( -

- {"ConfigMap "} - {configMap.getName()} - {" successfully updated."} -

- )); - } catch (error) { - this.props.showErrorNotification(`Failed to save config map: ${error}`); - } finally { - this.isSaving = false; - } - }; - - render() { - const { object: configMap, logger } = this.props; - - if (!configMap) { - return null; - } - - if (!(configMap instanceof ConfigMap)) { - logger.error("[ConfigMapDetails]: passed object that is not an instanceof ConfigMap", configMap); - - return null; - } - - const data = Array.from(this.data.entries()); - - return ( -
- { - data.length > 0 && ( - <> - Data - { - data.map(([name, value = ""]) => ( -
-
{name}
- this.data.set(name, v)} - setInitialHeight - /> -
- )) - } -
- ); - } -} - -export const ConfigMapDetails = withInjectables(NonInjectedConfigMapDetails, { - getProps: (di, props) => ({ - ...props, - configMapStore: di.inject(configMapStoreInjectable), - showSuccessNotification: di.inject(showSuccessNotificationInjectable), - showErrorNotification: di.inject(showErrorNotificationInjectable), - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+config-maps/config-maps-route-component.injectable.ts b/src/renderer/components/+config-maps/config-maps-route-component.injectable.ts deleted file mode 100644 index fd9c7df320..0000000000 --- a/src/renderer/components/+config-maps/config-maps-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { ConfigMaps } from "./config-maps"; -import configMapsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const configMapsRouteComponentInjectable = getInjectable({ - id: "config-maps-route-component", - - instantiate: (di) => ({ - route: di.inject(configMapsRouteInjectable), - Component: ConfigMaps, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default configMapsRouteComponentInjectable; diff --git a/src/renderer/components/+config-maps/config-maps-sidebar-items.injectable.tsx b/src/renderer/components/+config-maps/config-maps-sidebar-items.injectable.tsx deleted file mode 100644 index 638273b128..0000000000 --- a/src/renderer/components/+config-maps/config-maps-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 configMapsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToConfigMapsInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable"; - -const configMapsSidebarItemsInjectable = getInjectable({ - id: "config-maps-sidebar-items", - - instantiate: (di) => { - const route = di.inject(configMapsRouteInjectable); - const navigateToConfigMaps = di.inject(navigateToConfigMapsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "config-maps", - parentId: configSidebarItemId, - title: "ConfigMaps", - onClick: navigateToConfigMaps, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 10, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default configMapsSidebarItemsInjectable; diff --git a/src/renderer/components/+config-maps/config-maps.scss b/src/renderer/components/+config-maps/config-maps.scss deleted file mode 100644 index cf81669f79..0000000000 --- a/src/renderer/components/+config-maps/config-maps.scss +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ConfigMaps { - .TableCell { - &.name { - flex: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.keys { - flex: 2.5; - } - - &.age { - flex: .5; - } - - - } -} diff --git a/src/renderer/components/+config-maps/config-maps.tsx b/src/renderer/components/+config-maps/config-maps.tsx deleted file mode 100644 index b422c6e916..0000000000 --- a/src/renderer/components/+config-maps/config-maps.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./config-maps.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { ConfigMapStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import configMapStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - keys = "keys", - age = "age", -} - -interface Dependencies { - configMapStore: ConfigMapStore; -} - -@observer -class NonInjectedConfigMaps extends React.Component { - render() { - return ( - - configMap.getName(), - [columnId.namespace]: configMap => configMap.getNs(), - [columnId.keys]: configMap => configMap.getKeys(), - [columnId.age]: configMap => -configMap.getCreationTimestamp(), - }} - searchFilters={[ - configMap => configMap.getSearchFields(), - configMap => configMap.getKeys(), - ]} - renderHeaderTitle="Config Maps" - 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: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={configMap => [ - configMap.getName(), - , - , - configMap.getKeys().join(", "), - , - ]} - /> - - ); - } -} - -export const ConfigMaps = withInjectables(NonInjectedConfigMaps, { - getProps: (di, props) => ({ - ...props, - configMapStore: di.inject(configMapStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-maps/index.ts b/src/renderer/components/+config-maps/index.ts deleted file mode 100644 index f6750d8855..0000000000 --- a/src/renderer/components/+config-maps/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./config-maps"; -export * from "./config-map-details"; diff --git a/src/renderer/components/+config-maps/store.injectable.ts b/src/renderer/components/+config-maps/store.injectable.ts deleted file mode 100644 index e282e4d121..0000000000 --- a/src/renderer/components/+config-maps/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import configMapApiInjectable from "../../../common/k8s-api/endpoints/config-map.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { ConfigMapStore } from "./store"; - -const configMapStoreInjectable = getInjectable({ - id: "config-map-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "configMapStore is only available in certain environments"); - - const api = di.inject(configMapApiInjectable); - - return new ConfigMapStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default configMapStoreInjectable; diff --git a/src/renderer/components/+config-maps/store.ts b/src/renderer/components/+config-maps/store.ts deleted file mode 100644 index 3688f9fda7..0000000000 --- a/src/renderer/components/+config-maps/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { ConfigMap, ConfigMapApi, ConfigMapData } from "../../../common/k8s-api/endpoints/config-map.api"; - -export class ConfigMapStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-pod-disruption-budgets/index.ts b/src/renderer/components/+config-pod-disruption-budgets/index.ts deleted file mode 100644 index 421024572f..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./pod-disruption-budgets"; -export * from "./pod-disruption-budgets-details"; diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.scss b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.scss deleted file mode 100644 index 92245910f6..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDisruptionBudgetDetails { -} diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx deleted file mode 100644 index a6b6e48342..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-disruption-budgets-details.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import { Badge } from "../badge"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - logger: Logger; -} - -@observer -class NonInjectedPodDisruptionBudgetDetails extends React.Component { - - render() { - const { object: pdb } = this.props; - - if (!pdb) { - return null; - } - - if (!(pdb instanceof PodDisruptionBudget)) { - this.props.logger.error("[PodDisruptionBudgetDetails]: passed object that is not an instanceof PodDisruptionBudget", pdb); - - return null; - } - - const selectors = pdb.getSelectors(); - - return ( -
- {selectors.length > 0 && ( - - { - selectors.map(label => ) - } - - )} - - - {pdb.getMinAvailable()} - - - - {pdb.getMaxUnavailable()} - - - - {pdb.getCurrentHealthy()} - - - - {pdb.getDesiredHealthy()} - - -
- ); - } -} - -export const PodDisruptionBudgetDetails = withInjectables(NonInjectedPodDisruptionBudgetDetails, { - getProps: (di, props) => ({ - ...props, - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-route-component.injectable.ts b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-route-component.injectable.ts deleted file mode 100644 index e963b9408e..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { PodDisruptionBudgets } from "./pod-disruption-budgets"; -import podDisruptionBudgetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const podDisruptionBudgetsRouteComponentInjectable = getInjectable({ - id: "pod-disruption-budgets-route-component", - - instantiate: (di) => ({ - route: di.inject(podDisruptionBudgetsRouteInjectable), - Component: PodDisruptionBudgets, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default podDisruptionBudgetsRouteComponentInjectable; diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-sidebar-items.injectable.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-sidebar-items.injectable.tsx deleted file mode 100644 index a4e6092b33..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 podDisruptionBudgetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToPodDisruptionBudgetsInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable"; - -const podDisruptionBudgetsSidebarItemsInjectable = getInjectable({ - id: "pod-disruption-budgets-sidebar-items", - - instantiate: (di) => { - const route = di.inject(podDisruptionBudgetsRouteInjectable); - const navigateToPodDisruptionBudgets = di.inject(navigateToPodDisruptionBudgetsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "pod-disruption-budgets", - parentId: configSidebarItemId, - title: "Pod Disruption Budgets", - onClick: navigateToPodDisruptionBudgets, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 60, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default podDisruptionBudgetsSidebarItemsInjectable; diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.scss b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.scss deleted file mode 100644 index 8dc2dfd721..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.scss +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDisruptionBudgets { - .TableCell { - &.name { - flex: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.keys { - flex: 2.5; - } - - &.age { - flex: .5; - } - - - } -} diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx deleted file mode 100644 index 9a57b972a4..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-disruption-budgets.scss"; - -import * as React from "react"; -import { observer } from "mobx-react"; -import type { PodDisruptionBudget } from "../../../common/k8s-api/endpoints/pod-disruption-budget.api"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { PodDisruptionBudgetStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import podDisruptionBudgetStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - minAvailable = "min-available", - maxUnavailable = "max-unavailable", - currentHealthy = "current-healthy", - desiredHealthy = "desired-healthy", - age = "age", -} - -export interface PodDisruptionBudgetsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - podDisruptionBudgetStore: PodDisruptionBudgetStore; -} - -@observer -class NonInjectedPodDisruptionBudgets extends React.Component { - render() { - return ( - - pdb.getName(), - [columnId.namespace]: pdb => pdb.getNs(), - [columnId.minAvailable]: pdb => pdb.getMinAvailable(), - [columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(), - [columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(), - [columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(), - [columnId.age]: pdb => -pdb.getCreationTimestamp(), - }} - searchFilters={[ - pdb => pdb.getSearchFields(), - ]} - renderHeaderTitle="Pod Disruption Budgets" - 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: "Min Available", className: "min-available", sortBy: columnId.minAvailable, id: columnId.minAvailable }, - { title: "Max Unavailable", className: "max-unavailable", sortBy: columnId.maxUnavailable, id: columnId.maxUnavailable }, - { title: "Current Healthy", className: "current-healthy", sortBy: columnId.currentHealthy, id: columnId.currentHealthy }, - { title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={pdb => [ - pdb.getName(), - , - , - pdb.getMinAvailable(), - pdb.getMaxUnavailable(), - pdb.getCurrentHealthy(), - pdb.getDesiredHealthy(), - , - ]} - /> - - ); - } -} - -export const PodDisruptionBudgets = withInjectables(NonInjectedPodDisruptionBudgets, { - getProps: (di, props) => ({ - ...props, - podDisruptionBudgetStore: di.inject(podDisruptionBudgetStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts deleted file mode 100644 index 3f3b83e21f..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import podDisruptionBudgetApiInjectable from "../../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { PodDisruptionBudgetStore } from "./store"; - -const podDisruptionBudgetStoreInjectable = getInjectable({ - id: "pod-disruption-budget-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "podDisruptionBudgetStore is only available in certain environments"); - - const api = di.inject(podDisruptionBudgetApiInjectable); - - return new PodDisruptionBudgetStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default podDisruptionBudgetStoreInjectable; diff --git a/src/renderer/components/+config-pod-disruption-budgets/store.ts b/src/renderer/components/+config-pod-disruption-budgets/store.ts deleted file mode 100644 index c2907da95c..0000000000 --- a/src/renderer/components/+config-pod-disruption-budgets/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { PodDisruptionBudget, PodDisruptionBudgetApi } from "../../../common/k8s-api/endpoints/pod-disruption-budget.api"; - -export class PodDisruptionBudgetStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-priority-classes/index.ts b/src/renderer/components/+config-priority-classes/index.ts deleted file mode 100644 index 73538c3e18..0000000000 --- a/src/renderer/components/+config-priority-classes/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./priority-classes"; -export * from "./priority-classes-details"; diff --git a/src/renderer/components/+config-priority-classes/priority-classes-details.scss b/src/renderer/components/+config-priority-classes/priority-classes-details.scss deleted file mode 100644 index 92697c079e..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes-details.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PriorityClassesDetails {} \ No newline at end of file diff --git a/src/renderer/components/+config-priority-classes/priority-classes-details.tsx b/src/renderer/components/+config-priority-classes/priority-classes-details.tsx deleted file mode 100644 index 7caf5190a1..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes-details.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./priority-classes.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { PriorityClass } from "../../../common/k8s-api/endpoints"; - -export interface PriorityClassesDetailsProps extends KubeObjectDetailsProps { -} - -@observer -export class PriorityClassesDetails extends React.Component { - - render() { - const { object: pc } = this.props; - - return ( -
- - {pc.getDescription()} - - - - {pc.getValue()} - - - - {pc.getGlobalDefault()} - - -
- ); - } -} diff --git a/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx b/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx deleted file mode 100644 index 2823467fdf..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 priorityClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToPriorityClassesInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable"; - -const priorityClassesSidebarItemsInjectable = getInjectable({ - id: "priority-classes-sidebar-items", - - instantiate: (di) => { - const route = di.inject(priorityClassesRouteInjectable); - const navigateToPriorityClasses = di.inject(navigateToPriorityClassesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "priority-classes", - parentId: configSidebarItemId, - title: "Priority Classes", - onClick: navigateToPriorityClasses, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 70, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default priorityClassesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts b/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts deleted file mode 100644 index 258d3eb336..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { PriorityClasses } from "./priority-classes"; -import priorityClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const priorityClassesRouteComponentInjectable = getInjectable({ - id: "priority-classes-route-component", - - instantiate: (di) => ({ - route: di.inject(priorityClassesRouteInjectable), - Component: PriorityClasses, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default priorityClassesRouteComponentInjectable; diff --git a/src/renderer/components/+config-priority-classes/priority-classes.scss b/src/renderer/components/+config-priority-classes/priority-classes.scss deleted file mode 100644 index 082f3ae68e..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PriorityClasses { - .TableCell { - &.name { - flex: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.keys { - flex: 2.5; - } - - &.age { - flex: .5; - } - } -} \ No newline at end of file diff --git a/src/renderer/components/+config-priority-classes/priority-classes.tsx b/src/renderer/components/+config-priority-classes/priority-classes.tsx deleted file mode 100644 index 4706fd0269..0000000000 --- a/src/renderer/components/+config-priority-classes/priority-classes.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./priority-classes.scss"; - -import * as React from "react"; -import { observer } from "mobx-react"; -import type { PriorityClass } from "../../../common/k8s-api/endpoints/priority-class.api"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import { autoBind } from "../../../common/utils"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import priorityClassStoreInjectable from "./store.injectable"; -import type { PriorityClassStore } from "./store"; - -enum columnId { - name = "name", - value = "value", - globalDefault = "global-default", - age = "age", -} - -export interface PriorityClassesProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - priorityClassStore: PriorityClassStore; -} - -@observer -class NonInjectedPriorityClasses extends React.Component { - constructor(props: PriorityClassesProps & Dependencies) { - super(props); - autoBind(this); - } - - render() { - const { priorityClassStore } = this.props; - - return ( - - pc.getName(), - [columnId.value]: pc => pc.getValue(), - [columnId.globalDefault]: pc => pc.getGlobalDefault(), - [columnId.age]: pc => -pc.getCreationTimestamp(), - }} - searchFilters={[ - pc => pc.getSearchFields(), - ]} - renderHeaderTitle="Priority Classes" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Value", className: "value", sortBy: columnId.value, id: columnId.value }, - { title: "Global Default", className: "global-default", sortBy: columnId.globalDefault, id: columnId.globalDefault }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={pc => [ - pc.getName(), - , - pc.getValue(), - pc.getGlobalDefault(), - , - ]} - /> - - ); - } -} - -export const PriorityClasses = withInjectables(NonInjectedPriorityClasses, { - getProps: (di, props) => ({ - ...props, - priorityClassStore: di.inject(priorityClassStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-priority-classes/store.injectable.ts b/src/renderer/components/+config-priority-classes/store.injectable.ts deleted file mode 100644 index e3e22a53b9..0000000000 --- a/src/renderer/components/+config-priority-classes/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { PriorityClassStore } from "./store"; - -const priorityClassStoreInjectable = getInjectable({ - id: "priority-class-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "priorityClassStore is only available in certain environments"); - - const api = di.inject(priorityClassApiInjectable); - - return new PriorityClassStore({ - context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default priorityClassStoreInjectable; diff --git a/src/renderer/components/+config-priority-classes/store.ts b/src/renderer/components/+config-priority-classes/store.ts deleted file mode 100644 index f11fdce838..0000000000 --- a/src/renderer/components/+config-priority-classes/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { PriorityClass, PriorityClassApi } from "../../../common/k8s-api/endpoints/priority-class.api"; - -export class PriorityClassStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts deleted file mode 100644 index 76af08fc88..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/close.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import addQuotaDialogOpenStateInjectable from "./open-state.injectable"; - -const closeAddQuotaDialogInjectable = getInjectable({ - id: "close-add-quota-dialog", - instantiate: (di) => { - const state = di.inject(addQuotaDialogOpenStateInjectable); - - return action(() => state.set(false)); - }, -}); - -export default closeAddQuotaDialogInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts deleted file mode 100644 index 3f91737e72..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/is-open.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 addQuotaDialogOpenStateInjectable from "./open-state.injectable"; - -const isAddQuotaDialogOpenInjectable = getInjectable({ - id: "is-add-quota-dialog-open", - instantiate: (di) => { - const state = di.inject(addQuotaDialogOpenStateInjectable); - - return computed(() => state.get()); - }, -}); - -export default isAddQuotaDialogOpenInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts deleted file mode 100644 index 11af7e8d38..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/open-state.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 { observable } from "mobx"; - -const addQuotaDialogOpenStateInjectable = getInjectable({ - id: "add-quota-dialog-open-state", - instantiate: () => observable.box(false), -}); - -export default addQuotaDialogOpenStateInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts b/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts deleted file mode 100644 index 243f6b0fd9..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/open.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import addQuotaDialogOpenStateInjectable from "./open-state.injectable"; - -const openAddQuotaDialogInjectable = getInjectable({ - id: "open-add-quota-dialog", - instantiate: (di) => { - const state = di.inject(addQuotaDialogOpenStateInjectable); - - return action(() => state.set(true)); - }, -}); - -export default openAddQuotaDialogInjectable; diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/view.scss b/src/renderer/components/+config-resource-quotas/add-dialog/view.scss deleted file mode 100644 index 941f1c9a49..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/view.scss +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.AddQuotaDialog { - .quota-select { - flex-basis: 55%; - } - - .quota-entries { - display: flex; - gap: 8px; - margin-left: -1px; - flex-wrap: wrap; - - &:empty { - display: none; - } - - .quota { - border: 1px solid var(--halfGray); - border-radius: $radius; - padding: $padding * 0.5 $padding; - transition: all 150ms ease; - display: flex; - align-items: center; - gap: 8px; - - &:hover { - box-shadow: inset 0 0 0 1px var(--borderColor); - } - - .name { - font-weight: $font-weight-bold; - } - - .value { - color: var(--contentColor); - } - - .Icon:hover { - color: black; - } - } - } -} diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx b/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx deleted file mode 100644 index 21ee1bde3c..0000000000 --- a/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import React from "react"; -import type { IComputedValue } from "mobx"; -import { computed, observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import { Input } from "../../input"; -import { systemName } from "../../input/input_validators"; -import type { IResourceQuotaValues, ResourceQuotaApi } from "../../../../common/k8s-api/endpoints"; -import { Select } from "../../select"; -import { Icon } from "../../icon"; -import { Button } from "../../button"; -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import { SubTitle } from "../../layout/sub-title"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import closeAddQuotaDialogInjectable from "./close.injectable"; -import isAddQuotaDialogOpenInjectable from "./is-open.injectable"; -import resourceQuotaApiInjectable from "../../../../common/k8s-api/endpoints/resource-quota.api.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface AddQuotaDialogProps extends DialogProps { -} - -interface Dependencies { - resourceQuotaApi: ResourceQuotaApi; - isAddQuotaDialogOpen: IComputedValue; - closeAddQuotaDialog: () => void; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -const defaultQuotas = JSON.stringify({ - "limits.cpu": "", - "limits.memory": "", - "requests.cpu": "", - "requests.memory": "", - "requests.storage": "", - "persistentvolumeclaims": "", - "count/pods": "", - "count/persistentvolumeclaims": "", - "count/services": "", - "count/secrets": "", - "count/configmaps": "", - "count/replicationcontrollers": "", - "count/deployments.apps": "", - "count/replicasets.apps": "", - "count/statefulsets.apps": "", - "count/jobs.batch": "", - "count/cronjobs.batch": "", - "count/deployments.extensions": "", -} as IResourceQuotaValues); - -@observer -class NonInjectedAddQuotaDialog extends React.Component { - - public defaultNamespace = "default"; - - @observable quotaName = ""; - @observable quotaSelectValue: string | null = null; - @observable quotaInputValue = ""; - @observable namespace: string | null = this.defaultNamespace; - @observable quotas: IResourceQuotaValues = JSON.parse(defaultQuotas); - - constructor(props: AddQuotaDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - @computed get quotaEntries() { - return Object.entries(this.quotas) - .filter(([, value]) => !!value?.trim()); - } - - private getQuotaOptionLabelIconMaterial(quota: string) { - if (quota.endsWith(".cpu") || quota.endsWith(".memory")) { - return "memory"; - } - - if (quota.endsWith(".storage") || quota === "persistentvolumeclaims") { - return "storage"; - } - - if (quota.startsWith("count/")) { - return "looks_one"; - } - - return undefined; - } - - setQuota = () => { - if (!this.quotaSelectValue) return; - this.quotas[this.quotaSelectValue] = this.quotaInputValue; - this.quotaInputValue = ""; - }; - - close = () => { - this.props.closeAddQuotaDialog(); - }; - - reset = () => { - this.quotaName = ""; - this.quotaSelectValue = ""; - this.quotaInputValue = ""; - this.namespace = this.defaultNamespace; - this.quotas = JSON.parse(defaultQuotas); - }; - - addQuota = async () => { - const { quotaName, namespace } = this; - - if (!quotaName || !namespace) { - return; - } - - try { - const quotas = Object.fromEntries(this.quotaEntries); - - await this.props.resourceQuotaApi.create({ namespace, name: quotaName }, { - spec: { - hard: quotas, - }, - }); - this.close(); - } catch (err) { - this.props.showCheckedErrorNotification(err, "Unknown error occured while creating ResourceQuota"); - } - }; - - onInputQuota = (evt: React.KeyboardEvent) => { - switch (evt.key) { - case "Enter": - this.setQuota(); - evt.preventDefault(); // don't submit form - break; - } - }; - - render() { - const { closeAddQuotaDialog, isAddQuotaDialogOpen, resourceQuotaApi, ...dialogProps } = this.props; - const header =
Create ResourceQuota
; - - return ( - - - -
- this.quotaName = v.toLowerCase()} - className="box grow" - /> -
- - - this.namespace = option?.value ?? null} - /> - - -
- this.quotaInputValue = v} - onKeyDown={this.onInputQuota} - className="box grow" - /> - -
-
- {this.quotaEntries.map(([quota, value]) => ( -
-
{quota}
-
{value}
- this.quotas[quota] = ""} /> -
- ))} -
-
-
-
- ); - } -} - -export const AddQuotaDialog = withInjectables(NonInjectedAddQuotaDialog, { - getProps: (di, props) => ({ - ...props, - closeAddQuotaDialog: di.inject(closeAddQuotaDialogInjectable), - isAddQuotaDialogOpen: di.inject(isAddQuotaDialogOpenInjectable), - resourceQuotaApi: di.inject(resourceQuotaApiInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+config-resource-quotas/index.ts b/src/renderer/components/+config-resource-quotas/index.ts deleted file mode 100644 index 36cad725c1..0000000000 --- a/src/renderer/components/+config-resource-quotas/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./resource-quotas"; -export * from "./resource-quota-details"; diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.scss b/src/renderer/components/+config-resource-quotas/resource-quota-details.scss deleted file mode 100644 index 30d3564066..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.scss +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ResourceQuotaDetails { - .quota-list { - .param { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - padding-bottom: $padding * 2; - - .LineProgress { - margin-top: 3px; - width: 100%; - color: var(--colorInfo); - } - } - } -} diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx deleted file mode 100644 index 55ae188cbf..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./resource-quota-details.scss"; -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { observer } from "mobx-react"; -import { DrawerItem, DrawerTitle } from "../drawer"; -import { cpuUnitsToNumber, cssNames, unitsToBytes, metricUnitsToNumber, object, hasDefinedTupleValue } from "../../utils"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { ResourceQuota } from "../../../common/k8s-api/endpoints/resource-quota.api"; -import { LineProgress } from "../line-progress"; -import { Table, TableCell, TableHead, TableRow } from "../table"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps { -} - -function transformUnit(name: string, value: string): number | undefined { - if (name.includes("memory") || name.includes("storage")) { - return unitsToBytes(value); - } - - if (name.includes("cpu")) { - return cpuUnitsToNumber(value); - } - - return metricUnitsToNumber(value); -} - -function renderQuotas(quota: ResourceQuota): JSX.Element[] { - const { hard = {}, used = {}} = quota.status ?? {}; - - return object.entries(hard) - .filter(hasDefinedTupleValue) - .map(([name, rawMax]) => { - const rawCurrent = used[name] ?? "0"; - const current = transformUnit(name, rawCurrent); - const max = transformUnit(name, rawMax); - - if (current === undefined || max === undefined) { - return ( -
- {name} - - {`${rawCurrent} / ${rawMax}`} - -
- ); - } - - const usage = max === 0 - ? 100 // special case 0 max as always 100% usage - : current / max * 100; - - return ( -
- {name} - - {`${rawCurrent} / ${rawMax}`} - - - {`Set: ${rawMax}. Usage: ${+usage.toFixed(2)}%`} -

- )} - /> -
- ); - }); -} - -interface Dependencies { - logger: Logger; -} - -@observer -class NonInjectedResourceQuotaDetails extends React.Component { - render() { - const { object: quota } = this.props; - - if (!quota) { - return null; - } - - if (!(quota instanceof ResourceQuota)) { - this.props.logger.error("[ResourceQuotaDetails]: passed object that is not an instanceof ResourceQuota", quota); - - return null; - } - - return ( -
- - {renderQuotas(quota)} - - - {quota.getScopeSelector().length > 0 && ( - <> - Scope Selector - - - Operator - Scope name - Values - - { - quota.getScopeSelector().map((selector, index) => { - const { operator, scopeName, values } = selector; - - return ( - - {operator} - {scopeName} - {values.join(", ")} - - ); - }) - } -
- - )} -
- ); - } -} - -export const ResourceQuotaDetails = withInjectables(NonInjectedResourceQuotaDetails, { - getProps: (di, props) => ({ - ...props, - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas-route-component.injectable.ts b/src/renderer/components/+config-resource-quotas/resource-quotas-route-component.injectable.ts deleted file mode 100644 index 8e138a7064..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quotas-route-component.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 { ResourceQuotas } from "./resource-quotas"; -import resourceQuotasRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable"; -import { - routeSpecificComponentInjectionToken, -} from "../../routes/route-specific-component-injection-token"; - -const resourceQuotasRouteComponentInjectable = getInjectable({ - id: "resource-quotas-route-component", - - instantiate: (di) => ({ - route: di.inject(resourceQuotasRouteInjectable), - Component: ResourceQuotas, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default resourceQuotasRouteComponentInjectable; diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas-sidebar-items.injectable.tsx b/src/renderer/components/+config-resource-quotas/resource-quotas-sidebar-items.injectable.tsx deleted file mode 100644 index 7df66c8210..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quotas-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 resourceQuotasRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToResourceQuotasInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/navigate-to-resource-quotas.injectable"; - -const resourceQuotasSidebarItemsInjectable = getInjectable({ - id: "resource-quotas-sidebar-items", - - instantiate: (di) => { - const route = di.inject(resourceQuotasRouteInjectable); - const navigateToResourceQuotas = di.inject(navigateToResourceQuotasInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "resource-quotas", - parentId: configSidebarItemId, - title: "Resource Quotas", - onClick: navigateToResourceQuotas, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 30, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default resourceQuotasSidebarItemsInjectable; diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.scss b/src/renderer/components/+config-resource-quotas/resource-quotas.scss deleted file mode 100644 index b0bf869741..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quotas.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ResourceQuotas { - .TableCell { - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx deleted file mode 100644 index d769de7d07..0000000000 --- a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./resource-quotas.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { AddQuotaDialog } from "./add-dialog/view"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { ResourceQuotaStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import resourceQuotaStoreInjectable from "./store.injectable"; -import openAddQuotaDialogInjectable from "./add-dialog/open.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - age = "age", -} - -interface Dependencies { - resourceQuotaStore: ResourceQuotaStore; - openAddQuotaDialog: () => void; -} - -@observer -class NonInjectedResourceQuotas extends React.Component { - render() { - return ( - - resourceQuota.getName(), - [columnId.namespace]: resourceQuota => resourceQuota.getNs(), - [columnId.age]: resourceQuota => -resourceQuota.getCreationTimestamp(), - }} - searchFilters={[ - resourceQuota => resourceQuota.getSearchFields(), - resourceQuota => resourceQuota.getName(), - ]} - renderHeaderTitle="Resource Quotas" - 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: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={resourceQuota => [ - resourceQuota.getName(), - , - , - , - ]} - addRemoveButtons={{ - onAdd: this.props.openAddQuotaDialog, - addTooltip: "Create new ResourceQuota", - }} - /> - - - ); - } -} - -export const ResourceQuotas = withInjectables(NonInjectedResourceQuotas, { - getProps: (di, props) => ({ - ...props, - resourceQuotaStore: di.inject(resourceQuotaStoreInjectable), - openAddQuotaDialog: di.inject(openAddQuotaDialogInjectable), - }), -}); diff --git a/src/renderer/components/+config-resource-quotas/store.injectable.ts b/src/renderer/components/+config-resource-quotas/store.injectable.ts deleted file mode 100644 index cbbb014d05..0000000000 --- a/src/renderer/components/+config-resource-quotas/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import resourceQuotaApiInjectable from "../../../common/k8s-api/endpoints/resource-quota.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { ResourceQuotaStore } from "./store"; - -const resourceQuotaStoreInjectable = getInjectable({ - id: "resource-quota-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "resourceQuotaStore is only available in certain environments"); - - const api = di.inject(resourceQuotaApiInjectable); - - return new ResourceQuotaStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default resourceQuotaStoreInjectable; diff --git a/src/renderer/components/+config-resource-quotas/store.ts b/src/renderer/components/+config-resource-quotas/store.ts deleted file mode 100644 index 14272c3def..0000000000 --- a/src/renderer/components/+config-resource-quotas/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { ResourceQuota, ResourceQuotaApi } from "../../../common/k8s-api/endpoints/resource-quota.api"; - -export class ResourceQuotaStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-runtime-classes/index.ts b/src/renderer/components/+config-runtime-classes/index.ts deleted file mode 100644 index ff91dc6016..0000000000 --- a/src/renderer/components/+config-runtime-classes/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./runtime-classes"; -export * from "./runtime-classes-details"; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss deleted file mode 100644 index f72acb7bc9..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.RuntimeClassDetailsTolerations { - grid-template-columns: auto; - - .RuntimeClassTolerations { - margin-top: var(--margin); - } - - // Expanding value cell to cover 2 columns (whole Drawer width) - - >.name { - grid-row-start: 1; - grid-column-start: 1; - } - - >.value { - grid-row-start: 1; - grid-column-start: 1; - } - - .DrawerParamToggler>.params { - margin-left: var(--drawer-item-title-width); - } -} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx deleted file mode 100644 index c06e0c63bc..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./runtime-classes-details-tolerations.scss"; -import React from "react"; -import { DrawerParamToggler, DrawerItem } from "../drawer"; -import type { Toleration, KubeObject } from "../../../common/k8s-api/kube-object"; -import { RuntimeClassTolerations } from "./runtime-classes-tolerations"; - -export interface KubeObjectWithTolerations extends KubeObject { - getTolerations(): Toleration[]; -} - -export interface RuntimeClassDetailsTolerationsProps { - runtimeClass: KubeObjectWithTolerations; -} - -export function RuntimeClassDetailsTolerations({ runtimeClass: runtimeClass }: RuntimeClassDetailsTolerationsProps) { - const tolerations = runtimeClass.getTolerations(); - - if (!tolerations.length) return null; - - return ( - - - - - - ); -} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-details.scss deleted file mode 100644 index 55c5abcc09..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-details.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.RuntimeClassesDetails {} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx deleted file mode 100644 index f6fc61faeb..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./runtime-classes.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { RuntimeClass } from "../../../common/k8s-api/endpoints"; -import { Badge } from "../badge"; -import { RuntimeClassDetailsTolerations } from "./runtime-classes-details-tolerations"; - -export interface RuntimeClassesDetailsProps extends KubeObjectDetailsProps { -} - -@observer -export class RuntimeClassesDetails extends React.Component { - - render() { - const { object: rc } = this.props; - const nodeSelector = rc.getNodeSelectors(); - - return ( -
- - {rc.getHandler()} - - - - - - - - -
- ); - } -} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-items.injectable.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-items.injectable.tsx deleted file mode 100644 index 352504ca00..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 runtimeClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable"; -import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToRuntimeClassesInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable"; - -const runtimeClassesSidebarItemsInjectable = getInjectable({ - id: "runtime-classes-sidebar-items", - - instantiate: (di) => { - const route = di.inject(runtimeClassesRouteInjectable); - const navigateToRuntimeClasses = di.inject(navigateToRuntimeClassesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "runtime-classes", - parentId: configSidebarItemId, - title: "Runtime Classes", - onClick: navigateToRuntimeClasses, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 70, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default runtimeClassesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-route-component.injectable.ts b/src/renderer/components/+config-runtime-classes/runtime-classes-route-component.injectable.ts deleted file mode 100644 index 5118cdcc08..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { RuntimeClasses } from "./runtime-classes"; -import runtimeClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const runtimeClassesRouteComponentInjectable = getInjectable({ - id: "runtime-classes-route-component", - - instantiate: (di) => ({ - route: di.inject(runtimeClassesRouteInjectable), - Component: RuntimeClasses, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default runtimeClassesRouteComponentInjectable; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss deleted file mode 100644 index e398f7424a..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.RuntimeClassTolerations { - .TableHead { - background-color: var(--drawerSubtitleBackground); - } - - .TableCell { - white-space: normal; - word-break: normal; - - &.key { - flex-grow: 3; - } - } -} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx deleted file mode 100644 index bcb1776459..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./runtime-classes-tolerations.scss"; -import React from "react"; -import uniqueId from "lodash/uniqueId"; - -import type { Toleration } from "../../../common/k8s-api/kube-object"; -import { Table, TableCell, TableHead, TableRow } from "../table"; - -export interface RuntimeClassTolerationsProps { - tolerations: Toleration[]; -} - -enum sortBy { - Key = "key", - Operator = "operator", - Effect = "effect", - Seconds = "seconds", - Value = "value", -} - -const getTableRow = (toleration: Toleration) => { - const { key, operator, effect, tolerationSeconds, value } = toleration; - - return ( - - {key} - {operator} - {value} - {effect} - {tolerationSeconds} - - ); -}; - -export function RuntimeClassTolerations({ tolerations }: RuntimeClassTolerationsProps) { - return ( - toleration.key, - [sortBy.Operator]: toleration => toleration.operator, - [sortBy.Effect]: toleration => toleration.effect, - [sortBy.Seconds]: toleration => toleration.tolerationSeconds, - }} - sortSyncWithUrl={false} - className="RuntimeClassTolerations" - renderRow={getTableRow} - > - - Key - Operator - Value - Effect - Seconds - -
- ); -} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes.scss b/src/renderer/components/+config-runtime-classes/runtime-classes.scss deleted file mode 100644 index 0f3b151059..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.RuntimeClasses { - .TableCell { - &.name { - flex: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.keys { - flex: 2.5; - } - - &.age { - flex: .5; - } - } -} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes.tsx deleted file mode 100644 index 990912f2d4..0000000000 --- a/src/renderer/components/+config-runtime-classes/runtime-classes.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./runtime-classes.scss"; - -import * as React from "react"; -import { observer } from "mobx-react"; -import type { RuntimeClass } from "../../../common/k8s-api/endpoints/runtime-class.api"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import { autoBind } from "../../../common/utils"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import runtimeClassStoreInjectable from "./store.injectable"; -import type { RuntimeClassStore } from "./store"; - -enum columnId { - name = "name", - handler = "handler", - age = "age", -} - -export interface RuntimeClassesProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - runtimeClassStore: RuntimeClassStore; -} - -@observer -class NonInjectedRuntimeClasses extends React.Component { - constructor(props: RuntimeClassesProps & Dependencies) { - super(props); - autoBind(this); - } - - render() { - const { runtimeClassStore } = this.props; - - return ( - - rc.getName(), - [columnId.handler]: rc => rc.getHandler(), - [columnId.age]: rc => -rc.getCreationTimestamp(), - }} - searchFilters={[ - rc => rc.getSearchFields(), - ]} - renderHeaderTitle="Runtime Classes" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Handler", className: "handler", sortBy: columnId.handler, id: columnId.handler }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={rc => [ - rc.getName(), - , - rc.getHandler(), - , - ]} - /> - - ); - } -} - -export const RuntimeClasses = withInjectables(NonInjectedRuntimeClasses, { - getProps: (di, props) => ({ - ...props, - runtimeClassStore: di.inject(runtimeClassStoreInjectable), - }), -}); diff --git a/src/renderer/components/+config-runtime-classes/store.injectable.ts b/src/renderer/components/+config-runtime-classes/store.injectable.ts deleted file mode 100644 index 70c0efa6c4..0000000000 --- a/src/renderer/components/+config-runtime-classes/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { RuntimeClassStore } from "./store"; - -const runtimeClassStoreInjectable = getInjectable({ - id: "runtime-class-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "runtimeClassStore is only available in certain environments"); - - const api = di.inject(runtimeClassApiInjectable); - - return new RuntimeClassStore({ - context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default runtimeClassStoreInjectable; diff --git a/src/renderer/components/+config-runtime-classes/store.ts b/src/renderer/components/+config-runtime-classes/store.ts deleted file mode 100644 index bdb849ac85..0000000000 --- a/src/renderer/components/+config-runtime-classes/store.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { RuntimeClass, RuntimeClassApi } from "../../../common/k8s-api/endpoints/runtime-class.api"; - -export class RuntimeClassStore extends KubeObjectStore { -} diff --git a/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx b/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx deleted file mode 100644 index 6a586bdd0c..0000000000 --- a/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { SecretDetails } from "../secret-details"; -import { Secret, SecretType } from "../../../../common/k8s-api/endpoints"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import { renderFor } from "../../test-utils/renderFor"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import hostedClusterInjectable from "../../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../../cluster/create-cluster.injectable"; -import directoryForKubeConfigsInjectable from "../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; - -jest.mock("../../kube-object-meta/kube-object-meta", () => ({ - KubeObjectMeta: () => null, -})); - -describe("SecretDetails tests", () => { - it("should show the visibility toggle when the secret value is ''", () => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - const render = renderFor(di); - - di.override(directoryForUserDataInjectable, () => "/some-user-data"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - const secret = new Secret({ - apiVersion: "v1", - kind: "secret", - metadata: { - name: "test", - resourceVersion: "1", - uid: "uid", - namespace: "default", - selfLink: "/api/v1/secrets/default/test", - }, - data: { - foobar: "", - }, - type: SecretType.Opaque, - }); - const result = render(); - - expect(result.getByTestId("foobar-secret-entry").querySelector(".Icon")).toBeDefined(); - }); -}); diff --git a/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts deleted file mode 100644 index 98bf6addcf..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/close.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import addSecretDialogOpenStateInjectable from "./state.injectable"; - -const closeAddSecretDialogInjectable = getInjectable({ - id: "close-add-secret-dialog", - instantiate: (di) => { - const state = di.inject(addSecretDialogOpenStateInjectable); - - return action(() => state.set(false)); - }, -}); - -export default closeAddSecretDialogInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts deleted file mode 100644 index 2427695923..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/is-open.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 addSecretDialogOpenStateInjectable from "./state.injectable"; - -const isAddSecretDialogOpenInjectable = getInjectable({ - id: "is-add-secret-dialog-open", - instantiate: (di) => { - const state = di.inject(addSecretDialogOpenStateInjectable); - - return computed(() => state.get()); - }, -}); - -export default isAddSecretDialogOpenInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts deleted file mode 100644 index 971cbe4e7a..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/open.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import addSecretDialogOpenStateInjectable from "./state.injectable"; - -const openAddSecretDialogInjectable = getInjectable({ - id: "open-add-secret-dialog", - instantiate: (di) => { - const state = di.inject(addSecretDialogOpenStateInjectable); - - return action(() => state.set(true)); - }, -}); - -export default openAddSecretDialogInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts b/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts deleted file mode 100644 index de2533d3cd..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/state.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 { observable } from "mobx"; - -const addSecretDialogOpenStateInjectable = getInjectable({ - id: "add-secret-dialog-state", - instantiate: () => observable.box(false), -}); - -export default addSecretDialogOpenStateInjectable; diff --git a/src/renderer/components/+config-secrets/add-dialog/view.scss b/src/renderer/components/+config-secrets/add-dialog/view.scss deleted file mode 100644 index 9cf91da24b..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/view.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.AddSecretDialog { - --flex-gap: #{$margin * 1.5}; - - .Icon { - --color-active: black; - } - - .remove-icon { - flex: 0 0; - width: 18px; // icon size - } - - .fields-title { - display: grid; - grid-template-columns: min-content auto; - align-items: center; - gap: $margin * 0.5; - } -} diff --git a/src/renderer/components/+config-secrets/add-dialog/view.tsx b/src/renderer/components/+config-secrets/add-dialog/view.tsx deleted file mode 100644 index cf68db93be..0000000000 --- a/src/renderer/components/+config-secrets/add-dialog/view.tsx +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import React from "react"; -import type { IComputedValue } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import { Input } from "../../input"; -import { systemName } from "../../input/input_validators"; -import type { SecretApi } from "../../../../common/k8s-api/endpoints"; -import { reverseSecretTypeMap, SecretType } from "../../../../common/k8s-api/endpoints"; -import { SubTitle } from "../../layout/sub-title"; -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import { Select } from "../../select"; -import { Icon } from "../../icon"; -import { base64, iter } from "../../../utils"; -import upperFirst from "lodash/upperFirst"; -import { fromEntries } from "../../../../common/utils/objects"; -import type { ShowDetails } from "../../kube-detail-params/show-details.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import closeAddSecretDialogInjectable from "./close.injectable"; -import secretApiInjectable from "../../../../common/k8s-api/endpoints/secret.api.injectable"; -import showDetailsInjectable from "../../kube-detail-params/show-details.injectable"; -import isAddSecretDialogOpenInjectable from "./is-open.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface AddSecretDialogProps extends Partial { -} - -interface SecretTemplateField { - key: string; - value?: string; - required?: boolean; -} - -interface SecretTemplate { - [field: string]: SecretTemplateField[] | undefined; - annotations?: SecretTemplateField[]; - labels?: SecretTemplateField[]; - data?: SecretTemplateField[]; -} - -type ISecretField = keyof SecretTemplate; - -interface Dependencies { - secretApi: SecretApi; - isAddSecretDialogOpen: IComputedValue; - closeAddSecretDialog: () => void; - showDetails: ShowDetails; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedAddSecretDialog extends React.Component { - constructor(props: AddSecretDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - private secretTemplate: Partial> = { - [SecretType.Opaque]: {}, - [SecretType.ServiceAccountToken]: { - annotations: [ - { key: "kubernetes.io/service-account.name", required: true }, - { key: "kubernetes.io/service-account.uid", required: true }, - ], - }, - }; - - @observable secret = this.secretTemplate; - @observable name = ""; - @observable namespace = "default"; - @observable type = SecretType.Opaque; - - reset = () => { - this.name = ""; - this.secret = this.secretTemplate; - }; - - close = () => { - this.props.closeAddSecretDialog(); - }; - - private getDataFromFields = (fields: SecretTemplateField[] = [], processValue: (val: string) => string = (val => val)) => { - return iter.chain(fields.values()) - .filterMap(({ key, value }) => ( - value - ? [key, processValue(value)] as const - : undefined - )) - .collect(fromEntries); - }; - - createSecret = async () => { - const { name, namespace, type } = this; - const { data = [], labels = [], annotations = [] } = this.secret[type] ?? {}; - - try { - const newSecret = await this.props.secretApi.create({ namespace, name }, { - type, - data: this.getDataFromFields(data, val => val ? base64.encode(val) : ""), - metadata: { - name, - namespace, - annotations: this.getDataFromFields(annotations), - labels: this.getDataFromFields(labels), - }, - }); - - this.props.showDetails(newSecret?.selfLink); - this.close(); - } catch (err) { - this.props.showCheckedErrorNotification(err, "Unknown error occured while creating a Secret"); - } - }; - - private getFields(field: ISecretField) { - return (this.secret[this.type] ??= {})[field] ??= []; - } - - addField = (field: ISecretField) => { - this.getFields(field).push({ key: "", value: "" }); - }; - - removeField = (field: ISecretField, index: number) => { - this.getFields(field).splice(index, 1); - }; - - renderFields(field: ISecretField) { - return ( - <> - - this.addField(field)} - /> - -
- {this.getFields(field) - .map((item, index) => ( -
- item.key = v} - /> - item.value = v} - /> - this.removeField(field, index)} - /> -
- ))} -
- - ); - } - - render() { - const { closeAddSecretDialog, isAddSecretDialogOpen, secretApi, showDetails, ...dialogProps } = this.props; - const { namespace, name, type } = this; - const header =
Create Secret
; - - return ( - - - -
- - this.name = v} - /> -
-
-
- - this.namespace = option?.value ?? "default"} - /> -
-
- - this.editData(name, value, !revealSecret)} - /> - {typeof decodedVal === "string" && ( - toggle(this.revealSecret, name)} - /> - )} -
-
- ); - }; - - renderData() { - const secrets = Object.entries(this.data); - - if (secrets.length === 0) { - return null; - } - - return ( - <> - Data - {secrets.map(this.renderSecret)} -
- ); - } -} - -export const AddClusterRoleDialog = withInjectables(NonInjectedAddClusterRoleDialog, { - getProps: (di, props) => ({ - ...props, - closeAddClusterRoleDialog: di.inject(closeAddClusterRoleDialogInjectable), - clusterRoleStore: di.inject(clusterRoleStoreInjectable), - showDetails: di.inject(showDetailsInjectable), - state: di.inject(addClusterRoleDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+cluster-roles/cluster-roles-route-component.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/cluster-roles-route-component.injectable.ts deleted file mode 100644 index 829ae16baa..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/cluster-roles-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { ClusterRoles } from "./view"; -import clusterRolesRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token"; - -const clusterRolesRouteComponentInjectable = getInjectable({ - id: "cluster-roles-route-component", - - instantiate: (di) => ({ - route: di.inject(clusterRolesRouteInjectable), - Component: ClusterRoles, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default clusterRolesRouteComponentInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/cluster-roles-sidebar-items.injectable.tsx b/src/renderer/components/+user-management/+cluster-roles/cluster-roles-sidebar-items.injectable.tsx deleted file mode 100644 index 54ae68ef44..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/cluster-roles-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 clusterRolesRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable"; -import { userManagementSidebarItemId } from "../user-management-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; -import navigateToClusterRolesInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/cluster-roles/navigate-to-cluster-roles.injectable"; - -const clusterRolesSidebarItemsInjectable = getInjectable({ - id: "cluster-roles-sidebar-items", - - instantiate: (di) => { - const route = di.inject(clusterRolesRouteInjectable); - const navigateToClusterRoles = di.inject(navigateToClusterRolesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "cluster-roles", - parentId: userManagementSidebarItemId, - title: "Cluster Roles", - onClick: navigateToClusterRoles, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 20, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default clusterRolesSidebarItemsInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/details.scss b/src/renderer/components/+user-management/+cluster-roles/details.scss deleted file mode 100644 index cf04a99af2..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/details.scss +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ClusterRoleDetails { - .rule { - display: grid; - grid-template-columns: min-content auto; - gap: $margin; - - border: 1px solid var(--borderColor); - border-radius: $radius; - padding: $padding * 1.5; - - > .name { - color: var(--textColorSecondary); - text-align: right; - white-space: nowrap; - } - - &:not(:last-child) { - margin-bottom: $margin * 2; - } - } -} diff --git a/src/renderer/components/+user-management/+cluster-roles/details.tsx b/src/renderer/components/+user-management/+cluster-roles/details.tsx deleted file mode 100644 index fa0e21143e..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/details.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./details.scss"; - -import { observer } from "mobx-react"; -import React from "react"; - -import { DrawerTitle } from "../../drawer"; -import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import type { ClusterRole } from "../../../../common/k8s-api/endpoints"; - -export interface ClusterRoleDetailsProps extends KubeObjectDetailsProps { -} - -@observer -export class ClusterRoleDetails extends React.Component { - render() { - const { object: clusterRole } = this.props; - - if (!clusterRole) return null; - const rules = clusterRole.getRules(); - - return ( -
- Rules - {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { - return ( -
- {resources && ( - <> -
Resources
-
{resources.join(", ")}
- - )} - {verbs && ( - <> -
Verbs
-
{verbs.join(", ")}
- - )} - {apiGroups && ( - <> -
Api Groups
-
- {apiGroups - .map(apiGroup => apiGroup === "" ? `'${apiGroup}'` : apiGroup) - .join(", ") - } -
- - )} - {resourceNames && ( - <> -
Resource Names
-
{resourceNames.join(", ")}
- - )} -
- ); - })} -
- ); - } -} diff --git a/src/renderer/components/+user-management/+cluster-roles/index.ts b/src/renderer/components/+user-management/+cluster-roles/index.ts deleted file mode 100644 index f55fa5914b..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export * from "./view"; -export * from "./details"; -export * from "./add-dialog/view"; diff --git a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts deleted file mode 100644 index d7ea148d03..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import { storesAndApisCanBeCreatedInjectionToken } from "../../../../common/k8s-api/stores-apis-can-be-created.token"; -import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role.api.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token"; -import { ClusterRoleStore } from "./store"; -import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; - -const clusterRoleStoreInjectable = getInjectable({ - id: "cluster-role-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleStore is only available in certain environments"); - - const api = di.inject(clusterRoleApiInjectable); - - return new ClusterRoleStore({ - context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default clusterRoleStoreInjectable; diff --git a/src/renderer/components/+user-management/+cluster-roles/store.ts b/src/renderer/components/+user-management/+cluster-roles/store.ts deleted file mode 100644 index a8df8bdb90..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/store.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { ClusterRole, ClusterRoleApi, ClusterRoleData } from "../../../../common/k8s-api/endpoints"; -import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; - -export class ClusterRoleStore extends KubeObjectStore { - protected sortItems(items: ClusterRole[]) { - return super.sortItems(items, [ - clusterRole => clusterRole.kind, - clusterRole => clusterRole.getName(), - ]); - } -} diff --git a/src/renderer/components/+user-management/+cluster-roles/view.scss b/src/renderer/components/+user-management/+cluster-roles/view.scss deleted file mode 100644 index bc9a75780d..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/view.scss +++ /dev/null @@ -1,11 +0,0 @@ -.ClusterRoles { - .help-icon { - margin-left: $margin * 0.5; - } - - .TableCell { - &.warning { - @include table-cell-warning; - } - } -} diff --git a/src/renderer/components/+user-management/+cluster-roles/view.tsx b/src/renderer/components/+user-management/+cluster-roles/view.tsx deleted file mode 100644 index 8a64e775b3..0000000000 --- a/src/renderer/components/+user-management/+cluster-roles/view.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import { observer } from "mobx-react"; -import React from "react"; -import { KubeObjectListLayout } from "../../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { AddClusterRoleDialog } from "./add-dialog/view"; -import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../../kube-object/age"; -import type { ClusterRoleStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import clusterRoleStoreInjectable from "./store.injectable"; -import type { OpenAddClusterRoleDialog } from "./add-dialog/open.injectable"; -import openAddClusterRoleDialogInjectable from "./add-dialog/open.injectable"; - -enum columnId { - name = "name", - namespace = "namespace", - age = "age", -} - -interface Dependencies { - clusterRoleStore: ClusterRoleStore; - openAddClusterRoleDialog: OpenAddClusterRoleDialog; -} - -@observer -class NonInjectedClusterRoles extends React.Component { - render() { - const { - openAddClusterRoleDialog, - clusterRoleStore, - } = this.props; - - return ( - - clusterRole.getName(), - [columnId.age]: clusterRole => -clusterRole.getCreationTimestamp(), - }} - searchFilters={[ - clusterRole => clusterRole.getSearchFields(), - ]} - renderHeaderTitle="Cluster Roles" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={clusterRole => [ - clusterRole.getName(), - , - , - ]} - addRemoveButtons={{ - onAdd: () => openAddClusterRoleDialog(), - addTooltip: "Create new ClusterRole", - }} - /> - - - ); - } -} - -export const ClusterRoles = withInjectables(NonInjectedClusterRoles, { - getProps: (di, props) => ({ - ...props, - clusterRoleStore: di.inject(clusterRoleStoreInjectable), - openAddClusterRoleDialog: di.inject(openAddClusterRoleDialogInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx deleted file mode 100644 index 67d3b38fe1..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import userEvent from "@testing-library/user-event"; -import React from "react"; -import { ClusterRole } from "../../../../../common/k8s-api/endpoints"; -import { RoleBindingDialog } from "../dialog/view"; -import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; -import type { DiRender } from "../../../test-utils/renderFor"; -import { renderFor } from "../../../test-utils/renderFor"; -import directoryForUserDataInjectable from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be-created.injectable"; -import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../../../cluster/create-cluster.injectable"; -import type { OpenRoleBindingDialog } from "../dialog/open.injectable"; -import openRoleBindingDialogInjectable from "../dialog/open.injectable"; - -describe("RoleBindingDialog tests", () => { - let render: DiRender; - let openRoleBindingDialog: OpenRoleBindingDialog; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - openRoleBindingDialog = di.inject(openRoleBindingDialogInjectable); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - render = renderFor(di); - - const store = di.inject(clusterRoleStoreInjectable); - - store.items.replace([ - new ClusterRole({ - apiVersion: "rbac.authorization.k8s.io/v1", - kind: "ClusterRole", - metadata: { - name: "foobar", - resourceVersion: "1", - uid: "1", - selfLink: "/apis/rbac.authorization.k8s.io/v1/clusterroles/foobar", - }, - }), - ]); - }); - - it("should render without any errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("role select should be searchable", async () => { - openRoleBindingDialog(); - const res = render(); - - userEvent.click(await res.findByText("Select role", { exact: false })); - - await res.findAllByText("foobar", { - exact: false, - }); - }); -}); diff --git a/src/renderer/components/+user-management/+role-bindings/details.scss b/src/renderer/components/+user-management/+role-bindings/details.scss deleted file mode 100644 index 805e5e05fe..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/details.scss +++ /dev/null @@ -1,2 +0,0 @@ -.RoleBindingDetails { -} \ No newline at end of file diff --git a/src/renderer/components/+user-management/+role-bindings/details.tsx b/src/renderer/components/+user-management/+role-bindings/details.tsx deleted file mode 100644 index c83caddeec..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/details.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./details.scss"; - -import { reaction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import React from "react"; -import type { RoleBinding } from "../../../../common/k8s-api/endpoints"; -import { prevDefault } from "../../../utils"; -import { AddRemoveButtons } from "../../add-remove-buttons"; -import { DrawerTitle } from "../../drawer"; -import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { Table, TableCell, TableHead, TableRow } from "../../table"; -import { ObservableHashSet } from "../../../../common/utils/hash-set"; -import { hashSubject } from "../hashers"; -import type { OpenConfirmDialog } from "../../confirm-dialog/open.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import openConfirmDialogInjectable from "../../confirm-dialog/open.injectable"; -import type { OpenRoleBindingDialog } from "./dialog/open.injectable"; -import openRoleBindingDialogInjectable from "./dialog/open.injectable"; -import type { RoleBindingStore } from "./store"; -import roleBindingStoreInjectable from "./store.injectable"; - -export interface RoleBindingDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - openConfirmDialog: OpenConfirmDialog; - openRoleBindingDialog: OpenRoleBindingDialog; - roleBindingStore: RoleBindingStore; -} - -@observer -class NonInjectedRoleBindingDetails extends React.Component { - private readonly selectedSubjects = new ObservableHashSet([], hashSubject); - - async componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.object, () => { - this.selectedSubjects.clear(); - }), - ]); - } - - removeSelectedSubjects = () => { - const { object: roleBinding, openConfirmDialog, roleBindingStore } = this.props; - const { selectedSubjects } = this; - - openConfirmDialog({ - ok: () => roleBindingStore.removeSubjects(roleBinding, selectedSubjects.toJSON()), - labelOk: `Remove`, - message: ( -

- Remove selected bindings for - {roleBinding.getName()} - ? -

- ), - }); - }; - - render() { - const { selectedSubjects } = this; - const { object: roleBinding, openRoleBindingDialog } = this.props; - - if (!roleBinding) { - return null; - } - const { roleRef } = roleBinding; - const subjects = roleBinding.getSubjects(); - - return ( -
- Reference - - - Kind - Name - API Group - - - {roleRef.kind} - {roleRef.name} - {roleRef.apiGroup} - -
- - Bindings - {subjects.length > 0 && ( - - - - Type - Name - Namespace - - { - subjects.map((subject, i) => { - const { kind, name, namespace } = subject; - const isSelected = selectedSubjects.has(subject); - - return ( - this.selectedSubjects.toggle(subject))} - > - - {kind} - {name} - {namespace || "-"} - - ); - }) - } -
- )} - - openRoleBindingDialog(roleBinding)} - onRemove={selectedSubjects.size ? this.removeSelectedSubjects : undefined} - addTooltip={`Edit bindings of ${roleRef.name}`} - removeTooltip={`Remove selected bindings from ${roleRef.name}`} - /> -
- ); - } -} - -export const RoleBindingDetails = withInjectables(NonInjectedRoleBindingDetails, { - getProps: (di, props) => ({ - ...props, - openConfirmDialog: di.inject(openConfirmDialogInjectable), - openRoleBindingDialog: di.inject(openRoleBindingDialogInjectable), - roleBindingStore: di.inject(roleBindingStoreInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts deleted file mode 100644 index 1f42df1818..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import roleBindingDialogStateInjectable from "./state.injectable"; - -const closeRoleBindingDialogInjectable = getInjectable({ - id: "close-role-binding-dialog", - instantiate: (di) => { - const state = di.inject(roleBindingDialogStateInjectable); - - return action(() => state.set({ isOpen: false })); - }, -}); - -export default closeRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts deleted file mode 100644 index bda4f5363b..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { action } from "mobx"; -import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; -import roleBindingDialogStateInjectable from "./state.injectable"; - -export type OpenRoleBindingDialog = (roleBinding?: RoleBinding | undefined) => void; - -const openRoleBindingDialogInjectable = getInjectable({ - id: "open-role-binding-dialog", - instantiate: (di): OpenRoleBindingDialog => { - const state = di.inject(roleBindingDialogStateInjectable); - - return action((roleBinding) => state.set({ - isOpen: true, - roleBinding, - })); - }, -}); - -export default openRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts deleted file mode 100644 index 03b1093a3b..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; - -export type RoleBindingDialogState = { - isOpen: false; - roleBinding?: undefined; -} | { - isOpen: true; - roleBinding: RoleBinding | undefined; -}; - -const roleBindingDialogStateInjectable = getInjectable({ - id: "role-binding-dialog-state", - instantiate: () => observable.box({ isOpen: false }), -}); - -export default roleBindingDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/view.scss b/src/renderer/components/+user-management/+role-bindings/dialog/view.scss deleted file mode 100644 index 652d743f39..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/dialog/view.scss +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.AddRoleBindingDialog { - .Select + .Select { - margin-top: $margin *0.5; - } - .Checkbox { - margin-top: $margin; - } - .name { - color: #a0a0a0; - } -} diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx b/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx deleted file mode 100644 index 77f1724708..0000000000 --- a/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import type { IObservableValue } from "mobx"; -import { computed, observable, makeObservable, action } from "mobx"; -import { observer } from "mobx-react"; -import React from "react"; -import { NamespaceSelect } from "../../../+namespaces/namespace-select"; -import type { ClusterRole, Role, RoleApi, ServiceAccount } from "../../../../../common/k8s-api/endpoints"; -import type { DialogProps } from "../../../dialog"; -import { Dialog } from "../../../dialog"; -import { EditableList } from "../../../editable-list"; -import { Icon } from "../../../icon"; -import { SubTitle } from "../../../layout/sub-title"; -import type { SelectOption } from "../../../select"; -import { onMultiSelectFor, Select } from "../../../select"; -import { Wizard, WizardStep } from "../../../wizard"; -import { Input } from "../../../input"; -import { ObservableHashSet, nFircate } from "../../../../utils"; -import type { Subject } from "../../../../../common/k8s-api/endpoints/types/subject"; -import type { RoleBindingDialogState } from "./state.injectable"; -import type { RoleBindingStore } from "../store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import roleBindingStoreInjectable from "../store.injectable"; -import roleBindingDialogStateInjectable from "./state.injectable"; -import closeRoleBindingDialogInjectable from "./close.injectable"; -import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; -import type { RoleStore } from "../../+roles/store"; -import type { ClusterRoleStore } from "../../+cluster-roles/store"; -import type { ServiceAccountStore } from "../../+service-accounts/store"; -import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; -import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable"; -import roleStoreInjectable from "../../+roles/store.injectable"; -import serviceAccountStoreInjectable from "../../+service-accounts/store.injectable"; -import roleApiInjectable from "../../../../../common/k8s-api/endpoints/role.api.injectable"; -import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; - -export interface RoleBindingDialogProps extends Partial { -} - -interface Dependencies { - state: IObservableValue; - roleBindingStore: RoleBindingStore; - closeRoleBindingDialog: () => void; - showDetails: ShowDetails; - roleStore: RoleStore; - clusterRoleStore: ClusterRoleStore; - serviceAccountStore: ServiceAccountStore; - roleApi: RoleApi; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedRoleBindingDialog extends React.Component { - constructor(props: RoleBindingDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - @computed get roleBinding() { - return this.props.state.get().roleBinding; - } - - @computed get isEditing() { - return !!this.roleBinding; - } - - @observable.ref selectedRoleRef: Role | ClusterRole | null | undefined = null; - @observable bindingName = ""; - @observable bindingNamespace: string | null = null; - selectedAccounts = new ObservableHashSet([], sa => sa.metadata.uid); - selectedUsers = observable.set([]); - selectedGroups = observable.set([]); - - @computed get selectedBindings(): Subject[] { - const serviceAccounts: Subject[] = Array.from(this.selectedAccounts, sa => ({ - name: sa.getName(), - kind: "ServiceAccount", - namespace: sa.getNs(), - })); - const users: Subject[] = Array.from(this.selectedUsers, user => ({ - name: user, - kind: "User", - })); - const groups: Subject[] = Array.from(this.selectedGroups, group => ({ - name: group, - kind: "Group", - })); - - return [ - ...serviceAccounts, - ...users, - ...groups, - ]; - } - - @computed get roleRefOptions(): SelectOption[] { - const { - roleStore, - clusterRoleStore, - } = this.props; - const roles = roleStore.items - .filter(role => role.getNs() === this.bindingNamespace); - const clusterRoles = clusterRoleStore.items; - - return [ - ...roles, - ...clusterRoles, - ].map(r => ({ - value: r, - label: r.getName(), - })); - } - - @computed get serviceAccountOptions(): SelectOption[] { - return this.props.serviceAccountStore.items.map(serviceAccount => ({ - value: serviceAccount, - label: `${serviceAccount.getName()} (${serviceAccount.getNs()})`, - isSelected: this.selectedAccounts.has(serviceAccount), - })); - } - - onOpen = action(() => { - const { - roleStore, - clusterRoleStore, - serviceAccountStore, - roleApi, - } = this.props; - const binding = this.roleBinding; - - if (!binding) { - return this.reset(); - } - - this.selectedRoleRef = ( - binding.roleRef.kind === roleApi.kind - ? roleStore.items.find(item => item.getName() === binding.roleRef.name) - : clusterRoleStore.items.find(item => item.getName() === binding.roleRef.name) - ); - - this.bindingName = binding.getName(); - this.bindingNamespace = binding.getNs(); - - const [saSubjects, uSubjects, gSubjects] = nFircate(binding.getSubjects(), "kind", ["ServiceAccount", "User", "Group"]); - const accountNames = new Set(saSubjects.map(acc => acc.name)); - - this.selectedAccounts.replace( - serviceAccountStore.items - .filter(sa => accountNames.has(sa.getName())), - ); - this.selectedUsers.replace(uSubjects.map(user => user.name)); - this.selectedGroups.replace(gSubjects.map(group => group.name)); - }); - - reset = action(() => { - this.selectedRoleRef = null; - this.bindingName = ""; - this.bindingNamespace = ""; - this.selectedAccounts.clear(); - this.selectedUsers.clear(); - this.selectedGroups.clear(); - }); - - createBindings = async () => { - const { - roleBindingStore, - showDetails, - showCheckedErrorNotification, - } = this.props; - const { selectedRoleRef, bindingNamespace, selectedBindings, roleBinding, bindingName } = this; - - if (!selectedRoleRef || !roleBinding || !bindingNamespace || !bindingName) { - return; - } - - try { - const newRoleBinding = this.isEditing - ? await roleBindingStore.updateSubjects(roleBinding, selectedBindings) - : await roleBindingStore.create({ - name: bindingName, - namespace: bindingNamespace, - }, { - subjects: selectedBindings, - roleRef: { - name: selectedRoleRef.getName(), - kind: selectedRoleRef.kind, - }, - }); - - showDetails(newRoleBinding.selfLink); - this.props.closeRoleBindingDialog(); - } catch (err) { - showCheckedErrorNotification(err, `Unknown error occured while ${this.isEditing ? "editing" : "creating"} role bindings.`); - } - }; - - renderContents() { - return ( - <> - - this.bindingNamespace = opt?.value ?? null} - /> - - - this.bindingName = value} - /> - - - - Users - this.selectedUsers.add(newUser)} - items={Array.from(this.selectedUsers)} - remove={({ oldItem }) => this.selectedUsers.delete(oldItem)} - /> - - Groups - this.selectedGroups.add(newGroup)} - items={Array.from(this.selectedGroups)} - remove={({ oldItem }) => this.selectedGroups.delete(oldItem)} - /> - - Service Accounts - state.roleName.set(v)} - /> - - state.namespace.set(option?.value ?? "default")} - /> - - - - ); - } -} - -export const AddRoleDialog = withInjectables(NonInjectedAddRoleDialog, { - getProps: (di, props) => ({ - ...props, - closeAddRoleDialog: di.inject(closeAddRoleDialogInjectable), - roleStore: di.inject(roleStoreInjectable), - showDetails: di.inject(showDetailsInjectable), - state: di.inject(addRoleDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+roles/details.scss b/src/renderer/components/+user-management/+roles/details.scss deleted file mode 100644 index c93ac17687..0000000000 --- a/src/renderer/components/+user-management/+roles/details.scss +++ /dev/null @@ -1,21 +0,0 @@ -.RoleDetails { - .rule { - display: grid; - grid-template-columns: min-content auto; - gap: $margin; - - border: 1px solid var(--borderColor); - border-radius: $radius; - padding: $padding * 1.5; - - > .name { - color: var(--textColorSecondary); - text-align: right; - white-space: nowrap; - } - - &:not(:last-child) { - margin-bottom: $margin * 2; - } - } -} diff --git a/src/renderer/components/+user-management/+roles/details.tsx b/src/renderer/components/+user-management/+roles/details.tsx deleted file mode 100644 index 9257d7095e..0000000000 --- a/src/renderer/components/+user-management/+roles/details.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./details.scss"; - -import { observer } from "mobx-react"; -import React from "react"; - -import type { Role } from "../../../../common/k8s-api/endpoints"; -import { DrawerTitle } from "../../drawer"; -import type { KubeObjectDetailsProps } from "../../kube-object-details"; - -export interface RoleDetailsProps extends KubeObjectDetailsProps { -} - -@observer -export class RoleDetails extends React.Component { - render() { - const { object: role } = this.props; - - if (!role) return null; - const rules = role.getRules(); - - return ( -
- Rules - {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { - return ( -
- {resources && ( - <> -
Resources
-
{resources.join(", ")}
- - )} - {verbs && ( - <> -
Verbs
-
{verbs.join(", ")}
- - )} - {apiGroups && ( - <> -
Api Groups
-
- {apiGroups - .map(apiGroup => apiGroup === "" ? `'${apiGroup}'` : apiGroup) - .join(", ") - } -
- - )} - {resourceNames && ( - <> -
Resource Names
-
{resourceNames.join(", ")}
- - )} -
- ); - })} -
- ); - } -} diff --git a/src/renderer/components/+user-management/+roles/index.ts b/src/renderer/components/+user-management/+roles/index.ts deleted file mode 100644 index f55fa5914b..0000000000 --- a/src/renderer/components/+user-management/+roles/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export * from "./view"; -export * from "./details"; -export * from "./add-dialog/view"; diff --git a/src/renderer/components/+user-management/+roles/roles-route-component.injectable.ts b/src/renderer/components/+user-management/+roles/roles-route-component.injectable.ts deleted file mode 100644 index 980b2499de..0000000000 --- a/src/renderer/components/+user-management/+roles/roles-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Roles } from "./view"; -import rolesRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token"; - -const rolesRouteComponentInjectable = getInjectable({ - id: "roles-route-component", - - instantiate: (di) => ({ - route: di.inject(rolesRouteInjectable), - Component: Roles, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default rolesRouteComponentInjectable; diff --git a/src/renderer/components/+user-management/+roles/roles-sidebar-items.injectable.tsx b/src/renderer/components/+user-management/+roles/roles-sidebar-items.injectable.tsx deleted file mode 100644 index 97ee9715a2..0000000000 --- a/src/renderer/components/+user-management/+roles/roles-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 rolesRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable"; -import { userManagementSidebarItemId } from "../user-management-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; -import navigateToRolesInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/roles/navigate-to-roles.injectable"; - -const rolesSidebarItemsInjectable = getInjectable({ - id: "roles-sidebar-items", - - instantiate: (di) => { - const route = di.inject(rolesRouteInjectable); - const navigateToRoles = di.inject(navigateToRolesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "roles", - parentId: userManagementSidebarItemId, - title: "Roles", - onClick: navigateToRoles, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 30, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default rolesSidebarItemsInjectable; diff --git a/src/renderer/components/+user-management/+roles/store.injectable.ts b/src/renderer/components/+user-management/+roles/store.injectable.ts deleted file mode 100644 index 99eddcbfa0..0000000000 --- a/src/renderer/components/+user-management/+roles/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import roleApiInjectable from "../../../../common/k8s-api/endpoints/role.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token"; -import { RoleStore } from "./store"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; - -const roleStoreInjectable = getInjectable({ - id: "role-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "roleStore is only available in certain environments"); - - const api = di.inject(roleApiInjectable); - - return new RoleStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default roleStoreInjectable; diff --git a/src/renderer/components/+user-management/+roles/store.ts b/src/renderer/components/+user-management/+roles/store.ts deleted file mode 100644 index 5292d9b699..0000000000 --- a/src/renderer/components/+user-management/+roles/store.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Role, RoleApi, RoleData } from "../../../../common/k8s-api/endpoints"; -import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; - -export class RoleStore extends KubeObjectStore { - protected sortItems(items: Role[]) { - return super.sortItems(items, [ - role => role.kind, - role => role.getName(), - ]); - } -} diff --git a/src/renderer/components/+user-management/+roles/view.scss b/src/renderer/components/+user-management/+roles/view.scss deleted file mode 100644 index 6fae603fd3..0000000000 --- a/src/renderer/components/+user-management/+roles/view.scss +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.Roles { - .help-icon { - margin-left: $margin * 0.5; - } - - .TableCell { - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+user-management/+roles/view.tsx b/src/renderer/components/+user-management/+roles/view.tsx deleted file mode 100644 index a299ac1085..0000000000 --- a/src/renderer/components/+user-management/+roles/view.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import { observer } from "mobx-react"; -import React from "react"; -import { KubeObjectListLayout } from "../../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { AddRoleDialog } from "./add-dialog/view"; -import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../../kube-object/age"; -import type { RoleStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import roleStoreInjectable from "./store.injectable"; -import openAddRoleDialogInjectable from "./add-dialog/open.injectable"; -import { NamespaceSelectBadge } from "../../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - age = "age", -} - -interface Dependencies { - roleStore: RoleStore; - openAddRoleDialog: () => void; -} - -@observer -class NonInjectedRoles extends React.Component { - render() { - const { - roleStore, - openAddRoleDialog, - } = this.props; - - return ( - - role.getName(), - [columnId.namespace]: role => role.getNs(), - [columnId.age]: role => -role.getCreationTimestamp(), - }} - searchFilters={[ - role => role.getSearchFields(), - ]} - renderHeaderTitle="Roles" - 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: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={role => [ - role.getName(), - , - , - , - ]} - addRemoveButtons={{ - onAdd: () => openAddRoleDialog(), - addTooltip: "Create new Role", - }} - /> - - - ); - } -} - -export const Roles = withInjectables(NonInjectedRoles, { - getProps: (di, props) => ({ - ...props, - roleStore: di.inject(roleStoreInjectable), - openAddRoleDialog: di.inject(openAddRoleDialogInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts deleted file mode 100644 index 7c722c1264..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/close.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { action } from "mobx"; -import createServiceAccountDialogStateInjectable from "./state.injectable"; - -const closeCreateServiceAccountDialogInjectable = getInjectable({ - id: "close-create-service-account-dialog", - instantiate: (di) => { - const state = di.inject(createServiceAccountDialogStateInjectable); - - return action(() => { - state.isOpen.set(false); - state.name.set(""); - state.namespace.set("default"); - }); - }, -}); - -export default closeCreateServiceAccountDialogInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts deleted file mode 100644 index d1410817c9..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/open.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { action } from "mobx"; -import createServiceAccountDialogStateInjectable from "./state.injectable"; - -export type OpenCreateServiceAccountDialog = () => void; - -const openCreateServiceAccountDialogInjectable = getInjectable({ - id: "open-create-service-account-dialog", - instantiate: (di) => { - const state = di.inject(createServiceAccountDialogStateInjectable); - - return action(() => { - state.isOpen.set(true); - state.name.set(""); - state.namespace.set("default"); - }); - }, -}); - -export default openCreateServiceAccountDialogInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts b/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts deleted file mode 100644 index 8ca5455fbd..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/state.injectable.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 { IObservableValue } from "mobx"; -import { observable } from "mobx"; - -export interface CreateServiceAccountDialogState { - readonly isOpen: IObservableValue; - readonly name: IObservableValue; - readonly namespace: IObservableValue; -} - -const createServiceAccountDialogStateInjectable = getInjectable({ - id: "create-service-account-dialog", - instantiate: (): CreateServiceAccountDialogState => ({ - isOpen: observable.box(false), - name: observable.box(""), - namespace: observable.box("default"), - }), -}); - -export default createServiceAccountDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.scss b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.scss deleted file mode 100644 index 8f56fbe7b3..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.CreateServiceAccountDialog { -} diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx deleted file mode 100644 index 6b112b6ea9..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import React from "react"; -import { observer } from "mobx-react"; - -import { NamespaceSelect } from "../../../+namespaces/namespace-select"; -import type { DialogProps } from "../../../dialog"; -import { Dialog } from "../../../dialog"; -import { Input } from "../../../input"; -import { systemName } from "../../../input/input_validators"; -import { SubTitle } from "../../../layout/sub-title"; -import { Wizard, WizardStep } from "../../../wizard"; -import type { CreateServiceAccountDialogState } from "./state.injectable"; -import type { ServiceAccountStore } from "../store"; -import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import closeCreateServiceAccountDialogInjectable from "./close.injectable"; -import serviceAccountStoreInjectable from "../store.injectable"; -import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; -import createServiceAccountDialogStateInjectable from "./state.injectable"; -import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; - -export interface CreateServiceAccountDialogProps extends Partial { -} - -interface Dependencies { - state: CreateServiceAccountDialogState; - serviceAccountStore: ServiceAccountStore; - closeCreateServiceAccountDialog: () => void; - showDetails: ShowDetails; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedCreateServiceAccountDialog extends React.Component { - createAccount = async () => { - const { - closeCreateServiceAccountDialog, - serviceAccountStore, - state, - showDetails, - showCheckedErrorNotification, - } = this.props; - - try { - const serviceAccount = await serviceAccountStore.create({ - namespace: state.namespace.get(), - name: state.name.get(), - }); - - showDetails(serviceAccount.selfLink); - closeCreateServiceAccountDialog(); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while creating service account"); - } - }; - - render() { - const { closeCreateServiceAccountDialog, serviceAccountStore, state, ...dialogProps } = this.props; - - return ( - - Create Service Account} - done={closeCreateServiceAccountDialog} - > - - - state.name.set(v.toLowerCase())} - /> - - state.namespace.set(option?.value ?? "default")} - /> - - - - ); - } -} - -export const CreateServiceAccountDialog = withInjectables(NonInjectedCreateServiceAccountDialog, { - getProps: (di, props) => ({ - ...props, - closeCreateServiceAccountDialog: di.inject(closeCreateServiceAccountDialogInjectable), - serviceAccountStore: di.inject(serviceAccountStoreInjectable), - showDetails: di.inject(showDetailsInjectable), - state: di.inject(createServiceAccountDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+service-accounts/details.scss b/src/renderer/components/+user-management/+service-accounts/details.scss deleted file mode 100644 index 82e4f5edf8..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/details.scss +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ServiceAccountsDetails { - .links { - a { - margin-right: $margin; - } - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/details.tsx b/src/renderer/components/+user-management/+service-accounts/details.tsx deleted file mode 100644 index 2c0bec0f0a..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/details.tsx +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./details.scss"; - -import { autorun, observable, runInAction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import React from "react"; -import { Link } from "react-router-dom"; - -import type { Secret, ServiceAccount } from "../../../../common/k8s-api/endpoints"; -import { DrawerItem, DrawerTitle } from "../../drawer"; -import { Icon } from "../../icon"; -import type { KubeObjectDetailsProps } from "../../kube-object-details"; -import { Spinner } from "../../spinner"; -import { ServiceAccountsSecret } from "./secret"; -import type { SecretStore } from "../../+config-secrets/store"; -import type { GetDetailsUrl } from "../../kube-detail-params/get-details-url.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable"; -import secretStoreInjectable from "../../+config-secrets/store.injectable"; - -export interface ServiceAccountsDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - secretStore: SecretStore; - getDetailsUrl: GetDetailsUrl; -} - -@observer -class NonInjectedServiceAccountsDetails extends React.Component { - readonly secrets = observable.array(); - readonly imagePullSecrets = observable.array(); - - private defensiveLoadSecretIn = (namespace: string) => ( - ({ name }: { name: string }) => ( - this.props.secretStore.load({ name, namespace }) - .catch(() => name) - ) - ); - - componentDidMount(): void { - disposeOnUnmount(this, [ - autorun(async () => { - runInAction(() => { - this.secrets.clear(); - this.imagePullSecrets.clear(); - }); - - const { object: serviceAccount } = this.props; - const namespace = serviceAccount?.getNs(); - - if (!namespace) { - return; - } - - const defensiveLoadSecret = this.defensiveLoadSecretIn(namespace); - - const secretLoaders = Promise.all(serviceAccount.getSecrets().map(defensiveLoadSecret)); - const imagePullSecretLoaders = Promise.all(serviceAccount.getImagePullSecrets().map(defensiveLoadSecret)); - const [secrets, imagePullSecrets] = await Promise.all([ - secretLoaders, - imagePullSecretLoaders, - ]); - - runInAction(() => { - this.secrets.replace(secrets); - this.imagePullSecrets.replace(imagePullSecrets); - }); - }), - ]); - } - - renderSecrets() { - const { secrets } = this; - - if (!secrets) { - return ; - } - - return secrets.map(secret => ( - - )); - } - - renderImagePullSecrets() { - const { imagePullSecrets } = this; - - if (!imagePullSecrets) { - return ; - } - - return this.renderSecretLinks(imagePullSecrets); - } - - renderSecretLinks(secrets: (Secret | string)[]) { - return secrets.map((secret) => { - if (typeof secret === "string") { - return ( -
- {secret} - -
- ); - } - - return ( - - {secret.getName()} - - ); - }); - } - - render() { - const { object: serviceAccount, secretStore } = this.props; - - if (!serviceAccount) { - return null; - } - const tokens = secretStore.items.filter(secret => - secret.getNs() == serviceAccount.getNs() && - secret.getAnnotations().some(annot => annot == `kubernetes.io/service-account.name: ${serviceAccount.getName()}`), - ); - const imagePullSecrets = serviceAccount.getImagePullSecrets(); - - return ( -
- {tokens.length > 0 && ( - - {this.renderSecretLinks(tokens)} - - )} - {imagePullSecrets.length > 0 && ( - - {this.renderImagePullSecrets()} - - )} - - Mountable secrets -
- {this.renderSecrets()} -
-
- ); - } -} - -export const ServiceAccountsDetails = withInjectables(NonInjectedServiceAccountsDetails, { - getProps: (di, props) => ({ - ...props, - getDetailsUrl: di.inject(getDetailsUrlInjectable), - secretStore: di.inject(secretStoreInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+service-accounts/index.ts b/src/renderer/components/+user-management/+service-accounts/index.ts deleted file mode 100644 index e011cad5cd..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export * from "./view"; -export * from "./details"; -export * from "./create-dialog/view"; diff --git a/src/renderer/components/+user-management/+service-accounts/secret.scss b/src/renderer/components/+user-management/+service-accounts/secret.scss deleted file mode 100644 index aae1be272e..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/secret.scss +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ServiceAccountsSecret { - margin-bottom: $margin * 3; - - &:nth-child(even) { - margin-left: -$margin * 3; - margin-right: -$margin * 3; - padding: $padding $padding * 3; - } - - .secret-row { - display: flex; - border-bottom: 1px solid var(--borderFaintColor); - padding: $padding 0; - - &:first-child { - padding-top: 0 - } - } - - .name { - flex-basis: 23%; - color: var(--drawerItemNameColor); - } - - .value { - flex-basis: 76%; - color: var(--drawerItemValueColor); - word-break: break-all; - - &:empty:after { - content: '—' - } - } - - .asterisks { - font-size: large; - margin-right: $margin * 0.5; - line-height: 90%; - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/secret.tsx b/src/renderer/components/+user-management/+service-accounts/secret.tsx deleted file mode 100644 index 49acaa9ca8..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/secret.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./secret.scss"; - -import moment from "moment"; -import React from "react"; - -import type { Secret } from "../../../../common/k8s-api/endpoints/secret.api"; -import { prevDefault } from "../../../utils"; -import { Icon } from "../../icon"; - -export interface ServiceAccountsSecretProps { - secret: Secret | string; -} - -interface State { - showToken: boolean; -} - -interface RenderRowArgs { - name: string; - value: React.ReactNode; -} - -export class ServiceAccountsSecret extends React.Component { - public state: State = { - showToken: false, - }; - - renderSecretValue(secret: Secret) { - const { showToken } = this.state; - - return ( - <> - {!showToken && ( - <> - {"•".repeat(16)} - this.setState({ showToken: true }))} - /> - - )} - {showToken && ( - {secret.getToken()} - )} - - ); - } - - renderRow({ name, value }: RenderRowArgs) { - return ( -
- {name} - {value} -
- ); - } - - render() { - const { secret } = this.props; - - return ( -
- {this.renderRow({ - name: "Name: ", - value: ( - typeof secret === "string" - ? secret - : secret.getName() - ), - })} - {this.renderRow({ - name: "Value: ", - value: ( - typeof secret === "string" - ? "" - : this.renderSecretValue(secret) - ), - })} - {this.renderRow({ - name: "Created at: ", - value: ( - typeof secret === "string" || !secret.metadata.creationTimestamp - ? "" - : moment(secret.metadata.creationTimestamp).format("LLL") - ), - })} - {this.renderRow({ - name: "Type: ", - value: ( - typeof secret === "string" - ? "" - : secret.type - ), - })} -
- ); - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx b/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx deleted file mode 100644 index 63b1d89a20..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; - -import type { KubeObjectMenuProps } from "../../kube-object-menu"; -import type { ServiceAccount } from "../../../../common/k8s-api/endpoints"; -import { MenuItem } from "../../menu"; -import { Icon } from "../../icon"; -import type { OpenServiceAccountKubeConfigDialog } from "../../kubeconfig-dialog/open-service-account-kube-config-dialog.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import openServiceAccountKubeConfigDialogInjectable from "../../kubeconfig-dialog/open-service-account-kube-config-dialog.injectable"; - -interface Dependencies { - openServiceAccountKubeConfigDialog: OpenServiceAccountKubeConfigDialog; -} - -function NonInjectedServiceAccountMenu(props: KubeObjectMenuProps & Dependencies) { - const { object, toolbar, openServiceAccountKubeConfigDialog } = props; - - return ( - openServiceAccountKubeConfigDialog(object)}> - - Kubeconfig - - ); -} - -export const ServiceAccountMenu = withInjectables>(NonInjectedServiceAccountMenu, { - getProps: (di, props) => ({ - ...props, - openServiceAccountKubeConfigDialog: di.inject(openServiceAccountKubeConfigDialogInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/+service-accounts/service-accounts-route-component.injectable.ts b/src/renderer/components/+user-management/+service-accounts/service-accounts-route-component.injectable.ts deleted file mode 100644 index 5903af5194..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/service-accounts-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { ServiceAccounts } from "./view"; -import serviceAccountsRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token"; - -const serviceAccountsRouteComponentInjectable = getInjectable({ - id: "service-accounts-route-component", - - instantiate: (di) => ({ - route: di.inject(serviceAccountsRouteInjectable), - Component: ServiceAccounts, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default serviceAccountsRouteComponentInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/service-accounts-sidebar-items.injectable.tsx b/src/renderer/components/+user-management/+service-accounts/service-accounts-sidebar-items.injectable.tsx deleted file mode 100644 index d01b8d6480..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/service-accounts-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 serviceAccountsRouteInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable"; -import { userManagementSidebarItemId } from "../user-management-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; -import navigateToServiceAccountsInjectable from "../../../../common/front-end-routing/routes/cluster/user-management/service-accounts/navigate-to-service-accounts.injectable"; - -const serviceAccountsSidebarItemsInjectable = getInjectable({ - id: "service-accounts-sidebar-items", - - instantiate: (di) => { - const route = di.inject(serviceAccountsRouteInjectable); - const navigateToServiceAccounts = di.inject(navigateToServiceAccountsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "service-accounts", - parentId: userManagementSidebarItemId, - title: "Service Accounts", - onClick: navigateToServiceAccounts, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 10, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default serviceAccountsSidebarItemsInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts deleted file mode 100644 index a4749f7ecb..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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 assert from "assert"; -import serviceAccountApiInjectable from "../../../../common/k8s-api/endpoints/service-account.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token"; -import { ServiceAccountStore } from "./store"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; - -const serviceAccountStoreInjectable = getInjectable({ - id: "service-account-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "serviceAccountStore is only available in certain environments"); - - const api = di.inject(serviceAccountApiInjectable); - - return new ServiceAccountStore({ - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default serviceAccountStoreInjectable; diff --git a/src/renderer/components/+user-management/+service-accounts/store.ts b/src/renderer/components/+user-management/+service-accounts/store.ts deleted file mode 100644 index 27e5bbc401..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/store.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ServiceAccount, ServiceAccountApi, ServiceAccountData } from "../../../../common/k8s-api/endpoints"; -import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; - -export class ServiceAccountStore extends KubeObjectStore { - protected async createItem(params: { name: string; namespace?: string }) { - await super.createItem(params); - - return this.api.get(params); // hackfix: load freshly created account, cause it doesn't have "secrets" field yet - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/view.scss b/src/renderer/components/+user-management/+service-accounts/view.scss deleted file mode 100644 index 4cc730d949..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/view.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ServiceAccounts { - .TableCell { - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+user-management/+service-accounts/view.tsx b/src/renderer/components/+user-management/+service-accounts/view.tsx deleted file mode 100644 index ce7eb4988e..0000000000 --- a/src/renderer/components/+user-management/+service-accounts/view.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import { observer } from "mobx-react"; -import React from "react"; -import { KubeObjectListLayout } from "../../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { CreateServiceAccountDialog } from "./create-dialog/view"; -import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../../kube-object/age"; -import type { ServiceAccountStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import serviceAccountStoreInjectable from "./store.injectable"; -import type { OpenCreateServiceAccountDialog } from "./create-dialog/open.injectable"; -import openCreateServiceAccountDialogInjectable from "./create-dialog/open.injectable"; -import { NamespaceSelectBadge } from "../../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - age = "age", -} - -interface Dependencies { - serviceAccountStore: ServiceAccountStore; - openCreateServiceAccountDialog: OpenCreateServiceAccountDialog; -} - -@observer -class NonInjectedServiceAccounts extends React.Component { - render() { - const { - serviceAccountStore, - openCreateServiceAccountDialog, - } = this.props; - - return ( - - account.getName(), - [columnId.namespace]: account => account.getNs(), - [columnId.age]: account => -account.getCreationTimestamp(), - }} - searchFilters={[ - account => account.getSearchFields(), - ]} - renderHeaderTitle="Service Accounts" - 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: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={account => [ - account.getName(), - , - , - , - ]} - addRemoveButtons={{ - onAdd: () => openCreateServiceAccountDialog(), - addTooltip: "Create new Service Account", - }} - /> - - - ); - } -} - -export const ServiceAccounts = withInjectables(NonInjectedServiceAccounts, { - getProps: (di, props) => ({ - ...props, - serviceAccountStore: di.inject(serviceAccountStoreInjectable), - openCreateServiceAccountDialog: di.inject(openCreateServiceAccountDialogInjectable), - }), -}); diff --git a/src/renderer/components/+user-management/hashers.ts b/src/renderer/components/+user-management/hashers.ts deleted file mode 100644 index 895b152755..0000000000 --- a/src/renderer/components/+user-management/hashers.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { MD5 } from "crypto-js"; -import type { Subject } from "../../../common/k8s-api/endpoints/types/subject"; - -export function hashSubject(subject: Subject): string { - return MD5(JSON.stringify([ - ["kind", subject.kind], - ["name", subject.name], - ["namespace", subject.namespace], - ["apiGroup", subject.apiGroup], - ])).toString(); -} diff --git a/src/renderer/components/+user-management/user-management-sidebar-items.injectable.tsx b/src/renderer/components/+user-management/user-management-sidebar-items.injectable.tsx deleted file mode 100644 index 5a14bd083b..0000000000 --- a/src/renderer/components/+user-management/user-management-sidebar-items.injectable.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { - SidebarItemRegistration } from "../layout/sidebar-items.injectable"; -import { - sidebarItemsInjectionToken, -} from "../layout/sidebar-items.injectable"; -import { Icon } from "../icon"; -import React from "react"; -import { noop } from "lodash/fp"; - -export const userManagementSidebarItemId = "user-management"; - -const userManagementSidebarItemsInjectable = getInjectable({ - id: "user-management-sidebar-items", - - instantiate: () => - computed((): SidebarItemRegistration[] => [ - { - id: userManagementSidebarItemId, - parentId: null, - getIcon: () => , - title: "Access Control", - onClick: noop, - orderNumber: 100, - }, - ]), - - injectionToken: sidebarItemsInjectionToken, -}); - -export default userManagementSidebarItemsInjectable; diff --git a/src/renderer/components/+welcome/__test__/welcome.test.tsx b/src/renderer/components/+welcome/__test__/welcome.test.tsx deleted file mode 100644 index 2b12f5658f..0000000000 --- a/src/renderer/components/+welcome/__test__/welcome.test.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { screen } from "@testing-library/react"; -import "@testing-library/jest-dom/extend-expect"; -import { defaultWidth, Welcome } from "../welcome"; -import { computed } from "mobx"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DiRender } from "../../test-utils/renderFor"; -import { renderFor } from "../../test-utils/renderFor"; -import type { DiContainer } from "@ogre-tools/injectable"; -import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; -import { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; -import type { WelcomeBannerRegistration } from "../welcome-banner-items/welcome-banner-registration"; -import currentlyInClusterFrameInjectable from "../../../routes/currently-in-cluster-frame.injectable"; - -describe("", () => { - let render: DiRender; - let di: DiContainer; - let welcomeBannersStub: WelcomeBannerRegistration[]; - - beforeEach(() => { - di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(currentlyInClusterFrameInjectable, () => false); - - render = renderFor(di); - welcomeBannersStub = []; - - di.override(rendererExtensionsInjectable, () => - computed(() => [ - new TestExtension({ - id: "some-id", - welcomeBanners: welcomeBannersStub, - }), - ]), - ); - }); - - it("renders registered in WelcomeBannerRegistry and hide logo", async () => { - const testId = "testId"; - - welcomeBannersStub.push({ - Banner: () =>
, - }); - - const { container } = render(); - - expect(screen.queryByTestId(testId)).toBeInTheDocument(); - expect(container.getElementsByClassName("logo").length).toBe(0); - }); - - it("calculates max width from WelcomeBanner.width registered in WelcomeBannerRegistry", async () => { - welcomeBannersStub.push({ - width: 100, - Banner: () =>
, - }); - - welcomeBannersStub.push({ - width: 800, - Banner: () =>
, - }); - - render(); - - expect(screen.queryByTestId("welcome-banner-container")).toHaveStyle({ - // should take the max width of the banners (if > defaultWidth) - width: `800px`, - }); - expect(screen.queryByTestId("welcome-text-container")).toHaveStyle({ - width: `${defaultWidth}px`, - }); - expect(screen.queryByTestId("welcome-menu-container")).toHaveStyle({ - width: `${defaultWidth}px`, - }); - }); -}); - -class TestExtension extends LensRendererExtension { - constructor({ - id, - welcomeBanners, - }: { - id: string; - welcomeBanners: WelcomeBannerRegistration[]; - }) { - super({ - id, - absolutePath: "irrelevant", - isBundled: false, - isCompatible: false, - isEnabled: false, - manifest: { name: id, version: "some-version", engines: { lens: "^5.5.0" }}, - manifestPath: "irrelevant", - }); - - this.welcomeBanners = welcomeBanners; - } -} diff --git a/src/renderer/components/+welcome/default-welcome-route-component.injectable.ts b/src/renderer/components/+welcome/default-welcome-route-component.injectable.ts deleted file mode 100644 index 5472c49787..0000000000 --- a/src/renderer/components/+welcome/default-welcome-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Welcome } from "./welcome"; -import defaultWelcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/default-welcome-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const defaultWelcomeRouteComponentInjectable = getInjectable({ - id: "default-welcome-route-component", - - instantiate: (di) => ({ - route: di.inject(defaultWelcomeRouteInjectable), - Component: Welcome, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default defaultWelcomeRouteComponentInjectable; diff --git a/src/renderer/components/+welcome/index.ts b/src/renderer/components/+welcome/index.ts deleted file mode 100644 index af3ab83c8d..0000000000 --- a/src/renderer/components/+welcome/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./welcome"; diff --git a/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts deleted file mode 100644 index a74eabda23..0000000000 --- a/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; -import { computed } from "mobx"; - -const welcomeBannerItemsInjectable = getInjectable({ - id: "welcome-banner-items", - - instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); - - return computed(() => ( - extensions - .get() - .flatMap((extension) => extension.welcomeBanners) - )); - }, -}); - -export default welcomeBannerItemsInjectable; diff --git a/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.ts b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.ts deleted file mode 100644 index ba3b5e6f3a..0000000000 --- a/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * WelcomeBannerRegistration is for an extension to register - * Provide a Banner component to be renderered in the welcome screen. - */ -export interface WelcomeBannerRegistration { - /** - * The banner component to be shown on the welcome screen. - */ - Banner: React.ComponentType; - /** - * The banner width in px. - */ - width?: number; -} diff --git a/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts b/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts deleted file mode 100644 index 696d76777d..0000000000 --- a/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { IComputedValue } from "mobx"; -import { computed } from "mobx"; -import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; -import type { NavigateToCatalog } from "../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; - -interface Dependencies { - extensions: IComputedValue; - navigateToCatalog: NavigateToCatalog; -} - -export const getWelcomeMenuItems = ({ - extensions, - navigateToCatalog, -}: Dependencies) => { - const browseClusters = { - title: "Browse Clusters in Catalog", - icon: "view_list", - - click: () => - navigateToCatalog({ - group: "entity.k8slens.dev", - kind: "KubernetesCluster", - }), - }; - - return computed(() => [ - browseClusters, - ...extensions.get().flatMap((extension) => extension.welcomeMenus), - ]); -}; diff --git a/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts deleted file mode 100644 index c5234ac6f8..0000000000 --- a/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; -import { getWelcomeMenuItems } from "./get-welcome-menu-items"; -import navigateToCatalogInjectable from "../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; - -const welcomeMenuItemsInjectable = getInjectable({ - id: "welcome-menu-items", - - instantiate: (di) => - getWelcomeMenuItems({ - extensions: di.inject(rendererExtensionsInjectable), - navigateToCatalog: di.inject(navigateToCatalogInjectable), - }), -}); - -export default welcomeMenuItemsInjectable; diff --git a/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.ts b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.ts deleted file mode 100644 index 1dd79bc705..0000000000 --- a/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export interface WelcomeMenuRegistration { - title: string | (() => string); - icon: string; - click: () => void | Promise; -} diff --git a/src/renderer/components/+welcome/welcome.scss b/src/renderer/components/+welcome/welcome.scss deleted file mode 100644 index 0c87c062df..0000000000 --- a/src/renderer/components/+welcome/welcome.scss +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.Welcome { - text-align: center; - width: 100%; - height: 100%; - z-index: 1; - - h2 { - color: var(--textColorAccent); - font-weight: 600; - margin-top: 15px; - margin-bottom: 20px; - } - - p { - line-height: 1.5; - } - - ul { - margin-top: 20px; - - li { - text-align: left; - line-height: 1.5; - background-color: var(--layoutBackground); - padding: 7px; - border-radius: 4px; - margin-bottom: 7px; - cursor: pointer; - - a { - margin-left: 10px; - border-bottom: none; - } - } - li:hover { - color: var(--textColorAccent); - } - } - - .Icon.logo { - width: 200px; - height: 200px; - color: var(--primary); - } - - a.link { - color: var(--colorInfo); - - &:hover { - text-decoration: underline; - } - } -} diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx deleted file mode 100644 index cf413748bc..0000000000 --- a/src/renderer/components/+welcome/welcome.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./welcome.scss"; -import React from "react"; -import { observer } from "mobx-react"; -import type { IComputedValue } from "mobx"; -import type { CarouselProps } from "react-material-ui-carousel"; -import LegacyCarousel from "react-material-ui-carousel"; -import { Icon } from "../icon"; -import { slackUrl } from "../../../common/vars"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import welcomeMenuItemsInjectable from "./welcome-menu-items/welcome-menu-items.injectable"; -import type { WelcomeMenuRegistration } from "./welcome-menu-items/welcome-menu-registration"; -import welcomeBannerItemsInjectable from "./welcome-banner-items/welcome-banner-items.injectable"; -import type { WelcomeBannerRegistration } from "./welcome-banner-items/welcome-banner-registration"; -import productNameInjectable from "../../../common/vars/product-name.injectable"; - -export const defaultWidth = 320; - -// This is to fix some react 18 type errors -const Carousel = LegacyCarousel as React.ComponentType; - -interface Dependencies { - welcomeMenuItems: IComputedValue; - welcomeBannerItems: IComputedValue; - productName: string; -} - -const NonInjectedWelcome = observer(({ - welcomeMenuItems, - welcomeBannerItems, - productName, -}: Dependencies) => { - const welcomeBanners = welcomeBannerItems.get(); - - // if there is banner with specified width, use it to calculate the width of the container - const maxWidth = Math.max( - ...welcomeBanners.map(banner => banner.width ?? 0), - defaultWidth, - ); - - return ( -
-
- {welcomeBanners.length > 0 ? ( - 1} - autoPlay={true} - navButtonsAlwaysInvisible={true} - indicatorIconButtonProps={{ - style: { - color: "var(--iconActiveBackground)", - }, - }} - activeIndicatorIconButtonProps={{ - style: { - color: "var(--iconActiveColor)", - }, - }} - interval={8000} - > - {welcomeBanners.map((item, index) => ( - - ))} - - ) : ( - - )} - -
-
-

- {`Welcome to ${productName}!`} -

- -

- To get you started we have auto-detected your clusters in your - {" "} - kubeconfig file and added them to the catalog, your centralized - {" "} - view for managing all your cloud-native resources. -
-
- {"If you have any questions or feedback, please join our "} - - Lens Community slack channel - - . -

- - -
-
-
-
- ); -}); - -export const Welcome = withInjectables(NonInjectedWelcome, { - getProps: (di) => ({ - welcomeMenuItems: di.inject(welcomeMenuItemsInjectable), - welcomeBannerItems: di.inject(welcomeBannerItemsInjectable), - productName: di.inject(productNameInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx b/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx deleted file mode 100644 index d361180e49..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { CronJob, CronJobApi } from "../../../common/k8s-api/endpoints"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; -import type { OpenCronJobTriggerDialog } from "./trigger-dialog/open.injectable"; -import openCronJobTriggerDialogInjectable from "./trigger-dialog/open.injectable"; -import cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable"; -import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; - -export interface CronJobMenuProps extends KubeObjectMenuProps {} - -interface Dependencies { - openConfirmDialog: OpenConfirmDialog; - openCronJobTriggerDialog: OpenCronJobTriggerDialog; - cronJobApi: CronJobApi; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -const NonInjectedCronJobMenu = ({ - object, - toolbar, - openConfirmDialog, - openCronJobTriggerDialog, - cronJobApi, - showCheckedErrorNotification, -}: Dependencies & CronJobMenuProps) => ( - <> - openCronJobTriggerDialog(object)}> - - Trigger - - - {object.isSuspend() - ? ( - openConfirmDialog({ - ok: async () => { - try { - await cronJobApi.resume({ namespace: object.getNs(), name: object.getName() }); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while resuming CronJob"); - } - }, - labelOk: `Resume`, - message: ( -

- {"Resume CronJob "} - {object.getName()} - ? -

- ), - })} - > - - Resume -
- ) - : ( - openConfirmDialog({ - ok: async () => { - try { - await cronJobApi.suspend({ namespace: object.getNs(), name: object.getName() }); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while suspending CronJob"); - } - }, - labelOk: `Suspend`, - message: ( -

- {"Suspend CronJob "} - {object.getName()} - ? -

), - })} - > - - Suspend -
- ) - } - -); - -export const CronJobMenu = withInjectables(NonInjectedCronJobMenu, { - getProps: (di, props) => ({ - ...props, - openConfirmDialog: di.inject(openConfirmDialogInjectable), - openCronJobTriggerDialog: di.inject(openCronJobTriggerDialogInjectable), - cronJobApi: di.inject(cronJobApiInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts deleted file mode 100644 index 024515e1b8..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { CronJobTriggerDialog } from "./trigger-dialog/view"; -import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; - -const cronJobTriggerDialogClusterFrameChildComponentInjectable = getInjectable({ - id: "cron-job-trigger-dialog-cluster-frame-child-component", - - instantiate: () => ({ - id: "cron-job-trigger-dialog", - shouldRender: computed(() => true), - Component: CronJobTriggerDialog, - }), - - injectionToken: clusterFrameChildComponentInjectionToken, -}); - -export default cronJobTriggerDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/cron-jobs-route-component.injectable.ts b/src/renderer/components/+workloads-cronjobs/cron-jobs-route-component.injectable.ts deleted file mode 100644 index 0a774177bd..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cron-jobs-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { CronJobs } from "./cronjobs"; -import cronJobsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const cronJobsRouteComponentInjectable = getInjectable({ - id: "cron-jobs-route-component", - - instantiate: (di) => ({ - route: di.inject(cronJobsRouteInjectable), - Component: CronJobs, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default cronJobsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/cron-jobs-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-cronjobs/cron-jobs-sidebar-items.injectable.tsx deleted file mode 100644 index cc7c4a7140..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cron-jobs-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 cronJobsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToCronJobsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/cron-jobs/navigate-to-cron-jobs.injectable"; - -const cronJobsSidebarItemsInjectable = getInjectable({ - id: "cron-jobs-sidebar-items", - - instantiate: (di) => { - const route = di.inject(cronJobsRouteInjectable); - const navigateToCronJobs = di.inject(navigateToCronJobsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "cron-jobs", - parentId: workloadsSidebarItemId, - title: "CronJobs", - onClick: navigateToCronJobs, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 80, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default cronJobsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.scss b/src/renderer/components/+workloads-cronjobs/cronjob-details.scss deleted file mode 100644 index 334f6f0ca7..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.scss +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -.CronJobDetails { - .job { - .title { - margin-top: $margin * 2; - margin-bottom: $margin; - - a { - color: var(--colorInfo); - } - } - } - - .conditions { - @include job-condition-bgs; - } -} diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx deleted file mode 100644 index c8f925edc1..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./cronjob-details.scss"; - -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { DrawerItem, DrawerTitle } from "../drawer"; -import { Badge } from "../badge/badge"; -import type { JobStore } from "../+workloads-jobs/store"; -import { Link } from "react-router-dom"; -import type { CronJobStore } from "./store"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { Job } from "../../../common/k8s-api/endpoints"; -import { CronJob } from "../../../common/k8s-api/endpoints"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import cronJobStoreInjectable from "./store.injectable"; -import jobStoreInjectable from "../+workloads-jobs/store.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; -import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; - -export interface CronJobDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - jobStore: JobStore; - cronJobStore: CronJobStore; - logger: Logger; - getDetailsUrl: GetDetailsUrl; -} - -@observer -class NonInjectedCronJobDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.jobStore, - ]), - ]); - } - - render() { - const { object: cronJob, jobStore, cronJobStore, getDetailsUrl } = this.props; - - if (!cronJob) { - return null; - } - - if (!(cronJob instanceof CronJob)) { - this.props.logger.error("[CronJobDetails]: passed object that is not an instanceof CronJob", cronJob); - - return null; - } - - const childJobs = jobStore.getJobsByOwner(cronJob); - - return ( -
- - { - cronJob.isNeverRun() - ? `never (${cronJob.getSchedule()})` - : cronJob.getSchedule() - } - - - {cronJobStore.getActiveJobsNum(cronJob)} - - - {cronJob.getSuspendFlag()} - - - {cronJob.getLastScheduleTime()} - - {childJobs.length > 0 && ( - <> - Jobs - {childJobs.map((job: Job) => { - const selectors = job.getSelectors(); - const condition = job.getCondition(); - - return ( -
-
- job.selfLink ? getDetailsUrl(job.selfLink) : ""}> - {job.getName()} - -
- - {condition && ( - - )} - - - { - selectors.map(label => ) - } - -
- );}) - } - - )} -
- ); - } -} - -export const CronJobDetails = withInjectables(NonInjectedCronJobDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - cronJobStore: di.inject(cronJobStoreInjectable), - jobStore: di.inject(jobStoreInjectable), - logger: di.inject(loggerInjectable), - getDetailsUrl: di.inject(getDetailsUrlInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.scss b/src/renderer/components/+workloads-cronjobs/cronjobs.scss deleted file mode 100644 index 9fc51383b7..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.CronJobs { - .TableCell { - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx deleted file mode 100644 index 365c469917..0000000000 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./cronjobs.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import moment from "moment"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { CronJobStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import cronJobStoreInjectable from "./store.injectable"; -import eventStoreInjectable from "../+events/store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - schedule = "schedule", - suspend = "suspend", - active = "active", - lastSchedule = "last-schedule", - age = "age", -} - -interface Dependencies { - cronJobStore: CronJobStore; - eventStore: EventStore; -} - -const NonInjectedCronJobs = observer((props: Dependencies) => { - const { - cronJobStore, - eventStore, - } = props; - - return ( - - cronJob.getName(), - [columnId.namespace]: cronJob => cronJob.getNs(), - [columnId.suspend]: cronJob => cronJob.getSuspendFlag(), - [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), - [columnId.lastSchedule]: cronJob => ( - cronJob.status?.lastScheduleTime - ? moment().diff(cronJob.status.lastScheduleTime) - : 0 - ), - [columnId.age]: cronJob => -cronJob.getCreationTimestamp(), - }} - searchFilters={[ - cronJob => cronJob.getSearchFields(), - cronJob => cronJob.getSchedule(), - ]} - renderHeaderTitle="Cron Jobs" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Schedule", className: "schedule", id: columnId.schedule }, - { title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend }, - { title: "Active", className: "active", sortBy: columnId.active, id: columnId.active }, - { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={cronJob => [ - cronJob.getName(), - , - , - cronJob.isNeverRun() ? "never" : cronJob.getSchedule(), - cronJob.getSuspendFlag(), - cronJobStore.getActiveJobsNum(cronJob), - cronJob.getLastScheduleTime(), - , - ]} - /> - - ); -}); - -export const CronJobs = withInjectables(NonInjectedCronJobs, { - getProps: (di, props) => ({ - ...props, - cronJobStore: di.inject(cronJobStoreInjectable), - eventStore: di.inject(eventStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-cronjobs/index.ts b/src/renderer/components/+workloads-cronjobs/index.ts deleted file mode 100644 index 74c20f5131..0000000000 --- a/src/renderer/components/+workloads-cronjobs/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./cronjobs"; -export * from "./cronjob-details"; diff --git a/src/renderer/components/+workloads-cronjobs/store.injectable.ts b/src/renderer/components/+workloads-cronjobs/store.injectable.ts deleted file mode 100644 index 98d539a9ce..0000000000 --- a/src/renderer/components/+workloads-cronjobs/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import getJobsByOwnerInjectable from "../+workloads-jobs/get-jobs-by-owner.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { CronJobStore } from "./store"; - -const cronJobStoreInjectable = getInjectable({ - id: "cron-job-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "cronJobStore is only available in certain environments"); - - const api = di.inject(cronJobApiInjectable); - - return new CronJobStore({ - getJobsByOwner: di.inject(getJobsByOwnerInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default cronJobStoreInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/store.ts b/src/renderer/components/+workloads-cronjobs/store.ts deleted file mode 100644 index fe6c765723..0000000000 --- a/src/renderer/components/+workloads-cronjobs/store.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { CronJob, CronJobApi } from "../../../common/k8s-api/endpoints/cron-job.api"; -import type { GetJobsByOwner } from "../+workloads-jobs/get-jobs-by-owner.injectable"; - -interface Dependencies extends KubeObjectStoreDependencies { - getJobsByOwner: GetJobsByOwner; -} - -export class CronJobStore extends KubeObjectStore { - constructor(protected readonly dependencies: Dependencies, api: CronJobApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - getStatuses(cronJobs?: CronJob[]) { - const status = { scheduled: 0, suspended: 0 }; - - cronJobs?.forEach(cronJob => { - if (cronJob.spec.suspend) { - status.suspended++; - } - else { - status.scheduled++; - } - }); - - return status; - } - - getActiveJobsNum(cronJob: CronJob) { - // Active jobs are jobs without any condition 'Complete' nor 'Failed' - const jobs = this.dependencies.getJobsByOwner(cronJob); - - if (!jobs.length) return 0; - - return jobs.filter(job => !job.getCondition()).length; - } -} diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts deleted file mode 100644 index d089db47a9..0000000000 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/close.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 { action } from "mobx"; -import cronJobTriggerDialogStateInjectable from "./state.injectable"; - -const closeCronJobTriggerDialogInjectable = getInjectable({ - id: "close-cron-job-trigger-dialog", - instantiate: (di) => { - const state = di.inject(cronJobTriggerDialogStateInjectable); - - return action(() => state.set(undefined)); - }, -}); - -export default closeCronJobTriggerDialogInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts deleted file mode 100644 index 79ebc8a618..0000000000 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/open.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { action } from "mobx"; -import type { CronJob } from "../../../../common/k8s-api/endpoints"; -import cronJobTriggerDialogStateInjectable from "./state.injectable"; - -export type OpenCronJobTriggerDialog = (cronJob: CronJob) => void; - -const openCronJobTriggerDialogInjectable = getInjectable({ - id: "open-cron-job-trigger-dialog", - instantiate: (di): OpenCronJobTriggerDialog => { - const state = di.inject(cronJobTriggerDialogStateInjectable); - - return action((cronJob) => state.set(cronJob)); - }, -}); - -export default openCronJobTriggerDialogInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts b/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts deleted file mode 100644 index 944eae40c4..0000000000 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/state.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { CronJob } from "../../../../common/k8s-api/endpoints"; - -const cronJobTriggerDialogStateInjectable = getInjectable({ - id: "cron-job-trigger-dialog-state", - instantiate: () => observable.box(), -}); - -export default cronJobTriggerDialogStateInjectable; diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.scss b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.scss deleted file mode 100644 index b4ba8c2ffa..0000000000 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.CronJobTriggerDialog { - .Wizard { - .header { - span { - color: #a0a0a0; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - .WizardStep { - .step-content { - min-height: 90px; - overflow: hidden; - } - } - } -} diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx deleted file mode 100644 index 48ff80dc28..0000000000 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./view.scss"; - -import React, { Component } from "react"; -import type { IObservableValue } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import type { CronJob, JobApi } from "../../../../common/k8s-api/endpoints"; -import type { ShowNotification } from "../../notifications"; -import { cssNames } from "../../../utils"; -import { Input } from "../../input"; -import { systemName, maxLength } from "../../input/input_validators"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import closeCronJobTriggerDialogInjectable from "./close.injectable"; -import jobApiInjectable from "../../../../common/k8s-api/endpoints/job.api.injectable"; -import cronJobTriggerDialogStateInjectable from "./state.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; -import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable"; - -export interface CronJobTriggerDialogProps extends Partial { -} - -interface Dependencies { - state: IObservableValue; - jobApi: JobApi; - closeCronJobTriggerDialog: () => void; - showCheckedErrorNotification: ShowCheckedErrorNotification; - showErrorNotification: ShowNotification; -} - -@observer -class NonInjectedCronJobTriggerDialog extends Component { - @observable jobName = ""; - - constructor(props: CronJobTriggerDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - onOpen = () => { - const cronJob = this.props.state.get(); - - this.jobName = cronJob ? `${cronJob.getName()}-manual-${Math.random().toString(36).slice(2, 7)}` : ""; - this.jobName = this.jobName.slice(0, 63); - }; - - async trigger(cronJob: CronJob): Promise { - if (!cronJob.spec.jobTemplate) { - this.props.showErrorNotification(`CronJob ${cronJob.getName()} has no jobTemplate`); - - return; - } - - try { - await this.props.jobApi.create({ - name: this.jobName, - namespace: cronJob.getNs(), - }, { - spec: cronJob.spec.jobTemplate.spec, - metadata: { - annotations: { "cronjob.kubernetes.io/instantiate": "manual" }, - ownerReferences: [{ - apiVersion: cronJob.apiVersion, - blockOwnerDeletion: true, - controller: true, - kind: cronJob.kind, - name: cronJob.metadata.name, - uid: cronJob.metadata.uid, - }], - }, - }); - - this.props.closeCronJobTriggerDialog(); - } catch (err) { - this.props.showCheckedErrorNotification(err, "Unknown error occurred while creating job"); - } - } - - renderContents(cronJob: CronJob) { - return ( - - Trigger CronJob - {cronJob.getName()} - - )} - done={this.props.closeCronJobTriggerDialog} - > - this.trigger(cronJob)} - nextLabel="Trigger" - > -
- Job name: -
-
- this.jobName = v.toLowerCase()} - className="box grow" - /> -
-
-
- ); - } - - render() { - const { className, state, closeCronJobTriggerDialog, jobApi, ...dialogProps } = this.props; - const cronJob = state.get(); - - return ( - - {cronJob && this.renderContents(cronJob)} - - ); - } -} - -export const CronJobTriggerDialog = withInjectables(NonInjectedCronJobTriggerDialog, { - getProps: (di, props) => ({ - ...props, - closeCronJobTriggerDialog: di.inject(closeCronJobTriggerDialogInjectable), - jobApi: di.inject(jobApiInjectable), - state: di.inject(cronJobTriggerDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - showErrorNotification: di.inject(showErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.scss b/src/renderer/components/+workloads-daemonsets/daemonset-details.scss deleted file mode 100644 index 89863c9a20..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.DaemonSetDetails { -} diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx deleted file mode 100644 index e159d72ab3..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./daemonset-details.scss"; - -import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import { Badge } from "../badge"; -import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; -import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; -import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; -import type { DaemonSetStore } from "./store"; -import type { PodStore } from "../+workloads-pods/store"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { DaemonSet } from "../../../common/k8s-api/endpoints"; -import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import daemonSetStoreInjectable from "./store.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.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 { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - daemonSetStore: DaemonSetStore; - podStore: PodStore; - logger: Logger; -} - -@observer -class NonInjectedDaemonSetDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.podStore, - ]), - ]); - } - - render() { - const { object: daemonSet, daemonSetStore, logger } = this.props; - - if (!daemonSet) { - return null; - } - - if (!(daemonSet instanceof DaemonSet)) { - logger.error("[DaemonSetDetails]: passed object that is not an instanceof DaemonSet", daemonSet); - - return null; - } - - const { spec } = daemonSet; - const selectors = daemonSet.getSelectors(); - const images = daemonSet.getImages(); - const nodeSelector = daemonSet.getNodeSelectors(); - const childPods = daemonSetStore.getChildPods(daemonSet); - - return ( -
- {selectors.length > 0 && ( - - { - selectors.map(label => ) - } - - )} - {nodeSelector.length > 0 && ( - - { - nodeSelector.map(label => ()) - } - - )} - {images.length > 0 && ( - - { - images.map(image =>

{image}

) - } -
- )} - - {spec.updateStrategy.type} - - - - - - - -
- ); - } -} - -export const DaemonSetDetails = withInjectables(NonInjectedDaemonSetDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - daemonSetStore: di.inject(daemonSetStoreInjectable), - podStore: di.inject(podStoreInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), - requestPodMetricsForDaemonSets: di.inject(requestPodMetricsForDaemonSetsInjectable), - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx deleted file mode 100644 index 4c98835d65..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonset-menu.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { DaemonSet, DaemonSetApi } from "../../../common/k8s-api/endpoints"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable"; -import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; -import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; -import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; - -export interface DaemonSetMenuProps extends KubeObjectMenuProps {} - -interface Dependencies { - daemonsetApi: DaemonSetApi; - openConfirmDialog: OpenConfirmDialog; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -const NonInjectedDaemonSetMenu = ({ - daemonsetApi, - object, - toolbar, - openConfirmDialog, - showCheckedErrorNotification, -}: Dependencies & DaemonSetMenuProps) => ( - <> - openConfirmDialog({ - ok: async () => - { - try { - await daemonsetApi.restart({ - namespace: object.getNs(), - name: object.getName(), - }); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while restarting daemonset"); - } - }, - labelOk: "Restart", - message: ( -

- {"Are you sure you want to restart daemonset "} - {object.getName()} - ? -

- ), - })} - > - - Restart -
- -); - -export const DaemonSetMenu = withInjectables(NonInjectedDaemonSetMenu, { - getProps: (di, props) => ({ - ...props, - daemonsetApi: di.inject(daemonSetApiInjectable), - openConfirmDialog: di.inject(openConfirmDialogInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets-route-component.injectable.ts b/src/renderer/components/+workloads-daemonsets/daemonsets-route-component.injectable.ts deleted file mode 100644 index 329c36943c..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonsets-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { DaemonSets } from "./daemonsets"; -import daemonsetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const daemonsetsRouteComponentInjectable = getInjectable({ - id: "daemonsets-route-component", - - instantiate: (di) => ({ - route: di.inject(daemonsetsRouteInjectable), - Component: DaemonSets, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default daemonsetsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-daemonsets/daemonsets-sidebar-items.injectable.tsx deleted file mode 100644 index 3ec7c3a143..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonsets-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 daemonsetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToDaemonsetsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable"; - -const daemonsetsSidebarItemsInjectable = getInjectable({ - id: "daemonsets-sidebar-items", - - instantiate: (di) => { - const route = di.inject(daemonsetsRouteInjectable); - const navigateToDaemonsets = di.inject(navigateToDaemonsetsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "daemon-sets", - parentId: workloadsSidebarItemId, - title: "DaemonSets", - onClick: () => navigateToDaemonsets(), - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 40, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default daemonsetsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.scss b/src/renderer/components/+workloads-daemonsets/daemonsets.scss deleted file mode 100644 index a5ea8e74be..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.scss +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.DaemonSets { - .TableCell { - &.name { - flex-grow: 1.6; - } - - &.pods { - flex-grow: 0.3; - } - - &.warning { - @include table-cell-warning; - } - - &.labels { - flex-grow: 1.5; - @include table-cell-labels-offsets; - } - - - } -} diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx deleted file mode 100644 index 9760cd2457..0000000000 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./daemonsets.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { DaemonSet } from "../../../common/k8s-api/endpoints"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { Badge } from "../badge"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { DaemonSetStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import daemonSetStoreInjectable from "./store.injectable"; -import eventStoreInjectable from "../+events/store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - pods = "pods", - labels = "labels", - age = "age", -} - -interface Dependencies { - daemonSetStore: DaemonSetStore; - eventStore: EventStore; -} - -const NonInjectedDaemonSets = observer((props: Dependencies) => { - const { - daemonSetStore, - eventStore, - } = props; - - const getPodsLength = (daemonSet: DaemonSet) => daemonSetStore.getChildPods(daemonSet).length; - - return ( - - daemonSet.getName(), - [columnId.namespace]: daemonSet => daemonSet.getNs(), - [columnId.pods]: daemonSet => getPodsLength(daemonSet), - [columnId.age]: daemonSet => -daemonSet.getCreationTimestamp(), - }} - searchFilters={[ - daemonSet => daemonSet.getSearchFields(), - daemonSet => daemonSet.getLabels(), - ]} - renderHeaderTitle="Daemon Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { - title: "Namespace", - className: "namespace", - sortBy: columnId.namespace, - id: columnId.namespace, - }, - { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, - { className: "warning", showWithColumn: columnId.pods }, - { title: "Node Selector", className: "labels scrollable", id: columnId.labels }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={daemonSet => [ - daemonSet.getName(), - , - getPodsLength(daemonSet), - , - daemonSet.getNodeSelectors().map(selector => ( - - )), - , - ]} - /> - - ); -}); - -export const DaemonSets = withInjectables(NonInjectedDaemonSets, { - getProps: (di, props) => ({ - ...props, - daemonSetStore: di.inject(daemonSetStoreInjectable), - eventStore: di.inject(eventStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-daemonsets/index.ts b/src/renderer/components/+workloads-daemonsets/index.ts deleted file mode 100644 index 7d3ff97b2c..0000000000 --- a/src/renderer/components/+workloads-daemonsets/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./daemonsets"; -export * from "./daemonset-details"; diff --git a/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx deleted file mode 100644 index 3975308f46..0000000000 --- a/src/renderer/components/+workloads-daemonsets/metrics-details-component.injectable.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { IAsyncComputed } from "@ogre-tools/injectable-react"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { DaemonSet } from "../../../common/k8s-api/endpoints"; -import type { DaemonSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import daemonSetMetricsInjectable from "./metrics.injectable"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedDaemonSetMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const DaemonSetMetricsDetailsComponent = withInjectables>(NonInjectedDaemonSetMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(daemonSetMetricsInjectable, props.object), - ...props, - }), -}); - -const daemonSetMetricsDetailsComponentInjectable = getInjectable({ - id: "daemon-set-metrics-details-component", - instantiate: (di) => ({ - Component: DaemonSetMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.DaemonSet), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default daemonSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts b/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts deleted file mode 100644 index 71f4f54ee9..0000000000 --- a/src/renderer/components/+workloads-daemonsets/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { DaemonSet } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsForDaemonSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable"; - -const daemonSetMetricsInjectable = getInjectable({ - id: "daemon-set-metrics", - instantiate: (di, daemonSet) => { - const requestPodMetricsForDaemonSets = di.inject(requestPodMetricsForDaemonSetsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: () => { - now(60 * 1000); // update every minute - - return requestPodMetricsForDaemonSets([daemonSet], daemonSet.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, daemonSet: DaemonSet) => daemonSet.getId(), - }), -}); - -export default daemonSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/store.injectable.ts b/src/renderer/components/+workloads-daemonsets/store.injectable.ts deleted file mode 100644 index 4b79b19bb5..0000000000 --- a/src/renderer/components/+workloads-daemonsets/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import { DaemonSetStore } from "./store"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -const daemonSetStoreInjectable = getInjectable({ - id: "daemon-set-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "daemonSetStore is only available in certain environments"); - - const api = di.inject(daemonSetApiInjectable); - - return new DaemonSetStore({ - getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default daemonSetStoreInjectable; diff --git a/src/renderer/components/+workloads-daemonsets/store.ts b/src/renderer/components/+workloads-daemonsets/store.ts deleted file mode 100644 index 1345468b62..0000000000 --- a/src/renderer/components/+workloads-daemonsets/store.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import type { DaemonSet, DaemonSetApi, Pod } from "../../../common/k8s-api/endpoints"; -import { PodStatusPhase } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; - -export interface DaemonSetStoreDependencies extends KubeObjectStoreDependencies { - readonly getPodsByOwnerId: GetPodsByOwnerId; -} - -export class DaemonSetStore extends KubeObjectStore { - constructor(protected readonly dependencies: DaemonSetStoreDependencies, api: DaemonSetApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - getChildPods(daemonSet: DaemonSet): Pod[] { - return this.dependencies.getPodsByOwnerId(daemonSet.getId()); - } - - getStatuses(daemonSets?: DaemonSet[]) { - const status = { running: 0, failed: 0, pending: 0 }; - - for (const daemonSet of daemonSets ?? []) { - const statuses = new Set(this.getChildPods(daemonSet).map(pod => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - } - - return status; - } -} diff --git a/src/renderer/components/+workloads-deployments/deployment-details.scss b/src/renderer/components/+workloads-deployments/deployment-details.scss deleted file mode 100644 index ef0c2d35e2..0000000000 --- a/src/renderer/components/+workloads-deployments/deployment-details.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.DeploymentDetails { - .conditions { - .available { - color: white; - background-color: $deployment-available; - } - - .progressing { - color: white; - background-color: $deployment-progressing; - } - - .replica-failure { - color: white; - background-color: $deployment-replicafailure; - } - } -} diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx deleted file mode 100644 index 6137c7af48..0000000000 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./deployment-details.scss"; - -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import { Badge } from "../badge"; -import { Deployment } from "../../../common/k8s-api/endpoints"; -import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; -import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { DeploymentStore } from "./store"; -import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import type { ReplicaSetStore } from "../+workloads-replicasets/store"; -import { DeploymentReplicaSets } from "./deployment-replicasets"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable"; -import deploymentStoreInjectable from "./store.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface DeploymentDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - replicaSetStore: ReplicaSetStore; - deploymentStore: DeploymentStore; - logger: Logger; -} - -@observer -class NonInjectedDeploymentDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.replicaSetStore, - ]), - ]); - } - - render() { - const { object: deployment, replicaSetStore, deploymentStore, logger } = this.props; - - if (!deployment) { - return null; - } - - if (!(deployment instanceof Deployment)) { - logger.error("[DeploymentDetails]: passed object that is not an instanceof Deployment", deployment); - - return null; - } - - const { status, spec } = deployment; - const nodeSelector = deployment.getNodeSelectors(); - const selectors = deployment.getSelectors(); - const childPods = deploymentStore.getChildPods(deployment); - const replicaSets = replicaSetStore.getReplicaSetsByOwner(deployment); - - return ( -
- - {`${spec.replicas} desired, ${status?.updatedReplicas ?? 0} updated, `} - {`${status?.replicas ?? 0} total, ${status?.availableReplicas ?? 0} available, `} - {`${status?.unavailableReplicas ?? 0} unavailable`} - - {selectors.length > 0 && ( - - { - selectors.map(label => ) - } - - )} - {nodeSelector.length > 0 && ( - - { - nodeSelector.map(label => ( - - )) - } - - )} - - {spec.strategy.type} - - - { - deployment.getConditions() - .map(({ type, message, lastTransitionTime, status }) => ( - -

{message}

-

- {`Last transition time: ${lastTransitionTime}`} -

- - )} /> - )) - } -
- - - - -
- ); - } -} - -export const DeploymentDetails = withInjectables(NonInjectedDeploymentDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - replicaSetStore: di.inject(replicaSetStoreInjectable), - deploymentStore: di.inject(deploymentStoreInjectable), - logger: di.inject(loggerInjectable), - }), -}); - diff --git a/src/renderer/components/+workloads-deployments/deployment-menu.tsx b/src/renderer/components/+workloads-deployments/deployment-menu.tsx deleted file mode 100644 index a32cc0eb11..0000000000 --- a/src/renderer/components/+workloads-deployments/deployment-menu.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import type { OpenDeploymentScaleDialog } from "./scale/open.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable"; -import openDeploymentScaleDialogInjectable from "./scale/open.injectable"; -import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; -import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; -import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; - -export interface DeploymentMenuProps extends KubeObjectMenuProps {} - -interface Dependencies { - openDeploymentScaleDialog: OpenDeploymentScaleDialog; - deploymentApi: DeploymentApi; - openConfirmDialog: OpenConfirmDialog; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -const NonInjectedDeploymentMenu = ({ - deploymentApi, - object, - openDeploymentScaleDialog, - toolbar, - openConfirmDialog, - showCheckedErrorNotification, -}: Dependencies & DeploymentMenuProps) => ( - <> - openDeploymentScaleDialog(object)}> - - Scale - - openConfirmDialog({ - ok: async () => - { - try { - await deploymentApi.restart({ - namespace: object.getNs(), - name: object.getName(), - }); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while restarting deployment"); - } - }, - labelOk: "Restart", - message: ( -

- {"Are you sure you want to restart deployment "} - {object.getName()} - ? -

- ), - })} - > - - Restart -
- -); - -export const DeploymentMenu = withInjectables(NonInjectedDeploymentMenu, { - getProps: (di, props) => ({ - ...props, - deploymentApi: di.inject(deploymentApiInjectable), - openDeploymentScaleDialog: di.inject(openDeploymentScaleDialogInjectable), - openConfirmDialog: di.inject(openConfirmDialogInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-deployments/deployment-replicasets.scss b/src/renderer/components/+workloads-deployments/deployment-replicasets.scss deleted file mode 100644 index 0c04f54709..0000000000 --- a/src/renderer/components/+workloads-deployments/deployment-replicasets.scss +++ /dev/null @@ -1,36 +0,0 @@ -.DeploymentDetails { - .ReplicaSets { - position: relative; - min-height: 80px; - - .Table { - margin: 0 (-$margin * 3); - } - - .TableCell { - &:first-child { - margin-left: $margin; - } - - &:last-child { - margin-right: $margin; - } - - &.name { - flex-grow: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.namespace { - flex-grow: 1.2; - } - - &.actions { - @include table-cell-action; - } - } - } -} diff --git a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx b/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx deleted file mode 100644 index fc334f87f0..0000000000 --- a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./deployment-replicasets.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import { KubeObjectMenu } from "../kube-object-menu"; -import { Spinner } from "../spinner"; -import { prevDefault, stopPropagation } from "../../utils"; -import { DrawerTitle } from "../drawer"; -import { Table, TableCell, TableHead, TableRow } from "../table"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { KubeObjectAge } from "../kube-object/age"; -import type { ReplicaSetStore } from "../+workloads-replicasets/store"; -import type { ShowDetails } from "../kube-detail-params/show-details.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import showDetailsInjectable from "../kube-detail-params/show-details.injectable"; -import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable"; - - -enum sortBy { - name = "name", - namespace = "namespace", - pods = "pods", - age = "age", -} - -interface Dependencies { - replicaSetStore: ReplicaSetStore; - showDetails: ShowDetails; -} - -export interface DeploymentReplicaSetsProps { - replicaSets: ReplicaSet[]; -} - -interface Dependencies { - replicaSetStore: ReplicaSetStore; - showDetails: ShowDetails; -} - -@observer -class NonInjectedDeploymentReplicaSets extends React.Component { - getPodsLength(replicaSet: ReplicaSet) { - return this.props.replicaSetStore.getChildPods(replicaSet).length; - } - - render() { - const { - replicaSets, - replicaSetStore, - showDetails, - } = this.props; - - if (!replicaSets.length && !replicaSetStore.isLoaded) return ( -
- ); - if (!replicaSets.length) return null; - - return ( -
- Deploy Revisions - replicaSet.getName(), - [sortBy.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(), - [sortBy.age]: (replicaSet: ReplicaSet) => replicaSet.metadata.creationTimestamp, - [sortBy.pods]: (replicaSet: ReplicaSet) => this.getPodsLength(replicaSet), - }} - sortByDefault={{ sortBy: sortBy.pods, orderBy: "desc" }} - sortSyncWithUrl={false} - className="box grow" - > - - Name - - Namespace - Pods - Age - - - { - replicaSets.map(replica => ( - showDetails(replica.selfLink, false))} - > - {replica.getName()} - - {replica.getNs()} - {this.getPodsLength(replica)} - - - - - - )) - } -
-
- ); - } -} - -export const DeploymentReplicaSets = withInjectables(NonInjectedDeploymentReplicaSets, { - getProps: (di, props) => ({ - ...props, - replicaSetStore: di.inject(replicaSetStoreInjectable), - showDetails: di.inject(showDetailsInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-deployments/deployments-route-component.injectable.ts b/src/renderer/components/+workloads-deployments/deployments-route-component.injectable.ts deleted file mode 100644 index 65ec34acb2..0000000000 --- a/src/renderer/components/+workloads-deployments/deployments-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Deployments } from "./deployments"; -import deploymentsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const deploymentsRouteComponentInjectable = getInjectable({ - id: "deployments-route-component", - - instantiate: (di) => ({ - route: di.inject(deploymentsRouteInjectable), - Component: Deployments, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default deploymentsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/deployments-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-deployments/deployments-sidebar-items.injectable.tsx deleted file mode 100644 index 9f06301b8c..0000000000 --- a/src/renderer/components/+workloads-deployments/deployments-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 deploymentsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToDeploymentsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable"; - -const deploymentsSidebarItemsInjectable = getInjectable({ - id: "deployments-sidebar-items", - - instantiate: (di) => { - const route = di.inject(deploymentsRouteInjectable); - const navigateToDeployments = di.inject(navigateToDeploymentsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "deployments", - parentId: workloadsSidebarItemId, - title: "Deployments", - onClick: navigateToDeployments, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 30, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default deploymentsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-deployments/deployments.scss b/src/renderer/components/+workloads-deployments/deployments.scss deleted file mode 100644 index 5d9ed72c03..0000000000 --- a/src/renderer/components/+workloads-deployments/deployments.scss +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@import "../+workloads/workloads-mixins"; - -.Deployments { - .TableCell { - &.name { - flex-grow: 1.5; - } - - &.namespace { - flex-grow: 1.5; - } - - &.pods { - flex-grow: 1; - } - - &.warning { - @include table-cell-warning; - } - - &.conditions { - flex-grow: 1.5; - - .condition { - margin-right: $margin; - - &.available { - color: $deployment-available; - } - - &.progressing { - color: $deployment-progressing; - } - - &.replica-failure { - color: $deployment-replicafailure; - } - } - } - - - } -} diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx deleted file mode 100644 index 6fa9fff211..0000000000 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./deployments.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { Deployment } from "../../../common/k8s-api/endpoints"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { cssNames } from "../../utils"; -import kebabCase from "lodash/kebabCase"; -import orderBy from "lodash/orderBy"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { DeploymentStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import deploymentStoreInjectable from "./store.injectable"; -import eventStoreInjectable from "../+events/store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - pods = "pods", - replicas = "replicas", - age = "age", - condition = "condition", -} - -interface Dependencies { - deploymentStore: DeploymentStore; - eventStore: EventStore; -} - -@observer -class NonInjectedDeployments extends React.Component { - renderPods(deployment: Deployment) { - const { replicas, availableReplicas } = deployment.status ?? {}; - - return `${availableReplicas || 0}/${replicas || 0}`; - } - - renderConditions(deployment: Deployment) { - const conditions = orderBy(deployment.getConditions(true), "type", "asc"); - - return conditions.map(({ type, message }) => ( - - {type} - - )); - } - - render() { - const { - deploymentStore, - eventStore, - } = this.props; - - return ( - - deployment.getName(), - [columnId.namespace]: deployment => deployment.getNs(), - [columnId.replicas]: deployment => deployment.getReplicas(), - [columnId.age]: deployment => -deployment.getCreationTimestamp(), - [columnId.condition]: deployment => deployment.getConditionsText(), - }} - searchFilters={[ - deployment => deployment.getSearchFields(), - deployment => deployment.getConditionsText(), - ]} - renderHeaderTitle="Deployments" - 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: "Pods", className: "pods", id: columnId.pods }, - { - title: "Replicas", - className: "replicas", - sortBy: columnId.replicas, - id: columnId.replicas, - }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - { - title: "Conditions", - className: "conditions", - sortBy: columnId.condition, - id: columnId.condition, - }, - ]} - renderTableContents={deployment => [ - deployment.getName(), - , - , - this.renderPods(deployment), - deployment.getReplicas(), - , - this.renderConditions(deployment), - ]} - /> - - ); - } -} - -export const Deployments = withInjectables(NonInjectedDeployments, { - getProps: (di, props) => ({ - ...props, - deploymentStore: di.inject(deploymentStoreInjectable), - eventStore: di.inject(eventStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-deployments/index.ts b/src/renderer/components/+workloads-deployments/index.ts deleted file mode 100644 index 7e715da22b..0000000000 --- a/src/renderer/components/+workloads-deployments/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./deployments"; -export * from "./deployment-details"; diff --git a/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx deleted file mode 100644 index d40e93641d..0000000000 --- a/src/renderer/components/+workloads-deployments/metrics-details-component.injectable.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { IAsyncComputed } from "@ogre-tools/injectable-react"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { Deployment } from "../../../common/k8s-api/endpoints"; -import type { DeploymentPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import deploymentMetricsInjectable from "./metrics.injectable"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedDeploymentMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const DeploymentMetricsDetailsComponent = withInjectables>(NonInjectedDeploymentMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(deploymentMetricsInjectable, props.object), - ...props, - }), -}); - -const deploymentMetricsDetailsComponentInjectable = getInjectable({ - id: "deployment-metrics-details-component", - instantiate: (di) => ({ - Component: DeploymentMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Deployment), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default deploymentMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/metrics.injectable.ts b/src/renderer/components/+workloads-deployments/metrics.injectable.ts deleted file mode 100644 index fecd67c5db..0000000000 --- a/src/renderer/components/+workloads-deployments/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { Deployment } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsForDeploymentsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-deployments.injectable"; - -const deploymentMetricsInjectable = getInjectable({ - id: "deployment-metrics", - instantiate: (di, deployment) => { - const requestPodMetricsForDeployments = di.inject(requestPodMetricsForDeploymentsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: () => { - now(60 * 1000); - - return requestPodMetricsForDeployments([deployment], deployment.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, deployment: Deployment) => deployment.getId(), - }), -}); - -export default deploymentMetricsInjectable; diff --git a/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts deleted file mode 100644 index 31a444dba0..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { DeploymentScaleDialog } from "./dialog"; -import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; - -const deploymentScaleDialogClusterFrameChildComponentInjectable = getInjectable({ - id: "deployment-scale-dialog-cluster-frame-child-component", - - instantiate: () => ({ - id: "deployment-scale-dialog", - shouldRender: computed(() => true), - Component: DeploymentScaleDialog, - }), - - injectionToken: clusterFrameChildComponentInjectionToken, -}); - -export default deploymentScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/scale/dialog-state.injectable.ts b/src/renderer/components/+workloads-deployments/scale/dialog-state.injectable.ts deleted file mode 100644 index 9da03e509f..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/dialog-state.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { Deployment } from "../../../../common/k8s-api/endpoints"; - -const deploymentScaleDialogStateInjectable = getInjectable({ - id: "deployment-scale-dialog-state", - instantiate: () => observable.box(), -}); - -export default deploymentScaleDialogStateInjectable; diff --git a/src/renderer/components/+workloads-deployments/scale/dialog.scss b/src/renderer/components/+workloads-deployments/scale/dialog.scss deleted file mode 100644 index 06a93316aa..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/dialog.scss +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.DeploymentScaleDialog { - .Wizard { - .header { - span { - color: #a0a0a0; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - .WizardStep { - .step-content { - min-height: 90px; - overflow: hidden; - } - } - - .current-scale { - font-weight: bold - } - - .desired-scale { - flex: 1.1 0; - } - - .slider-container { - flex: 1 0; - } - - .plus-minus-container { - margin-left: $margin * 2; - .Icon { - --color-active: black; - } - } - - .warning { - color: var(--colorSoftError); - font-size: small; - display: flex; - align-items: center; - - .Icon { - margin: 0; - margin-right: $margin; - } - } - } -} diff --git a/src/renderer/components/+workloads-deployments/scale/dialog.test.tsx b/src/renderer/components/+workloads-deployments/scale/dialog.test.tsx deleted file mode 100644 index b7f6ba99cb..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/dialog.test.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { waitFor, fireEvent } from "@testing-library/react"; -import "@testing-library/jest-dom/extend-expect"; - -import { DeploymentScaleDialog } from "./dialog"; -import type { DeploymentApi } from "../../../../common/k8s-api/endpoints/deployment.api"; -import { Deployment } from "../../../../common/k8s-api/endpoints/deployment.api"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable"; -import type { OpenDeploymentScaleDialog } from "./open.injectable"; -import openDeploymentScaleDialogInjectable from "./open.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import type { DiRender } from "../../test-utils/renderFor"; -import { renderFor } from "../../test-utils/renderFor"; - -const dummyDeployment = new Deployment({ - apiVersion: "v1", - kind: "dummy", - metadata: { - uid: "dummy", - name: "dummy", - creationTimestamp: "dummy", - resourceVersion: "dummy", - namespace: "default", - selfLink: "/apis/apps/v1/deployments/default/dummy", - }, - spec: { - replicas: 1, - selector: { matchLabels: { dummy: "label" }}, - template: { - metadata: { - labels: { dummy: "label" }, - }, - spec: { - containers: [{ - name: "dummy", - image: "dummy", - resources: { - requests: { - cpu: "1", - memory: "10Mi", - }, - }, - terminationMessagePath: "dummy", - terminationMessagePolicy: "File", - imagePullPolicy: "Always", - }], - restartPolicy: "dummy", - terminationGracePeriodSeconds: 10, - dnsPolicy: "dummy", - serviceAccountName: "dummy", - serviceAccount: "dummy", - securityContext: {}, - schedulerName: "dummy", - }, - }, - strategy: { - type: "dummy", - rollingUpdate: { - maxUnavailable: 1, - maxSurge: 10, - }, - }, - }, - status: { - observedGeneration: 1, - replicas: 1, - updatedReplicas: 1, - readyReplicas: 1, - conditions: [{ - type: "dummy", - status: "True", - lastTransitionTime: "dummy", - reason: "dummy", - message: "dummy", - }], - }, -}); - -describe("", () => { - let deploymentApi: DeploymentApi; - let openDeploymentScaleDialog: OpenDeploymentScaleDialog; - let render: DiRender; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - deploymentApi = di.inject(deploymentApiInjectable); - openDeploymentScaleDialog = di.inject(openDeploymentScaleDialogInjectable); - render = renderFor(di); - }); - - it("renders w/o errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("inits with a dummy deployment with mocked current/desired scale", async () => { - // mock deploymentApi.getReplicas() which will be called - // when rendered. - const initReplicas = 3; - - deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const { findByTestId } = render(); - - openDeploymentScaleDialog(dummyDeployment); - // we need to wait for the DeploymentScaleDialog to show up - // because there is an in which renders null at start. - await waitFor(async () => { - const [currentScale, desiredScale] = await Promise.all([ - findByTestId("current-scale"), - findByTestId("desired-scale"), - ]); - - expect(currentScale).toHaveTextContent(`${initReplicas}`); - expect(desiredScale).toHaveTextContent(`${initReplicas}`); - }); - - }); - - it("changes the desired scale when clicking the icon buttons +/-", async () => { - const initReplicas = 1; - - deploymentApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const component = render(); - - openDeploymentScaleDialog(dummyDeployment); - await waitFor(async () => { - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect(component.baseElement.querySelector("input")?.value).toBe(`${initReplicas}`); - }); - const up = await component.findByTestId("desired-replicas-up"); - const down = await component.findByTestId("desired-replicas-down"); - - fireEvent.click(up); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas + 1}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect(component.baseElement.querySelector("input")?.value).toBe(`${initReplicas + 1}`); - - fireEvent.click(down); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect(component.baseElement.querySelector("input")?.value).toBe(`${initReplicas}`); - - // edge case, desiredScale must = 0 - let times = 10; - - for (let i = 0; i < times; i++) { - fireEvent.click(down); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("0"); - expect(component.baseElement.querySelector("input")?.value).toBe("0"); - - // edge case, desiredScale must = 100 scaleMax (100) - times = 120; - - for (let i = 0; i < times; i++) { - fireEvent.click(up); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("100"); - expect(component.baseElement.querySelector("input")?.value).toBe("100"); - expect(await component.findByTestId("warning")) - .toHaveTextContent("High number of replicas may cause cluster performance issues"); - }); -}); diff --git a/src/renderer/components/+workloads-deployments/scale/dialog.tsx b/src/renderer/components/+workloads-deployments/scale/dialog.tsx deleted file mode 100644 index d6c8b0fcaa..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/dialog.tsx +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./dialog.scss"; - -import React, { Component } from "react"; -import type { IObservableValue } from "mobx"; -import { computed, observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import type { Deployment, DeploymentApi } from "../../../../common/k8s-api/endpoints"; -import { Icon } from "../../icon"; -import { Slider } from "../../slider"; -import { cssNames } from "../../../utils"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable"; -import deploymentScaleDialogStateInjectable from "./dialog-state.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface DeploymentScaleDialogProps extends Partial { -} - -interface Dependencies { - deploymentApi: DeploymentApi; - state: IObservableValue; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedDeploymentScaleDialog extends Component { - @observable ready = false; - @observable currentReplicas = 0; - @observable desiredReplicas = 0; - - constructor(props: DeploymentScaleDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - close = () => { - this.props.state.set(undefined); - }; - - @computed get scaleMax() { - const { currentReplicas } = this; - const defaultMax = 50; - - return currentReplicas <= defaultMax - ? defaultMax * 2 - : currentReplicas * 2; - } - - onOpen = async (deployment: Deployment) => { - this.currentReplicas = await this.props.deploymentApi.getReplicas({ - namespace: deployment.getNs(), - name: deployment.getName(), - }); - this.desiredReplicas = this.currentReplicas; - this.ready = true; - }; - - onClose = () => { - this.ready = false; - }; - - onChange = (evt: React.ChangeEvent, value: number) => { - this.desiredReplicas = value; - }; - - scale = async (deployment: Deployment) => { - const { currentReplicas, desiredReplicas, close } = this; - - try { - if (currentReplicas !== desiredReplicas) { - await this.props.deploymentApi.scale({ - name: deployment.getName(), - namespace: deployment.getNs(), - }, desiredReplicas); - } - close(); - } catch (err) { - this.props.showCheckedErrorNotification(err, "Unknown error occured while scaling Deployment"); - } - }; - - private readonly scaleMin = 0; - - desiredReplicasUp = () => { - this.desiredReplicas = Math.min(this.scaleMax, this.desiredReplicas + 1); - }; - - desiredReplicasDown = () => { - this.desiredReplicas = Math.max(this.scaleMin, this.desiredReplicas - 1); - }; - - renderContents(deployment: Deployment) { - const { currentReplicas, desiredReplicas, onChange, scaleMax } = this; - const warning = currentReplicas < 10 && desiredReplicas > 90; - - return ( - - Scale Deployment - {deployment.getName()} - - )} - done={this.close} - > - this.scale(deployment)} - nextLabel="Scale" - disabledNext={!this.ready} - > -
- Current replica scale: - {" "} - {currentReplicas} -
-
-
- Desired number of replicas: - {" "} - {desiredReplicas} -
-
- -
-
- - -
-
- {warning && ( -
- - High number of replicas may cause cluster performance issues -
- )} -
-
- ); - } - - render() { - const { className, state, ...dialogProps } = this.props; - const deployment = state.get(); - - return ( - this.onOpen(deployment))} - onClose={this.onClose} - close={this.close} - > - {deployment && this.renderContents(deployment)} - - ); - } -} - -export const DeploymentScaleDialog = withInjectables(NonInjectedDeploymentScaleDialog, { - getProps: (di, props) => ({ - ...props, - deploymentApi: di.inject(deploymentApiInjectable), - state: di.inject(deploymentScaleDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-deployments/scale/open.injectable.ts b/src/renderer/components/+workloads-deployments/scale/open.injectable.ts deleted file mode 100644 index db21f51b34..0000000000 --- a/src/renderer/components/+workloads-deployments/scale/open.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 { Deployment } from "../../../../common/k8s-api/endpoints"; -import deploymentScaleDialogStateInjectable from "./dialog-state.injectable"; - -export type OpenDeploymentScaleDialog = (obj: Deployment) => void; - -const openDeploymentScaleDialogInjectable = getInjectable({ - id: "open-deployment-scale-dialog", - instantiate: (di): OpenDeploymentScaleDialog => { - const state = di.inject(deploymentScaleDialogStateInjectable); - - return obj => state.set(obj); - }, -}); - -export default openDeploymentScaleDialogInjectable; diff --git a/src/renderer/components/+workloads-deployments/store.injectable.ts b/src/renderer/components/+workloads-deployments/store.injectable.ts deleted file mode 100644 index 50ae255e21..0000000000 --- a/src/renderer/components/+workloads-deployments/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import { storesAndApisCanBeCreatedInjectionToken } from "../../../common/k8s-api/stores-apis-can-be-created.token"; -import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable"; -import { DeploymentStore } from "./store"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -const deploymentStoreInjectable = getInjectable({ - id: "deployment-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "deploymentStore is only available in certain environments"); - - const api = di.inject(deploymentApiInjectable); - - return new DeploymentStore({ - podStore: di.inject(podStoreInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default deploymentStoreInjectable; diff --git a/src/renderer/components/+workloads-deployments/store.ts b/src/renderer/components/+workloads-deployments/store.ts deleted file mode 100644 index af5d085594..0000000000 --- a/src/renderer/components/+workloads-deployments/store.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { PodStore } from "../+workloads-pods/store"; -import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints"; -import { PodStatusPhase } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; - -// This needs to be disables because of https://github.com/microsoft/TypeScript/issues/15300 -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type DeploymentStatuses = { - running: number; - failed: number; - pending: number; -}; - -export interface DeploymentStoreDependencies extends KubeObjectStoreDependencies { - readonly podStore: PodStore; -} - -export class DeploymentStore extends KubeObjectStore { - constructor(protected readonly dependencies: DeploymentStoreDependencies, api: DeploymentApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - protected sortItems(items: Deployment[]) { - return super.sortItems(items, [ - item => item.getReplicas(), - ], "desc"); - } - - getStatuses(deployments: Deployment[]): DeploymentStatuses; - /** - * @deprecated - */ - getStatuses(deployments: Deployment[] | undefined): DeploymentStatuses; - getStatuses(deployments: Deployment[] = []) { - const status = { running: 0, failed: 0, pending: 0 }; - - deployments.forEach(deployment => { - const statuses = new Set(this.getChildPods(deployment).map(pod => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - }); - - return status; - } - - getChildPods(deployment: Deployment) { - return this.dependencies.podStore - .getByLabel(deployment.getTemplateLabels()) - .filter(pod => pod.getNs() === deployment.getNs()); - } -} diff --git a/src/renderer/components/+workloads-jobs/get-jobs-by-owner.injectable.ts b/src/renderer/components/+workloads-jobs/get-jobs-by-owner.injectable.ts deleted file mode 100644 index 3429ff5afb..0000000000 --- a/src/renderer/components/+workloads-jobs/get-jobs-by-owner.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { CronJob, Job } from "../../../common/k8s-api/endpoints"; -import { getInjectable } from "@ogre-tools/injectable"; -import jobStoreInjectable from "./store.injectable"; - -export type GetJobsByOwner = (cronJob: CronJob) => Job[]; - -const getJobsByOwnerInjectable = getInjectable({ - id: "get-jobs-by-owner", - instantiate: (di): GetJobsByOwner => { - const store = di.inject(jobStoreInjectable); - - return (cronJob) => store.getJobsByOwner(cronJob); - }, -}); - -export default getJobsByOwnerInjectable; diff --git a/src/renderer/components/+workloads-jobs/index.ts b/src/renderer/components/+workloads-jobs/index.ts deleted file mode 100644 index 2c7ba990de..0000000000 --- a/src/renderer/components/+workloads-jobs/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./jobs"; -export * from "./job-details"; diff --git a/src/renderer/components/+workloads-jobs/job-details.scss b/src/renderer/components/+workloads-jobs/job-details.scss deleted file mode 100644 index 7b101a96b6..0000000000 --- a/src/renderer/components/+workloads-jobs/job-details.scss +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -.JobDetails { - .conditions { - @include job-condition-bgs; - } -} diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx deleted file mode 100644 index 388032cac7..0000000000 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./job-details.scss"; - -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { DrawerItem } from "../drawer"; -import { Badge } from "../badge"; -import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; -import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; -import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; -import type { JobStore } from "./store"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { Job } from "../../../common/k8s-api/endpoints"; -import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import type { PodStore } from "../+workloads-pods/store"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import jobStoreInjectable from "./store.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface JobDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - podStore: PodStore; - jobStore: JobStore; - logger: Logger; -} - -@observer -class NonInjectedJobDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.podStore, - ]), - ]); - } - - render() { - const { object: job, jobStore, logger } = this.props; - - if (!job) { - return null; - } - - if (!(job instanceof Job)) { - logger.error("[JobDetails]: passed object that is not an instanceof Job", job); - - return null; - } - - const selectors = job.getSelectors(); - const nodeSelector = job.getNodeSelectors(); - const images = job.getImages(); - const childPods = jobStore.getChildPods(job); - const condition = job.getCondition(); - - return ( -
- - { - Object.keys(selectors).map(label => ) - } - - {nodeSelector.length > 0 && ( - - { - nodeSelector.map(label => ( - - )) - } - - )} - {images.length > 0 && ( - - { - images.map(image =>

{image}

) - } -
- )} - - {condition && ( - - )} - - - {job.getDesiredCompletions()} - - - {job.getParallelism()} - - - - - - - -
- ); - } -} - -export const JobDetails = withInjectables(NonInjectedJobDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - podStore: di.inject(podStoreInjectable), - jobStore: di.inject(jobStoreInjectable), - logger: di.inject(loggerInjectable), - }), -}); - diff --git a/src/renderer/components/+workloads-jobs/jobs-route-component.injectable.ts b/src/renderer/components/+workloads-jobs/jobs-route-component.injectable.ts deleted file mode 100644 index e060cf95ee..0000000000 --- a/src/renderer/components/+workloads-jobs/jobs-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Jobs } from "./jobs"; -import jobsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const jobsRouteComponentInjectable = getInjectable({ - id: "jobs-route-component", - - instantiate: (di) => ({ - route: di.inject(jobsRouteInjectable), - Component: Jobs, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default jobsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-jobs/jobs-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-jobs/jobs-sidebar-items.injectable.tsx deleted file mode 100644 index d89d6a2874..0000000000 --- a/src/renderer/components/+workloads-jobs/jobs-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 jobsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToJobsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable"; - -const jobsSidebarItemsInjectable = getInjectable({ - id: "jobs-sidebar-items", - - instantiate: (di) => { - const route = di.inject(jobsRouteInjectable); - const navigateToJobs = di.inject(navigateToJobsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "jobs", - parentId: workloadsSidebarItemId, - title: "Jobs", - onClick: navigateToJobs, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 70, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default jobsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-jobs/jobs.scss b/src/renderer/components/+workloads-jobs/jobs.scss deleted file mode 100644 index f293ba5b38..0000000000 --- a/src/renderer/components/+workloads-jobs/jobs.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@import "../+workloads/workloads-mixins"; - -.Jobs { - .TableCell { - &.name { - flex-grow: 2; - } - - &.warning { - @include table-cell-warning; - } - - &.conditions { - @include job-condition-colors; - } - - - } -} diff --git a/src/renderer/components/+workloads-jobs/jobs.tsx b/src/renderer/components/+workloads-jobs/jobs.tsx deleted file mode 100644 index 5893a27708..0000000000 --- a/src/renderer/components/+workloads-jobs/jobs.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./jobs.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import kebabCase from "lodash/kebabCase"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { JobStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import eventStoreInjectable from "../+events/store.injectable"; -import jobStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - completions = "completions", - conditions = "conditions", - age = "age", -} - -interface Dependencies { - jobStore: JobStore; - eventStore: EventStore; -} - -const NonInjectedJobs = observer((props: Dependencies) => { - const { - eventStore, - jobStore, - } = props; - - return ( - - job.getName(), - [columnId.namespace]: job => job.getNs(), - [columnId.conditions]: job => job.getCondition()?.type, - [columnId.age]: job => -job.getCreationTimestamp(), - }} - searchFilters={[ - job => job.getSearchFields(), - ]} - renderHeaderTitle="Jobs" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, - { title: "Completions", className: "completions", id: columnId.completions }, - { className: "warning", showWithColumn: columnId.completions }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, - ]} - renderTableContents={job => { - const condition = job.getCondition(); - - return [ - job.getName(), - , - `${job.getCompletions()} / ${job.getDesiredCompletions()}`, - , - , - condition && { - title: condition.type, - className: kebabCase(condition.type), - }, - ]; - }} - /> - - ); -}); - -export const Jobs = withInjectables(NonInjectedJobs, { - getProps: (di, props) => ({ - ...props, - eventStore: di.inject(eventStoreInjectable), - jobStore: di.inject(jobStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx deleted file mode 100644 index 479086c17c..0000000000 --- a/src/renderer/components/+workloads-jobs/metrics-details-component.injectable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { type IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { Job } from "../../../common/k8s-api/endpoints"; -import type { JobPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import jobMetricsInjectable from "./metrics.injectable"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedJobMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const JobMetricsDetailsComponent = withInjectables>(NonInjectedJobMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(jobMetricsInjectable, props.object), - ...props, - }), -}); - -const jobMetricsDetailsComponentInjectable = getInjectable({ - id: "job-metrics-details-component", - instantiate: (di) => ({ - Component: JobMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Job), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default jobMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-jobs/metrics.injectable.ts b/src/renderer/components/+workloads-jobs/metrics.injectable.ts deleted file mode 100644 index 9922dc6ad1..0000000000 --- a/src/renderer/components/+workloads-jobs/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { Job } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsForJobsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-jobs.injectable"; - -const jobMetricsInjectable = getInjectable({ - id: "job-metrics", - instantiate: (di, job) => { - const requestPodMetricsForJobs = di.inject(requestPodMetricsForJobsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: () => { - now(60 * 1000); - - return requestPodMetricsForJobs([job], job.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, job: Job) => job.getId(), - }), -}); - -export default jobMetricsInjectable; diff --git a/src/renderer/components/+workloads-jobs/store.injectable.ts b/src/renderer/components/+workloads-jobs/store.injectable.ts deleted file mode 100644 index 083b465ded..0000000000 --- a/src/renderer/components/+workloads-jobs/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import jobApiInjectable from "../../../common/k8s-api/endpoints/job.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { JobStore } from "./store"; - -const jobStoreInjectable = getInjectable({ - id: "job-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "jobStore is only available in certain environments"); - - const api = di.inject(jobApiInjectable); - - return new JobStore({ - getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default jobStoreInjectable; diff --git a/src/renderer/components/+workloads-jobs/store.ts b/src/renderer/components/+workloads-jobs/store.ts deleted file mode 100644 index 2f6f671aad..0000000000 --- a/src/renderer/components/+workloads-jobs/store.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import type { Job, JobApi } from "../../../common/k8s-api/endpoints/job.api"; -import type { CronJob, Pod } from "../../../common/k8s-api/endpoints"; -import { PodStatusPhase } from "../../../common/k8s-api/endpoints"; -import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable"; - -interface Dependencies extends KubeObjectStoreDependencies { - getPodsByOwnerId: GetPodsByOwnerId; -} - -export class JobStore extends KubeObjectStore { - constructor(protected readonly dependencies: Dependencies, api: JobApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - getChildPods(job: Job): Pod[] { - return this.dependencies.getPodsByOwnerId(job.getId()); - } - - getJobsByOwner(cronJob: CronJob) { - return this.items.filter(job => - job.getNs() == cronJob.getNs() && - job.getOwnerRefs().find(ref => ref.name === cronJob.getName() && ref.kind === cronJob.kind), - ); - } - - getStatuses(jobs?: Job[]) { - const status = { succeeded: 0, running: 0, failed: 0, pending: 0 }; - - for (const job of jobs ?? []) { - const statuses = new Set(this.getChildPods(job).map(pod => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else if (statuses.has(PodStatusPhase.RUNNING)) { - status.running++; - } else { - status.succeeded++; - } - } - - return status; - } -} diff --git a/src/renderer/components/+workloads-overview/overview-statuses.scss b/src/renderer/components/+workloads-overview/overview-statuses.scss deleted file mode 100644 index 6951c8dd9a..0000000000 --- a/src/renderer/components/+workloads-overview/overview-statuses.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.OverviewStatuses { - position: relative; - width: 100%; - min-width: $unit * 75; - background: var(--contentColor); - - .workloads { - display: grid; - grid-template-columns: repeat(auto-fit, 180px); - justify-content: space-evenly; - grid-gap: $margin; - padding: $padding * 2; - - - .workload { - margin-bottom: $margin * 2; - - > .title { - text-align: center; - - a { - color: var(--colorInfo); - } - } - } - } -} diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx deleted file mode 100644 index 19c81712a9..0000000000 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./overview-statuses.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { OverviewWorkloadStatus } from "./overview-workload-status"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { IComputedValue } from "mobx"; -import workloadsInjectable from "./workloads/workloads.injectable"; -import type { Workload } from "./workloads/workload-injection-token"; -import { formatKubeApiResource } from "../../../common/rbac"; - -export interface OverviewStatusesProps {} - -interface Dependencies { - workloads: IComputedValue; -} - -const NonInjectedOverviewStatuses = observer( - ({ workloads }: Dependencies & OverviewStatusesProps) => ( -
-
- {workloads.get().map((workload) => ( - - ))} -
-
- ), -); - -export const OverviewStatuses = withInjectables(NonInjectedOverviewStatuses, { - getProps: (di, props) => ({ - workloads: di.inject(workloadsInjectable), - ...props, - }), -}); diff --git a/src/renderer/components/+workloads-overview/overview-workload-status.scss b/src/renderer/components/+workloads-overview/overview-workload-status.scss deleted file mode 100644 index f8b2aadfd5..0000000000 --- a/src/renderer/components/+workloads-overview/overview-workload-status.scss +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.OverviewWorkloadStatus { - --workload-status-running: #{$pod-status-running-color}; - --workload-status-pending: #{$pod-status-pending-color}; - --workload-status-evicted: #{$pod-status-evicted-color}; - --workload-status-succeeded: #{$pod-status-succeeded-color}; - --workload-status-scheduled: #{$cronjob-scheduled}; - --workload-status-suspended: #{$cronjob-suspended}; - --workload-status-failed: #{$pod-status-failed-color}; - --workload-status-terminated: #{$pod-status-terminated-color}; - --workload-status-unknown: #{$pod-status-unknown-color}; -} diff --git a/src/renderer/components/+workloads-overview/overview-workload-status.tsx b/src/renderer/components/+workloads-overview/overview-workload-status.tsx deleted file mode 100644 index 41d947b326..0000000000 --- a/src/renderer/components/+workloads-overview/overview-workload-status.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./overview-workload-status.scss"; - -import React from "react"; -import capitalize from "lodash/capitalize"; -import { observer } from "mobx-react"; -import type { PieChartData } from "../chart"; -import { PieChart } from "../chart"; -import { object } from "../../utils"; -import type { LensTheme } from "../../themes/lens-theme"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { PascalCase } from "type-fest"; -import type { IComputedValue } from "mobx"; -import activeThemeInjectable from "../../themes/active.injectable"; -import type { Workload } from "./workloads/workload-injection-token"; - -export type LowercaseOrPascalCase = Lowercase | PascalCase; - -export type WorkloadStatus = Partial, number>>; - -function toLowercase(src: T): Lowercase { - return src.toLowerCase() as Lowercase; -} - -export interface OverviewWorkloadStatusProps { - workload: Workload; -} - -interface Dependencies { - activeTheme: IComputedValue; -} - -const statusBackgroundColorMapping = { - "running": "colorOk", - "scheduled": "colorOk", - "pending": "colorWarning", - "suspended": "colorWarning", - "evicted": "colorError", - "succeeded": "colorSuccess", - "failed": "colorError", - "terminated": "colorTerminated", - "terminating": "colorTerminated", - "unknown": "colorVague", - "complete": "colorSuccess", -} as const; - -const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatusProps & Dependencies) => { - const { - workload, - activeTheme, - } = props; - - const statusesToBeShown = object.entries(workload.status.get()).filter(([, val]) => val > 0); - const theme = activeTheme.get(); - - const emptyDataSet = { - data: [1], - backgroundColor: [theme.colors.pieChartDefaultColor], - label: "Empty", - }; - const statusDataSet = { - label: "Status", - data: statusesToBeShown.map(([, value]) => value), - backgroundColor: statusesToBeShown.map(([status]) => ( - theme.colors[statusBackgroundColorMapping[toLowercase(status)]] - )), - tooltipLabels: statusesToBeShown.map(([status]) => ( - (percent: string) => `${capitalize(status)}: ${percent}` - )), - }; - - const chartData: Required = { - datasets: [statusesToBeShown.length > 0 ? statusDataSet : emptyDataSet], - - labels: statusesToBeShown.map( - ([status, value]) => `${capitalize(status)}: ${value}`, - ), - }; - - return ( -
-
- -
-
- ); -}); - -export const OverviewWorkloadStatus = withInjectables(NonInjectedOverviewWorkloadStatus, { - getProps: (di, props) => ({ - ...props, - activeTheme: di.inject(activeThemeInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-overview/overview.scss b/src/renderer/components/+workloads-overview/overview.scss deleted file mode 100644 index 35910d65a1..0000000000 --- a/src/renderer/components/+workloads-overview/overview.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.WorkloadsOverview { - --flex-gap: #{$padding * 2}; - min-height: 100%; - - .header { - background: var(--contentColor); - position: relative; - padding: $padding * 2; - - h5 { - color: var(--textColorPrimary); - } - - .Icon.load-error { - color: var(--colorWarning); - } - } -} diff --git a/src/renderer/components/+workloads-overview/overview.tsx b/src/renderer/components/+workloads-overview/overview.tsx deleted file mode 100644 index 8b60676da2..0000000000 --- a/src/renderer/components/+workloads-overview/overview.tsx +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./overview.scss"; - -import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import type { DeploymentStore } from "../+workloads-deployments/store"; -import type { StatefulSetStore } from "../+workloads-statefulsets/store"; -import type { JobStore } from "../+workloads-jobs/store"; -import type { CronJobStore } from "../+workloads-cronjobs/store"; -import type { IComputedValue } from "mobx"; -import { makeObservable, observable, reaction } from "mobx"; -import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; -import { Icon } from "../icon"; -import { TooltipPosition } from "../tooltip"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import workloadOverviewDetailsInjectable from "./workload-overview-details/workload-overview-details.injectable"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import type { PodStore } from "../+workloads-pods/store"; -import type { DaemonSetStore } from "../+workloads-daemonsets/store"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import daemonSetStoreInjectable from "../+workloads-daemonsets/store.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { ReplicaSetStore } from "../+workloads-replicasets/store"; -import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable"; -import cronJobStoreInjectable from "../+workloads-cronjobs/store.injectable"; -import deploymentStoreInjectable from "../+workloads-deployments/store.injectable"; -import jobStoreInjectable from "../+workloads-jobs/store.injectable"; -import statefulSetStoreInjectable from "../+workloads-statefulsets/store.injectable"; -import type { EventStore } from "../+events/store"; -import eventStoreInjectable from "../+events/store.injectable"; -import type { ClusterContext } from "../../cluster-frame-context/cluster-frame-context"; - -interface Dependencies { - detailComponents: IComputedValue[]>; - clusterFrameContext: ClusterContext; - subscribeStores: SubscribeStores; - podStore: PodStore; - daemonSetStore: DaemonSetStore; - replicaSetStore: ReplicaSetStore; - deploymentStore: DeploymentStore; - jobStore: JobStore; - cronJobStore: CronJobStore; - statefulSetStore: StatefulSetStore; - eventStore: EventStore; -} - -@observer -class NonInjectedWorkloadsOverview extends React.Component { - @observable loadErrors: string[] = []; - - constructor(props: Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.cronJobStore, - this.props.daemonSetStore, - this.props.deploymentStore, - this.props.eventStore, - this.props.jobStore, - this.props.podStore, - this.props.replicaSetStore, - this.props.statefulSetStore, - ], { - onLoadFailure: error => this.loadErrors.push(String(error)), - }), - reaction(() => this.props.clusterFrameContext.contextNamespaces.slice(), () => { - // clear load errors - this.loadErrors.length = 0; - }), - ]); - } - - renderLoadErrors() { - if (this.loadErrors.length === 0) { - return null; - } - - return ( - - {this.loadErrors.map((error, index) =>

{error}

)} - - ), - preferredPositions: TooltipPosition.BOTTOM, - }} - /> - ); - } - - render() { - return ( - -
-
-
Overview
- {this.renderLoadErrors()} - -
- - {this.props.detailComponents.get().map((Details, index) => ( -
- ))} -
-
- ); - } -} - -export const WorkloadsOverview = withInjectables(NonInjectedWorkloadsOverview, { - getProps: (di) => ({ - detailComponents: di.inject(workloadOverviewDetailsInjectable), - clusterFrameContext: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - subscribeStores: di.inject(subscribeStoresInjectable), - daemonSetStore: di.inject(daemonSetStoreInjectable), - podStore: di.inject(podStoreInjectable), - replicaSetStore: di.inject(replicaSetStoreInjectable), - cronJobStore: di.inject(cronJobStoreInjectable), - deploymentStore: di.inject(deploymentStoreInjectable), - jobStore: di.inject(jobStoreInjectable), - statefulSetStore: di.inject(statefulSetStoreInjectable), - eventStore: di.inject(eventStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/implementations/overview-statuses.injectable.ts b/src/renderer/components/+workloads-overview/workload-overview-details/implementations/overview-statuses.injectable.ts deleted file mode 100644 index e456bce595..0000000000 --- a/src/renderer/components/+workloads-overview/workload-overview-details/implementations/overview-statuses.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { workloadOverviewDetailInjectionToken } from "../workload-overview-detail-injection-token"; -import { OverviewStatuses } from "../../overview-statuses"; -import { computed } from "mobx"; - -const overviewStatusesInjectable = getInjectable({ - id: "overview-statuses", - - instantiate: () => ({ - Component: OverviewStatuses, - enabled: computed(() => true), - orderNumber: 50, - }), - - injectionToken: workloadOverviewDetailInjectionToken, -}); - -export default overviewStatusesInjectable; diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/implementations/workload-events.injectable.ts b/src/renderer/components/+workloads-overview/workload-overview-details/implementations/workload-events.injectable.ts deleted file mode 100644 index 7c76d2b829..0000000000 --- a/src/renderer/components/+workloads-overview/workload-overview-details/implementations/workload-events.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { workloadOverviewDetailInjectionToken } from "../workload-overview-detail-injection-token"; -import { computed } from "mobx"; -import { WorkloadEvents } from "../../../../initializers/workload-events"; - -const workloadEventsInjectable = getInjectable({ - id: "workload-events", - - instantiate: () => ({ - Component: WorkloadEvents, - enabled: computed(() => true), - orderNumber: 300, - }), - - injectionToken: workloadOverviewDetailInjectionToken, -}); - -export default workloadEventsInjectable; diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-injection-token.ts b/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-injection-token.ts deleted file mode 100644 index 746b815fd2..0000000000 --- a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-injection-token.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import type React from "react"; - -interface WorkloadOverviewDetail { - orderNumber: number; - Component: React.ElementType<{}>; - enabled: IComputedValue; -} - -export const workloadOverviewDetailInjectionToken = - getInjectionToken({ - id: "workload-overview-detail-injection-token", - }); diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts b/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts deleted file mode 100644 index e82b1f43e5..0000000000 --- a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * 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 getRandomIdInjectable from "../../../../common/utils/get-random-id.injectable"; -import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; -import extensionShouldBeEnabledForClusterFrameInjectable from "../../../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; -import { workloadOverviewDetailInjectionToken } from "./workload-overview-detail-injection-token"; -import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token"; - -const workloadOverviewDetailRegistratorInjectable = getInjectable({ - id: "workload-overview-detail-registrator", - - instantiate: (di) => { - const getRandomId = di.inject(getRandomIdInjectable); - - const getExtensionShouldBeEnabledForClusterFrame = ( - extension: LensRendererExtension, - ) => - di.inject(extensionShouldBeEnabledForClusterFrameInjectable, extension); - - return (ext) => { - const extension = ext as LensRendererExtension; - - const extensionShouldBeEnabledForClusterFrame = - getExtensionShouldBeEnabledForClusterFrame(extension); - - return extension.kubeWorkloadsOverviewItems.map((registration) => { - const id = `workload-overview-detail-from-${ - extension.sanitizedExtensionId - }-${getRandomId()}`; - - return getInjectable({ - id, - - instantiate: () => ({ - Component: registration.components.Details, - - enabled: computed(() => { - if (!extensionShouldBeEnabledForClusterFrame.value.get()) { - return false; - } - - return registration.visible ? registration.visible.get() : true; - }), - - orderNumber: - 0.5 + (registration.priority ? 100 - registration.priority : 50), - }), - - injectionToken: workloadOverviewDetailInjectionToken, - }); - }); - }; - }, - - injectionToken: extensionRegistratorInjectionToken, -}); - -export default workloadOverviewDetailRegistratorInjectable; diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-details.injectable.ts b/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-details.injectable.ts deleted file mode 100644 index 205dddec0a..0000000000 --- a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-details.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 { filter, map, sortBy } from "lodash/fp"; -import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; -import { workloadOverviewDetailInjectionToken } from "./workload-overview-detail-injection-token"; -import { pipeline } from "@ogre-tools/fp"; - -const workloadOverviewDetailsInjectable = getInjectable({ - id: "workload-overview-details", - - instantiate: (di) => { - const computedInjectMany = di.inject(computedInjectManyInjectable); - const details = computedInjectMany(workloadOverviewDetailInjectionToken); - - return computed(() => - pipeline( - details.get(), - filter((detail) => detail.enabled.get()), - sortBy((detail) => detail.orderNumber), - map((detail) => detail.Component), - ), - ); - }, -}); - -export default workloadOverviewDetailsInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts b/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts deleted file mode 100644 index ab23619b95..0000000000 --- a/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { IComputedValue } from "mobx"; - -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -interface WorkloadsOverviewDetailComponents { - Details: React.ComponentType<{}>; -} - -export interface WorkloadsOverviewDetailRegistration { - components: WorkloadsOverviewDetailComponents; - priority?: number; - visible?: IComputedValue; -} diff --git a/src/renderer/components/+workloads-overview/workloads-overview-route-component.injectable.ts b/src/renderer/components/+workloads-overview/workloads-overview-route-component.injectable.ts deleted file mode 100644 index 59a2a6d7ae..0000000000 --- a/src/renderer/components/+workloads-overview/workloads-overview-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { WorkloadsOverview } from "./overview"; -import workloadsOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const workloadsOverviewRouteComponentInjectable = getInjectable({ - id: "workloads-overview-route-component", - - instantiate: (di) => ({ - route: di.inject(workloadsOverviewRouteInjectable), - Component: WorkloadsOverview, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default workloadsOverviewRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads-overview-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-overview/workloads-overview-sidebar-items.injectable.tsx deleted file mode 100644 index c82c1e6e75..0000000000 --- a/src/renderer/components/+workloads-overview/workloads-overview-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 workloadsOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToWorkloadsOverviewInjectable from "../../../common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable"; - -const workloadsOverviewSidebarItemsInjectable = getInjectable({ - id: "workloads-overview-sidebar-items", - - instantiate: (di) => { - const route = di.inject(workloadsOverviewRouteInjectable); - const navigateToWorkloadsOverview = di.inject(navigateToWorkloadsOverviewInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "overview", - parentId: workloadsSidebarItemId, - title: "Overview", - onClick: navigateToWorkloadsOverview, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 10, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default workloadsOverviewSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/cron-jobs-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/cron-jobs-workload.injectable.ts deleted file mode 100644 index 7e8ab5cf6e..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/cron-jobs-workload.injectable.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import navigateToCronJobsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/cron-jobs/navigate-to-cron-jobs.injectable"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import cronJobsStoreInjectable from "../../../+workloads-cronjobs/store.injectable"; -import { computed } from "mobx"; - -const cronJobsWorkloadInjectable = getInjectable({ - id: "cron-jobs-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToCronJobsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(cronJobsStoreInjectable); - - return { - resource: { - apiName: "cronjobs", - group: "batch", - }, - open: navigate, - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - title: ResourceNames.cronjobs, - orderNumber: 70, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default cronJobsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/daemonsets-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/daemonsets-workload.injectable.ts deleted file mode 100644 index 7b6d7d750f..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/daemonsets-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import daemonsetsStoreInjectable from "../../../+workloads-daemonsets/store.injectable"; -import navigateToDaemonsetsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable"; -import { computed } from "mobx"; - -const daemonsetsWorkloadInjectable = getInjectable({ - id: "daemonsets-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToDaemonsetsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(daemonsetsStoreInjectable); - - return { - resource: { - apiName: "daemonsets", - group: "apps", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.daemonsets, - orderNumber: 30, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default daemonsetsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/deployments-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/deployments-workload.injectable.ts deleted file mode 100644 index b41e4ea5ca..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/deployments-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import deploymentsStoreInjectable from "../../../+workloads-deployments/store.injectable"; -import navigateToDeploymentsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable"; -import { computed } from "mobx"; - -const deploymentsWorkloadInjectable = getInjectable({ - id: "deployments-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToDeploymentsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(deploymentsStoreInjectable); - - return { - resource: { - apiName: "deployments", - group: "apps", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.deployments, - orderNumber: 20, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default deploymentsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/jobs-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/jobs-workload.injectable.ts deleted file mode 100644 index e68fd88c1c..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/jobs-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import jobStoreInjectable from "../../../+workloads-jobs/store.injectable"; -import navigateToJobsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable"; -import { computed } from "mobx"; - -const jobsWorkloadInjectable = getInjectable({ - id: "jobs-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToJobsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(jobStoreInjectable); - - return { - resource: { - apiName: "jobs", - group: "batch", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.jobs, - orderNumber: 60, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default jobsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/pods-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/pods-workload.injectable.ts deleted file mode 100644 index 246469b9e7..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/pods-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import navigateToPodsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import { computed } from "mobx"; -import podStoreInjectable from "../../../+workloads-pods/store.injectable"; - -const podsWorkloadInjectable = getInjectable({ - id: "pods-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToPodsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(podStoreInjectable); - - return { - resource: { - apiName: "pods", - group: "", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.pods, - orderNumber: 10, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default podsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/replicasets-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/replicasets-workload.injectable.ts deleted file mode 100644 index 86189e4634..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/replicasets-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import replicasetsStoreInjectable from "../../../+workloads-replicasets/store.injectable"; -import { computed } from "mobx"; -import navigateToReplicasetsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/replicasets/navigate-to-replicasets.injectable"; - -const replicasetsWorkloadInjectable = getInjectable({ - id: "replicasets-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToReplicasetsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(replicasetsStoreInjectable); - - return { - resource: { - apiName: "replicasets", - group: "apps", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.replicasets, - orderNumber: 50, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default replicasetsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/implementations/statefulsets-workload.injectable.ts b/src/renderer/components/+workloads-overview/workloads/implementations/statefulsets-workload.injectable.ts deleted file mode 100644 index 19b85c4fa0..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/implementations/statefulsets-workload.injectable.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { workloadInjectionToken } from "../workload-injection-token"; -import { ResourceNames } from "../../../../utils/rbac"; -import namespaceStoreInjectable from "../../../+namespaces/store.injectable"; -import statefulsetsStoreInjectable from "../../../+workloads-statefulsets/store.injectable"; -import { computed } from "mobx"; -import navigateToStatefulsetsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable"; - -const statefulsetsWorkloadInjectable = getInjectable({ - id: "statefulsets-workload", - - instantiate: (di) => { - const navigate = di.inject(navigateToStatefulsetsInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const store = di.inject(statefulsetsStoreInjectable); - - return { - resource: { - apiName: "statefulsets", - group: "apps", - }, - open: navigate, - - amountOfItems: computed( - () => store.getAllByNs(namespaceStore.contextNamespaces).length, - ), - - status: computed(() => - store.getStatuses(store.getAllByNs(namespaceStore.contextNamespaces)), - ), - - title: ResourceNames.statefulsets, - orderNumber: 40, - }; - }, - - injectionToken: workloadInjectionToken, -}); - -export default statefulsetsWorkloadInjectable; diff --git a/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts b/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts deleted file mode 100644 index fca19148a7..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; -import type { KubeApiResourceDescriptor } from "../../../../common/rbac"; -import type { WorkloadStatus } from "../overview-workload-status"; - -export interface Workload { - resource: KubeApiResourceDescriptor; - open: () => void; - amountOfItems: IComputedValue; - status: IComputedValue; - title: string; - orderNumber: number; -} - -export const workloadInjectionToken = getInjectionToken({ - id: "workload-injection-token", -}); diff --git a/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts b/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts deleted file mode 100644 index d6b23b582f..0000000000 --- a/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 { shouldShowResourceInjectionToken } from "../../../../common/cluster-store/allowed-resources-injection-token"; -import { byOrderNumber } from "../../../../common/utils/composable-responsibilities/orderable/orderable"; -import { workloadInjectionToken } from "./workload-injection-token"; - -const workloadsInjectable = getInjectable({ - id: "workloads", - - instantiate: (di) => { - const workloads = di.injectMany(workloadInjectionToken); - - return computed(() => ( - workloads - .filter(w => di.inject(shouldShowResourceInjectionToken, w.resource).get()) - .sort(byOrderNumber) - )); - }, -}); - -export default workloadsInjectable; diff --git a/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap b/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap deleted file mode 100644 index 1183ce4309..0000000000 --- a/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap +++ /dev/null @@ -1,182 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` renders both env and configMapRef envFrom 1`] = ` - -
-
- - Environment - - -
- - foobar - - : - https://localhost:12345 -
-
- - configFoo - - : - configBar -
-
-
-
- -`; - -exports[` renders env 1`] = ` - -
-
- - Environment - - -
- - foobar - - : - https://localhost:12345 -
-
-
-
- -`; - -exports[` renders env 2`] = ` - -
-
- - Environment - - -
- - foobar - - : - https://localhost:12345 -
-
-
-
- -`; - -exports[` renders envFrom when given a configMapRef 1`] = ` - -
-
- - Environment - - -
- - configFoo - - : - configBar -
-
-
-
- -`; - -exports[` renders envFrom when given a secretRef 1`] = ` - -
-
- - Environment - - -
- - bar - - : - secretKeyRef(my-secret.bar) -   - - - visibility - - -
- Show -
-
-
-
-
- -`; diff --git a/src/renderer/components/+workloads-pods/__tests__/pod-container-env.test.tsx b/src/renderer/components/+workloads-pods/__tests__/pod-container-env.test.tsx deleted file mode 100644 index bb85aee1ec..0000000000 --- a/src/renderer/components/+workloads-pods/__tests__/pod-container-env.test.tsx +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import type { ConfigMapStore } from "../../+config-maps/store"; -import configMapStoreInjectable from "../../+config-maps/store.injectable"; -import type { SecretStore } from "../../+config-secrets/store"; -import secretStoreInjectable from "../../+config-secrets/store.injectable"; -import type { Container } from "../../../../common/k8s-api/endpoints"; -import { Secret, ConfigMap, Pod, SecretType } from "../../../../common/k8s-api/endpoints"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DiRender } from "../../test-utils/renderFor"; -import { renderFor } from "../../test-utils/renderFor"; -import { ContainerEnvironment } from "../pod-container-env"; - -describe("", () => { - let render: DiRender; - let secretStore: jest.Mocked>; - let configMapStore: jest.Mocked>; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - secretStore = ({ - load: jest.fn().mockImplementation(async () => { - return {} as Secret; - }), - getByName: jest.fn(), - }); - configMapStore = ({ - load: jest.fn().mockImplementation(async () => { - return {} as ConfigMap; - }), - getByName: jest.fn(), - }); - - di.override(secretStoreInjectable, () => secretStore as jest.Mocked); - di.override(configMapStoreInjectable, () => configMapStore as jest.Mocked); - - render = renderFor(di); - }); - - it("renders env", () => { - const container: Container = { - image: "my-image", - name: "my-first-container", - env: [{ - name: "foobar", - value: "https://localhost:12345", - }], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - selfLink: "/api/v1/pods/default/my-pod", - uid: "1234", - }, - spec: { - containers: [container], - }, - }); - const result = render(); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders envFrom when given a configMapRef", () => { - configMapStore.getByName.mockImplementation((name, namespace) => { - expect(name).toBe("my-config-map"); - expect(namespace).toBe("default"); - - return new ConfigMap({ - apiVersion: "v1", - kind: "ConfigMap", - metadata: { - name: "my-config-map", - namespace: "default", - resourceVersion: "2", - selfLink: "/api/v1/configmaps/default/my-config-map", - uid: "456", - }, - data: { - configFoo: "configBar", - }, - }); - }); - - const container: Container = { - image: "my-image", - name: "my-first-container", - envFrom: [{ - configMapRef: { - name: "my-config-map", - }, - }], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - selfLink: "/api/v1/pods/default/my-pod", - uid: "1234", - }, - spec: { - containers: [container], - }, - }); - const result = render(); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders envFrom when given a secretRef", () => { - secretStore.getByName.mockImplementation((name, namespace) => { - expect(name).toBe("my-secret"); - expect(namespace).toBe("default"); - - return new Secret({ - apiVersion: "v1", - kind: "Secret", - metadata: { - name: "my-secret", - namespace: "default", - resourceVersion: "3", - selfLink: "/api/v1/secrets/default/my-secret", - uid: "237", - }, - type: SecretType.BasicAuth, - data: { - bar: "bat", - }, - }); - }); - - const container: Container = { - image: "my-image", - name: "my-first-container", - envFrom: [{ - secretRef: { - name: "my-secret", - }, - }], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - selfLink: "/api/v1/pods/default/my-pod", - uid: "1234", - }, - spec: { - containers: [container], - }, - }); - const result = render(); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders env", () => { - const container: Container = { - image: "my-image", - name: "my-first-container", - env: [{ - name: "foobar", - value: "https://localhost:12345", - }], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - selfLink: "/api/v1/pods/default/my-pod", - uid: "1234", - }, - spec: { - containers: [container], - }, - }); - const result = render(); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders both env and configMapRef envFrom", () => { - configMapStore.getByName.mockImplementation((name, namespace) => { - expect(name).toBe("my-config-map"); - expect(namespace).toBe("default"); - - return new ConfigMap({ - apiVersion: "v1", - kind: "ConfigMap", - metadata: { - name: "my-config-map", - namespace: "default", - resourceVersion: "2", - selfLink: "/api/v1/configmaps/default/my-config-map", - uid: "456", - }, - data: { - configFoo: "configBar", - }, - }); - }); - - const container: Container = { - image: "my-image", - name: "my-first-container", - envFrom: [{ - configMapRef: { - name: "my-config-map", - }, - }], - env: [{ - name: "foobar", - value: "https://localhost:12345", - }], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - selfLink: "/api/v1/pods/default/my-pod", - uid: "1234", - }, - spec: { - containers: [container], - }, - }); - const result = render(); - - expect(result.baseElement).toMatchSnapshot(); - }); -}); diff --git a/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx b/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx deleted file mode 100644 index 9ddf3080ba..0000000000 --- a/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import "@testing-library/jest-dom/extend-expect"; -import { fireEvent } from "@testing-library/react"; -import type { Toleration } from "../../../../common/k8s-api/kube-object"; -import { PodTolerations } from "../pod-tolerations"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DiRender } from "../../test-utils/renderFor"; -import { renderFor } from "../../test-utils/renderFor"; -import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; - -const tolerations: Toleration[] =[ - { - key: "CriticalAddonsOnly", - operator: "Exist", - effect: "NoExecute", - tolerationSeconds: 3600, - }, - { - key: "node.kubernetes.io/not-ready", - operator: "NoExist", - effect: "NoSchedule", - tolerationSeconds: 7200, - }, -]; - -describe("", () => { - let render: DiRender; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForLensLocalStorageInjectable, () => "some-directory-for-lens-local-storage" ); - - render = renderFor(di); - }); - - it("renders w/o errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("shows all tolerations", () => { - const { container } = render(); - const rows = container.querySelectorAll(".TableRow"); - - expect(rows[0].querySelector(".key")?.textContent).toBe("CriticalAddonsOnly"); - expect(rows[0].querySelector(".operator")?.textContent).toBe("Exist"); - expect(rows[0].querySelector(".effect")?.textContent).toBe("NoExecute"); - expect(rows[0].querySelector(".seconds")?.textContent).toBe("3600"); - - expect(rows[1].querySelector(".key")?.textContent).toBe("node.kubernetes.io/not-ready"); - expect(rows[1].querySelector(".operator")?.textContent).toBe("NoExist"); - expect(rows[1].querySelector(".effect")?.textContent).toBe("NoSchedule"); - expect(rows[1].querySelector(".seconds")?.textContent).toBe("7200"); - }); - - it("sorts table properly", () => { - const { container, getByText } = render(); - const headCell = getByText("Key"); - - fireEvent.click(headCell); - fireEvent.click(headCell); - - const rows = container.querySelectorAll(".TableRow"); - - expect(rows[0].querySelector(".key")?.textContent).toBe("node.kubernetes.io/not-ready"); - }); -}); diff --git a/src/renderer/components/+workloads-pods/container-charts.tsx b/src/renderer/components/+workloads-pods/container-charts.tsx deleted file mode 100644 index 5c77f22389..0000000000 --- a/src/renderer/components/+workloads-pods/container-charts.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React, { useContext } from "react"; -import { observer } from "mobx-react"; -import type { ChartDataSets } from "../chart"; -import { BarChart } from "../chart"; -import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; -import { NoMetrics } from "../resource-metrics/no-metrics"; -import { ResourceMetricsContext } from "../resource-metrics"; -import type { LensTheme } from "../../themes/lens-theme"; -import { mapValues } from "lodash"; -import { type MetricsTab, metricTabOptions } from "../chart/options"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import activeThemeInjectable from "../../themes/active.injectable"; -import type { IComputedValue } from "mobx"; - -export interface ContainerChartsProps {} - -interface Dependencies { - activeTheme: IComputedValue; -} - -const NonInjectedContainerCharts = observer(({ - activeTheme, -}: Dependencies & ContainerChartsProps) => { - const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {}; - - if (!metrics || !object || !tab) return null; - if (isMetricsEmpty(metrics)) return ; - - const { chartCapacityColor } = activeTheme.get().colors; - const { - cpuUsage, - cpuRequests, - cpuLimits, - memoryUsage, - memoryRequests, - memoryLimits, - fsUsage, - fsWrites, - fsReads, - } = mapValues(metrics, metric => normalizeMetrics(metric).data.result[0].values); - - const datasets: Partial> = { - CPU: [ - { - id: "cpuUsage", - label: `Usage`, - tooltip: `CPU cores usage`, - borderColor: "#3D90CE", - data: cpuUsage.map(([x, y]) => ({ x, y })), - }, - { - id: "cpuRequests", - label: `Requests`, - tooltip: `CPU requests`, - borderColor: "#30b24d", - data: cpuRequests.map(([x, y]) => ({ x, y })), - }, - { - id: "cpuLimits", - label: `Limits`, - tooltip: `CPU limits`, - borderColor: chartCapacityColor, - data: cpuLimits.map(([x, y]) => ({ x, y })), - }, - ], - Memory: [ - { - id: "memoryUsage", - label: `Usage`, - tooltip: `Memory usage`, - borderColor: "#c93dce", - data: memoryUsage.map(([x, y]) => ({ x, y })), - }, - { - id: "memoryRequests", - label: `Requests`, - tooltip: `Memory requests`, - borderColor: "#30b24d", - data: memoryRequests.map(([x, y]) => ({ x, y })), - }, - { - id: "memoryLimits", - label: `Limits`, - tooltip: `Memory limits`, - borderColor: chartCapacityColor, - data: memoryLimits.map(([x, y]) => ({ x, y })), - }, - ], - Filesystem: [ - { - id: "fsUsage", - label: `Usage`, - tooltip: `Bytes consumed on this filesystem`, - borderColor: "#ffc63d", - data: fsUsage.map(([x, y]) => ({ x, y })), - }, - { - id: "fsWrites", - label: `Writes`, - tooltip: `Bytes written on this filesystem`, - borderColor: "#ff963d", - data: fsWrites.map(([x, y]) => ({ x, y })), - }, - { - id: "fsReads", - label: `Reads`, - tooltip: `Bytes read on this filesystem`, - borderColor: "#fff73d", - data: fsReads.map(([x, y]) => ({ x, y })), - }, - ], - }; - - return ( - - ); -}); - -export const ContainerCharts = withInjectables(NonInjectedContainerCharts, { - getProps: (di, props) => ({ - ...props, - activeTheme: di.inject(activeThemeInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/container-metrics.injectable.ts b/src/renderer/components/+workloads-pods/container-metrics.injectable.ts deleted file mode 100644 index dccd1668ef..0000000000 --- a/src/renderer/components/+workloads-pods/container-metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { Pod } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; - -const podContainerMetricsInjectable = getInjectable({ - id: "pod-container-metrics", - instantiate: (di, pod) => { - const requestPodMetrics = di.inject(requestPodMetricsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: () => { - now(60 * 1000); - - return requestPodMetrics([pod], pod.getNs(), "container, namespace"); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, pod: Pod) => pod.getId(), - }), -}); - -export default podContainerMetricsInjectable; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx b/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx deleted file mode 100644 index b0f0662da3..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variant-helpers.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { Link } from "react-router-dom"; -import type { PodVolumeVariants, Pod, SecretReference } from "../../../../../common/k8s-api/endpoints"; -import type { KubeApiQueryParams, ResourceDescriptor } from "../../../../../common/k8s-api/kube-api"; -import type { LocalObjectReference } from "../../../../../common/k8s-api/kube-object"; -import { DrawerItem } from "../../../drawer"; -import type { GetDetailsUrl } from "../../../kube-detail-params/get-details-url.injectable"; -import getDetailsUrlInjectable from "../../../kube-detail-params/get-details-url.injectable"; - -export interface PodVolumeVariantSpecificProps { - variant: PodVolumeVariants[Kind]; - pod: Pod; - volumeName: string; -} - -export type VolumeVariantComponent = React.FunctionComponent>; - -export interface LocalRefPropsApi { - getUrl(desc?: Partial, query?: Partial): string; -} - -export interface LocalRefProps { - pod: Pod; - title: string; - kubeRef: LocalObjectReference | SecretReference | undefined; - api: LocalRefPropsApi; -} - -interface Dependencies { - getDetailsUrl: GetDetailsUrl; -} - -const NonInjectedLocalRef = (props: LocalRefProps & Dependencies) => { - const { - pod, - title, - kubeRef, - api, - getDetailsUrl, - } = props; - - if (!kubeRef) { - return null; - } - - return ( - - - {kubeRef.name} - - - ); -}; - -export const LocalRef = withInjectables(NonInjectedLocalRef, { - getProps: (di, props) => ({ - ...props, - getDetailsUrl: di.inject(getDetailsUrlInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variant.tsx b/src/renderer/components/+workloads-pods/details/volumes/variant.tsx deleted file mode 100644 index d952530bb7..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variant.tsx +++ /dev/null @@ -1,411 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import type { Pod, PodSpecVolume, PodVolumeKind } from "../../../../../common/k8s-api/endpoints"; -import { DrawerItem } from "../../../drawer"; -import { Icon } from "../../../icon"; -import { AwsElasticBlockStore } from "./variants/aws-elastic-block-store"; -import { AzureDisk } from "./variants/azure-disk"; -import { AzureFile } from "./variants/azure-file"; -import { CephFs } from "./variants/ceph-fs"; -import { Cinder } from "./variants/cinder"; -import { ConfigMap } from "./variants/config-map"; -import { ContainerStorageInterface } from "./variants/container-storage-interface"; -import { DownwardAPI } from "./variants/downward-api"; -import { EmptyDir } from "./variants/empty-dir"; -import { Ephemeral } from "./variants/ephemeral"; -import { FiberChannel } from "./variants/fiber-channel"; -import { FlexVolume } from "./variants/flex-volume"; -import { Flocker } from "./variants/flocker"; -import { GcePersistentDisk } from "./variants/gce-persistent-disk"; -import { GitRepo } from "./variants/git-repo"; -import { GlusterFs } from "./variants/gluster-fs"; -import { HostPath } from "./variants/host-path"; -import { IScsi } from "./variants/i-scsi"; -import { Local } from "./variants/local"; -import { NetworkFs } from "./variants/network-fs"; -import { PersistentVolumeClaim } from "./variants/persistent-volume-claim"; -import { PhotonPersistentDisk } from "./variants/photon-persistent-disk"; -import { PortworxVolume } from "./variants/portworx-volume"; -import { Projected } from "./variants/projected"; -import { Quobyte } from "./variants/quobyte"; -import { RadosBlockDevice } from "./variants/rados-block-device"; -import { ScaleIo } from "./variants/scale-io"; -import { Secret } from "./variants/secret"; -import { StorageOs } from "./variants/storage-os"; -import { VsphereVolume } from "./variants/vsphere-volume"; - -const deprecatedVolumeTypes = new Set([ - "flocker", - "gitRepo", - "quobyte", - "storageos", -]); - -interface VolumeVariantProps { - pod: Pod; - volume: PodSpecVolume; -} - -interface VolumeVariantRender { - kind: PodVolumeKind; - element: JSX.Element; -} - -function renderVolumeVariant({ pod, volume }: VolumeVariantProps): VolumeVariantRender | null { - if (volume.awsElasticBlockStore) { - return { - kind: "awsElasticBlockStore", - element: , - }; - } - - if (volume.azureDisk) { - return { - kind: "azureDisk", - element: , - }; - } - - if (volume.azureFile) { - return { - kind: "azureFile", - element: , - }; - } - - if (volume.cephfs) { - return { - kind: "cephfs", - element: , - }; - } - - if (volume.cinder) { - return { - kind: "cinder", - element: , - }; - } - - if (volume.configMap) { - return { - kind: "configMap", - element: , - }; - } - - if (volume.csi) { - return { - kind: "csi", - element: , - }; - } - - if (volume.downwardAPI) { - return { - kind: "downwardAPI", - element: , - }; - } - - if (volume.emptyDir) { - return { - kind: "emptyDir", - element: , - }; - } - - if (volume.ephemeral) { - return { - kind: "ephemeral", - element: , - }; - } - - if (volume.fc) { - return { - kind: "fc", - element: , - }; - } - - if (volume.flexVolume) { - return { - kind: "flexVolume", - element: , - }; - } - - if (volume.flocker) { - return { - kind: "flocker", - element: , - }; - } - - if (volume.gcePersistentDisk) { - return { - kind: "gcePersistentDisk", - element: , - }; - } - - if (volume.gitRepo) { - return { - kind: "gitRepo", - element: , - }; - } - - if (volume.glusterfs) { - return { - kind: "glusterfs", - element: , - }; - } - - if (volume.hostPath) { - return { - kind: "hostPath", - element: , - }; - } - - if (volume.iscsi) { - return { - kind: "iscsi", - element: , - }; - } - - if (volume.local) { - return { - kind: "local", - element: , - }; - } - - if (volume.nfs) { - return { - kind: "nfs", - element: , - }; - } - - if (volume.persistentVolumeClaim) { - return { - kind: "persistentVolumeClaim", - element: , - }; - } - - if (volume.photonPersistentDisk) { - return { - kind: "photonPersistentDisk", - element: , - }; - } - - if (volume.portworxVolume) { - return { - kind: "portworxVolume", - element: , - }; - } - - if (volume.projected) { - return { - kind: "projected", - element: , - }; - } - - if (volume.quobyte) { - return { - kind: "quobyte", - element: , - }; - } - - if (volume.rbd) { - return { - kind: "rbd", - element: , - }; - } - - if (volume.scaleIO) { - return { - kind: "scaleIO", - element: , - }; - } - - if (volume.secret) { - return { - kind: "secret", - element: , - }; - } - - if (volume.storageos) { - return { - kind: "storageos", - element: , - }; - } - - if (volume.vsphereVolume) { - return { - kind: "vsphereVolume", - element: , - }; - } - - return null; -} - -export function VolumeVariant(props: VolumeVariantProps) { - const result = renderVolumeVariant(props); - - if (!result) { - return

Error! Unknown pod volume kind

; - } - - const { kind, element } = result; - const isDeprecated = deprecatedVolumeTypes.has(kind); - - return ( - <> - - {kind} - {isDeprecated && } - - {element} - - ); -} diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap b/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap deleted file mode 100644 index ef0d3dd261..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap +++ /dev/null @@ -1,229 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` renders 1`] = ` - -
-
- - Sources - - -
-
- -`; - -exports[` renders a secret source including overriding mode 1`] = ` - -
-
- - Default Mount Mode - - - 0o777 - -
-
- - Sources - - -
- Secret -
-
- - Name - - - my-projected-secret - -
-
- - Items - - -
    -
  • - foo⇢/bar - (0o666) -
  • -
-
-
-
-
-
- -`; - -exports[` renders a secret source, when provided 1`] = ` - -
-
- - Default Mount Mode - - - 0o777 - -
-
- - Sources - - -
- Secret -
-
- - Name - - - my-projected-secret - -
-
- - Items - - -
    -
  • - foo⇢/bar -
  • -
-
-
-
-
-
- -`; - -exports[` renders default mount mode in octal when provided 1`] = ` - -
-
- - Default Mount Mode - - - 0o777 - -
-
- - Sources - - -
-
- -`; - -exports[` renders when no sources array provided 1`] = ` - -
-
- - Default Mount Mode - - - 0o777 - -
-
- - Sources - - -
-
- -`; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap deleted file mode 100644 index 627b2fb1a3..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap +++ /dev/null @@ -1,229 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should render 'false' for Readonly when false is provided 1`] = ` -
-
- - Monitors - - -
    - -
-
- - Mount Path - - - / - -
-
- - Username - - - admin - -
-
- - Secret Filepath - - - /etc/ceph/user.secret - -
-
- - Readonly - - - false - -
-
-`; - -exports[` should render 'false' for Readonly when not provided 1`] = ` -
-
- - Monitors - - -
    - -
-
- - Mount Path - - - / - -
-
- - Username - - - admin - -
-
- - Secret Filepath - - - /etc/ceph/user.secret - -
-
- - Readonly - - - false - -
-
-`; - -exports[` should render 'true' for Readonly when true is provided 1`] = ` -
-
- - Monitors - - -
    - -
-
- - Mount Path - - - / - -
-
- - Username - - - admin - -
-
- - Secret Filepath - - - /etc/ceph/user.secret - -
-
- - Readonly - - - true - -
-
-`; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx deleted file mode 100644 index 0345050a77..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import type { CephfsSource } from "../../../../../../../common/k8s-api/endpoints"; -import { Pod } from "../../../../../../../common/k8s-api/endpoints"; -import { getDiForUnitTesting } from "../../../../../../getDiForUnitTesting"; -import storesAndApisCanBeCreatedInjectable from "../../../../../../stores-apis-can-be-created.injectable"; -import type { DiRender } from "../../../../../test-utils/renderFor"; -import { renderFor } from "../../../../../test-utils/renderFor"; -import { CephFs } from "../ceph-fs"; - -describe("", () => { - let render: DiRender; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - render = renderFor(di); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - }); - - it("should render 'false' for Readonly when not provided", () => { - const cephfsName = "my-ceph"; - const cephfsVolume: CephfsSource = { - monitors: [], - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: cephfsName, - cephfs: cephfsVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.container).toMatchSnapshot(); - expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("false"); - }); - - it("should render 'false' for Readonly when false is provided", () => { - const cephfsName = "my-ceph"; - const cephfsVolume: CephfsSource = { - monitors: [], - readOnly: false, - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: cephfsName, - cephfs: cephfsVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.container).toMatchSnapshot(); - expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("false"); - }); - - it("should render 'true' for Readonly when true is provided", () => { - const cephfsName = "my-ceph"; - const cephfsVolume: CephfsSource = { - monitors: [], - readOnly: true, - }; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: cephfsName, - cephfs: cephfsVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.container).toMatchSnapshot(); - expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("true"); - }); -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/aws-elastic-block-store.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/aws-elastic-block-store.tsx deleted file mode 100644 index 285a0fa12e..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/aws-elastic-block-store.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const AwsElasticBlockStore: VolumeVariantComponent<"awsElasticBlockStore"> = ( - ({ variant: { volumeID, fsType = "ext4" }}) => ( - <> - - {volumeID} - - - {fsType} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/azure-disk.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/azure-disk.tsx deleted file mode 100644 index 0e19b41843..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/azure-disk.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const AzureDisk: VolumeVariantComponent<"azureDisk"> = ( - ({ variant: { diskName, diskURI, kind = "Shared", cachingMode = "None", fsType = "ext4", readonly = false }}) => ( - <> - - {diskName} - - - {diskURI} - - - {kind} - - - {cachingMode} - - - {fsType} - - - {readonly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/azure-file.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/azure-file.tsx deleted file mode 100644 index c73fd42c02..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/azure-file.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const AzureFile: VolumeVariantComponent<"azureFile"> = ( - ({ variant: { readOnly = false, secretName, shareName, secretNamespace = "default" }}) => ( - <> - - {secretName} - - - {shareName} - - - {secretNamespace} - - - {readOnly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx deleted file mode 100644 index 64b68436a3..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedCephFs = (props: PodVolumeVariantSpecificProps<"cephfs"> & Dependencies) => { - const { - pod, - variant: { - monitors, - path = "/", - user = "admin", - secretFile = "/etc/ceph/user.secret", - secretRef, - readOnly = false, - }, - secretApi, - } = props; - - return ( - <> - -
    - {monitors.map(monitor =>
  • {monitor}
  • )} -
-
- - {path} - - - {user} - - { - secretRef - ? ( - - ) - : ( - - {secretFile} - - ) - } - - {readOnly.toString()} - - - ); -}; - -export const CephFs = withInjectables>(NonInjectedCephFs, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/cinder.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/cinder.tsx deleted file mode 100644 index 366438e0d8..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/cinder.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Cinder: VolumeVariantComponent<"cinder"> = ( - ({ variant: { volumeID, fsType = "ext4" }}) => ( - <> - - {volumeID} - - - {fsType} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx deleted file mode 100644 index e59fed5633..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/config-map.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { ConfigMapApi } from "../../../../../../common/k8s-api/endpoints"; -import configMapApiInjectable from "../../../../../../common/k8s-api/endpoints/config-map.api.injectable"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - configMapApi: ConfigMapApi; -} - -const NonInjectedConfigMap = (props: PodVolumeVariantSpecificProps<"configMap"> & Dependencies) => { - const { - pod, - variant: { name }, - configMapApi, - } = props; - - return ( - - ); -}; - -export const ConfigMap = withInjectables>(NonInjectedConfigMap, { - getProps: (di, props) => ({ - ...props, - configMapApi: di.inject(configMapApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx deleted file mode 100644 index 78576a8ed5..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/container-storage-interface.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedContainerStorageInterface = (props: PodVolumeVariantSpecificProps<"csi"> & Dependencies) => { - const { - pod, - variant: { - driver, - readOnly = false, - fsType = "ext4", - volumeAttributes = {}, - nodePublishSecretRef, - controllerPublishSecretRef, - nodeStageSecretRef, - controllerExpandSecretRef, - }, - secretApi, - } = props; - - return ( - <> - - {driver} - - - {readOnly.toString()} - - - {fsType} - - - - - - {Object.entries(volumeAttributes) - .map(([key, value]) => ( - - {value} - - ))} - - ); -}; - -export const ContainerStorageInterface = withInjectables>(NonInjectedContainerStorageInterface, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/downward-api.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/downward-api.tsx deleted file mode 100644 index c6c14bda2e..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/downward-api.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const DownwardAPI: VolumeVariantComponent<"downwardAPI"> = ( - ({ variant: { items }}) => ( - <> - -
    - {items.map(item =>
  • {item.path}
  • )} -
-
- - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/empty-dir.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/empty-dir.tsx deleted file mode 100644 index c648d42354..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/empty-dir.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const EmptyDir: VolumeVariantComponent<"emptyDir"> = ( - ({ variant: { medium, sizeLimit }}) => ( - <> - - {medium || ""} - - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/ephemeral.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/ephemeral.tsx deleted file mode 100644 index a839f73fdb..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/ephemeral.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { dump } from "js-yaml"; -import React from "react"; -import { DrawerItem, DrawerItemLabels } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Ephemeral: VolumeVariantComponent<"ephemeral"> = ( - ({ pod, volumeName, variant: { volumeClaimTemplate: { metadata = {}, spec }}}) => ( - <> - - {`${pod.getName()}-${volumeName}`} - - - - - {dump(spec)} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/fiber-channel.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/fiber-channel.tsx deleted file mode 100644 index 684c4f1f32..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/fiber-channel.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const FiberChannel: VolumeVariantComponent<"fc"> = ( - ({ variant: { targetWWNs, lun, fsType = "ext4", readOnly = false }}) => ( - <> - -
    - {targetWWNs.map(targetWWN =>
  • {targetWWN}
  • )} -
-
- - {lun.toString()} - - - {fsType} - - - {readOnly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx deleted file mode 100644 index 130edfaaa6..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/flex-volume.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedFlexVolume = (props: PodVolumeVariantSpecificProps<"flexVolume"> & Dependencies) => { - const { - pod, - variant: { - driver, - fsType, - secretRef, - readOnly = false, - options = {}, - }, - secretApi, - } = props; - - return ( - <> - - {driver} - - - {fsType || "-- system default --"} - - - - {readOnly.toString()} - - { - Object.entries(options) - .map(([key, value]) => ( - - {value} - - )) - } - - ); -}; - -export const FlexVolume = withInjectables>(NonInjectedFlexVolume, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/flocker.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/flocker.tsx deleted file mode 100644 index d4edefcba2..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/flocker.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Flocker: VolumeVariantComponent<"flocker"> = ( - ({ variant: { datasetName }}) => ( - <> - - {datasetName} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/gce-persistent-disk.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/gce-persistent-disk.tsx deleted file mode 100644 index 7c15b53109..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/gce-persistent-disk.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const GcePersistentDisk: VolumeVariantComponent<"gcePersistentDisk"> = ( - ({ variant: { pdName, fsType = "ext4" }}) => ( - <> - - {pdName} - - - {fsType} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/git-repo.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/git-repo.tsx deleted file mode 100644 index 23e6421210..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/git-repo.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const GitRepo: VolumeVariantComponent<"gitRepo"> = ( - ({ variant: { repository, revision }}) => ( - <> - - {repository} - - - {revision} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/gluster-fs.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/gluster-fs.tsx deleted file mode 100644 index b3937b7610..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/gluster-fs.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const GlusterFs: VolumeVariantComponent<"glusterfs"> = ( - ({ variant: { endpoints, path, readOnly = false }}) => ( - <> - - {endpoints} - - - {path} - - - {readOnly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/host-path.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/host-path.tsx deleted file mode 100644 index 5b80431733..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/host-path.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const HostPath: VolumeVariantComponent<"hostPath"> = ( - ({ variant: { path, type }}) => ( - <> - - {path} - - - {type || "-- none --"} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/i-scsi.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/i-scsi.tsx deleted file mode 100644 index 1146752061..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/i-scsi.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const IScsi: VolumeVariantComponent<"iscsi"> = ( - ({ variant: { targetPortal, iqn, lun, fsType = "ext4", readOnly = false, chapAuthDiscovery, chapAuthSession, secretRef }}) => ( - <> - - {targetPortal} - - - {iqn} - - - {lun.toString()} - - - {fsType} - - - {readOnly.toString()} - - {chapAuthDiscovery && ( - - {chapAuthDiscovery.toString()} - - )} - {chapAuthSession && ( - - {chapAuthSession.toString()} - - )} - { secretRef && ( - - {secretRef.name} - - )} - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/local.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/local.tsx deleted file mode 100644 index a4e087ae2c..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/local.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Local: VolumeVariantComponent<"local"> = ( - ({ variant: { path }}) => ( - <> - - {path} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/network-fs.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/network-fs.tsx deleted file mode 100644 index d40f2eaa08..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/network-fs.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const NetworkFs: VolumeVariantComponent<"nfs"> = ( - ({ variant: { server, path, readOnly = false }}) => ( - <> - - {server} - - - {path} - - - {readOnly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx deleted file mode 100644 index 9893a65931..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/persistent-volume-claim.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { PersistentVolumeClaimApi } from "../../../../../../common/k8s-api/endpoints"; -import persistentVolumeClaimApiInjectable from "../../../../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - persistentVolumeClaimApi: PersistentVolumeClaimApi; -} - -const NonInjectedPersistentVolumeClaim = (props: PodVolumeVariantSpecificProps<"persistentVolumeClaim"> & Dependencies) => { - const { - pod, - variant: { claimName }, - persistentVolumeClaimApi, - } = props; - - return ( - - ); -}; - -export const PersistentVolumeClaim = withInjectables>(NonInjectedPersistentVolumeClaim, { - getProps: (di, props) => ({ - ...props, - persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/photon-persistent-disk.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/photon-persistent-disk.tsx deleted file mode 100644 index 457da2f62b..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/photon-persistent-disk.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const PhotonPersistentDisk: VolumeVariantComponent<"photonPersistentDisk"> = ( - ({ variant: { pdID, fsType = "ext4" }}) => ( - <> - - {pdID} - - - {fsType} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/portworx-volume.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/portworx-volume.tsx deleted file mode 100644 index 103be78acf..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/portworx-volume.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const PortworxVolume: VolumeVariantComponent<"portworxVolume"> = ( - ({ variant: { volumeID, fsType = "ext4", readOnly = false }}) => ( - <> - - {volumeID} - - - {fsType} - - - {readOnly.toString()} - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx deleted file mode 100644 index 24405bc1a9..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { render } from "@testing-library/react"; -import React from "react"; -import type { ProjectedSource } from "../../../../../../common/k8s-api/endpoints"; -import { Pod } from "../../../../../../common/k8s-api/endpoints"; -import { Projected } from "./projected"; - -describe("", () => { - it("renders", () => { - const projectedVolume: ProjectedSource = { }; - const projectedVolumeName = "my-projected"; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: projectedVolumeName, - projected: projectedVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders default mount mode in octal when provided", () => { - const projectedVolume: ProjectedSource = { - defaultMode: 0o777, - sources: [], - }; - const projectedVolumeName = "my-projected"; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: projectedVolumeName, - projected: projectedVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders when no sources array provided", () => { - const projectedVolume: ProjectedSource = { - defaultMode: 0o777, - }; - const projectedVolumeName = "my-projected"; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: projectedVolumeName, - projected: projectedVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.baseElement).toMatchSnapshot(); - }); - - it("renders a secret source, when provided", () => { - const projectedVolume: ProjectedSource = { - defaultMode: 0o777, - sources: [{ - secret: { - name: "my-projected-secret", - items: [{ - key: "foo", - path: "/bar", - }], - }, - }], - }; - const projectedVolumeName = "my-projected"; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: projectedVolumeName, - projected: projectedVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.baseElement).toMatchSnapshot(); - expect(result.getByText("foo⇢/bar", { exact: false })).toBeTruthy(); - }); - - it("renders a secret source including overriding mode", () => { - const projectedVolume: ProjectedSource = { - defaultMode: 0o777, - sources: [{ - secret: { - name: "my-projected-secret", - items: [{ - key: "foo", - path: "/bar", - mode: 0o666, - }], - }, - }], - }; - const projectedVolumeName = "my-projected"; - const pod = new Pod({ - apiVersion: "v1", - kind: "Pod", - metadata: { - name: "my-pod", - namespace: "default", - resourceVersion: "1", - uid: "123", - selfLink: "/api/v1/pod/default/my-pod", - }, - spec: { - volumes: [{ - name: projectedVolumeName, - projected: projectedVolume, - }], - }, - }); - const result = render(( - - )); - - expect(result.baseElement).toMatchSnapshot(); - expect(result.getByText("(0o666)", { exact: false })).toBeTruthy(); - }); -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx deleted file mode 100644 index c7ca22c87f..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { displayMode } from "../../../../../utils"; -import { DrawerItem, DrawerTitle } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Projected: VolumeVariantComponent<"projected"> = ( - ({ variant: { sources, defaultMode }}) => ( - <> - {typeof defaultMode === "number" && ( - - {displayMode(defaultMode)} - - )} - - { - sources?.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => ( - - {secret && ( - <> - Secret - - {secret.name} - - -
    - {secret.items?.map(({ key, path, mode }) => ( -
  • - {`${key}⇢${path}`} - {typeof mode === "number" && ( - ` (${displayMode(mode)})` - )} -
  • - ))} -
-
- - )} - {downwardAPI && ( - <> - Downward API - -
    - {downwardAPI.items?.map(({ path }) => ( -
  • - {path} -
  • - ))} -
-
- - )} - {configMap && ( - <> - Config Map - - {configMap.name} - - -
    - {configMap.items?.map(({ key, path }) => ( -
  • - {`${key}⇢${path}`} -
  • - ))} -
-
- - )} - {serviceAccountToken && ( - <> - Service Account Token - - - {`${serviceAccountToken.expirationSeconds ?? 60*60 /* an hour */}s`} - - - {serviceAccountToken.path} - - - )} -
- )) - } -
- - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/quobyte.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/quobyte.tsx deleted file mode 100644 index a315f254e8..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/quobyte.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const Quobyte: VolumeVariantComponent<"quobyte"> = ( - ({ variant: { registry, volume, readOnly = false, user = "serviceaccount", group, tenant }}) => ( - <> - - {registry} - - - {volume} - - - {readOnly.toString()} - - - {user} - - - {group ?? "-- no group --"} - - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx deleted file mode 100644 index eac32050d7..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/rados-block-device.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedRadosBlockDevice = (props: PodVolumeVariantSpecificProps<"rbd"> & Dependencies) => { - const { - pod, - variant: { - monitors, - image, - fsType = "ext4", - pool = "rbd", - user = "admin", - keyring = "/etc/ceph/keyright", - secretRef, - readOnly = false, - }, - secretApi, - } = props; - - return ( - <> - -
    - {monitors.map(monitor =>
  • {monitor}
  • )} -
-
- - {image} - - - {fsType} - - - {pool} - - - {user} - - { - secretRef - ? ( - - ) - : ( - - {keyring} - - ) - } - - {readOnly.toString()} - - - ); -}; - -export const RadosBlockDevice = withInjectables>(NonInjectedRadosBlockDevice, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx deleted file mode 100644 index 717be48fe5..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/scale-io.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedScaleIo = (props: PodVolumeVariantSpecificProps<"scaleIO"> & Dependencies) => { - const { - pod, - variant: { - gateway, - system, - secretRef, - sslEnabled = false, - protectionDomain, - storagePool, - storageMode = "ThinProvisioned", - volumeName, - fsType = "xfs", - readOnly = false, - }, - secretApi, - } = props; - - return ( - <> - - {gateway} - - - {system} - - - - {sslEnabled.toString()} - - - - - - {volumeName} - - - {fsType} - - - {readOnly.toString()} - - - ); -}; - -export const ScaleIo = withInjectables>(NonInjectedScaleIo, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx deleted file mode 100644 index 5ad1ee542c..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/secret.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedSecret = (props: PodVolumeVariantSpecificProps<"secret"> & Dependencies) => { - const { - pod, - variant: { secretName, items = [], defaultMode = 0o644, optional = false }, - secretApi, - } = props; - - return ( - <> - - - - {`0o${defaultMode.toString(8)}`} - - - {optional.toString()} - - - ); -}; - -export const Secret = withInjectables>(NonInjectedSecret, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx deleted file mode 100644 index a69ec5918b..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/storage-os.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import type { SecretApi } from "../../../../../../common/k8s-api/endpoints"; -import secretApiInjectable from "../../../../../../common/k8s-api/endpoints/secret.api.injectable"; -import { DrawerItem } from "../../../../drawer"; -import type { PodVolumeVariantSpecificProps } from "../variant-helpers"; -import { LocalRef } from "../variant-helpers"; - -interface Dependencies { - secretApi: SecretApi; -} - -const NonInjectedStorageOs = (props: PodVolumeVariantSpecificProps<"storageos"> & Dependencies) => { - const { - pod, - variant: { volumeName, volumeNamespace, fsType = "ext4", readOnly = false, secretRef }, - secretApi, - } = props; - - return ( - <> - - {volumeName} - - - - {fsType} - - - {readOnly.toString()} - - - - ); -}; - -export const StorageOs = withInjectables>(NonInjectedStorageOs, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/vsphere-volume.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/vsphere-volume.tsx deleted file mode 100644 index eb97917e91..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/vsphere-volume.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { DrawerItem } from "../../../../drawer"; -import type { VolumeVariantComponent } from "../variant-helpers"; - -export const VsphereVolume: VolumeVariantComponent<"vsphereVolume"> = ( - ({ variant: { volumePath, fsType = "ext4", storagePolicyName, storagePolicyID }}) => ( - <> - - {volumePath} - - - {fsType} - - - - - ) -); diff --git a/src/renderer/components/+workloads-pods/details/volumes/view.tsx b/src/renderer/components/+workloads-pods/details/volumes/view.tsx deleted file mode 100644 index f90ca6a31e..0000000000 --- a/src/renderer/components/+workloads-pods/details/volumes/view.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observer } from "mobx-react"; -import React from "react"; -import type { Pod } from "../../../../../common/k8s-api/endpoints"; -import { DrawerTitle } from "../../../drawer"; -import { Icon } from "../../../icon"; -import { VolumeVariant } from "./variant"; - -export interface PodVolumesProps { - pod: Pod; -} - -export const PodVolumes = observer(({ pod }: PodVolumesProps) => { - const volumes = pod.getVolumes() ?? []; - - if (volumes.length === 0) { - return null; - } - - return ( - <> - Volumes - {volumes.map(volume => ( -
-
- - {volume.name} -
- -
- ))} - - ); -}); diff --git a/src/renderer/components/+workloads-pods/get-pod-by-id.injectable.ts b/src/renderer/components/+workloads-pods/get-pod-by-id.injectable.ts deleted file mode 100644 index b0c0a5729a..0000000000 --- a/src/renderer/components/+workloads-pods/get-pod-by-id.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 { Pod } from "../../../common/k8s-api/endpoints"; -import podStoreInjectable from "./store.injectable"; - -export type GetPodById = (id: string) => Pod | undefined; - -const getPodByIdInjectable = getInjectable({ - id: "get-pod-by-id", - instantiate: (di): GetPodById => { - const store = di.inject(podStoreInjectable); - - return id => store.getById(id); - }, -}); - -export default getPodByIdInjectable; diff --git a/src/renderer/components/+workloads-pods/get-pods-by-owner-id.injectable.ts b/src/renderer/components/+workloads-pods/get-pods-by-owner-id.injectable.ts deleted file mode 100644 index 01862831a2..0000000000 --- a/src/renderer/components/+workloads-pods/get-pods-by-owner-id.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 { Pod } from "../../../common/k8s-api/endpoints"; -import podStoreInjectable from "./store.injectable"; - -export type GetPodsByOwnerId = (ownerId: string) => Pod[]; - -const getPodsByOwnerIdInjectable = getInjectable({ - id: "get-pods-by-owner-id", - instantiate: (di): GetPodsByOwnerId => { - const podStore = di.inject(podStoreInjectable); - - return (ownerId) => podStore.getPodsByOwnerId(ownerId); - }, -}); - -export default getPodsByOwnerIdInjectable; diff --git a/src/renderer/components/+workloads-pods/index.ts b/src/renderer/components/+workloads-pods/index.ts deleted file mode 100644 index 7a97e64c8d..0000000000 --- a/src/renderer/components/+workloads-pods/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./pods"; -export * from "./pod-details"; diff --git a/src/renderer/components/+workloads-pods/load-pods-from-all-namespaces.injectable.ts b/src/renderer/components/+workloads-pods/load-pods-from-all-namespaces.injectable.ts deleted file mode 100644 index f6e96d5b79..0000000000 --- a/src/renderer/components/+workloads-pods/load-pods-from-all-namespaces.injectable.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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 namespaceStoreInjectable from "../+namespaces/store.injectable"; -import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; -import podStoreInjectable from "./store.injectable"; - -const loadPodsFromAllNamespacesInjectable = getInjectable({ - id: "load-pods-from-all-namespaces", - instantiate: (di) => { - const podStore = di.inject(podStoreInjectable); - const namespaceStore = di.inject(namespaceStoreInjectable); - const showErrorNotification = di.inject(showErrorNotificationInjectable); - - return () => { - podStore.loadAll({ - namespaces: [...namespaceStore.getItems().map(ns => ns.getName())], - onLoadFailure: error => - showErrorNotification(`Can not load Pods. ${String(error)}`), - }); - }; - }, -}); - -export default loadPodsFromAllNamespacesInjectable; diff --git a/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx b/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx deleted file mode 100644 index aab325ab9b..0000000000 --- a/src/renderer/components/+workloads-pods/metrics-detail-container.injectable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { type IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { Pod } from "../../../common/k8s-api/endpoints"; -import type { PodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import podMetricsInjectable from "./metrics.injectable"; -import { PodCharts, podMetricTabs } from "./pod-charts"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedPodMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const PodMetricsDetailsComponent = withInjectables>(NonInjectedPodMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(podMetricsInjectable, props.object), - ...props, - }), -}); - -const podMetricsDetailsComponentInjectable = getInjectable({ - id: "pod-metrics-details-container", - instantiate: (di) => ({ - Component: PodMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.Pod), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default podMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-pods/metrics.injectable.ts b/src/renderer/components/+workloads-pods/metrics.injectable.ts deleted file mode 100644 index a06bc9e7e9..0000000000 --- a/src/renderer/components/+workloads-pods/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { Pod } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; - -const podMetricsInjectable = getInjectable({ - id: "pod-metrics", - instantiate: (di, pod) => { - const requestPodMetrics = di.inject(requestPodMetricsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: () => { - now(60 * 1000); - - return requestPodMetrics([pod], pod.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, pod: Pod) => pod.getId(), - }), -}); - -export default podMetricsInjectable; diff --git a/src/renderer/components/+workloads-pods/pod-charts.tsx b/src/renderer/components/+workloads-pods/pod-charts.tsx deleted file mode 100644 index 2911388bdc..0000000000 --- a/src/renderer/components/+workloads-pods/pod-charts.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { mapValues } from "lodash"; -import { observer } from "mobx-react"; -import React, { useContext } from "react"; -import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; -import type { ChartDataSets } from "../chart"; -import { BarChart } from "../chart"; -import type { MetricsTab } from "../chart/options"; -import { metricTabOptions } from "../chart/options"; -import type { AtLeastOneMetricTab } from "../resource-metrics"; -import { ResourceMetricsContext } from "../resource-metrics"; -import { NoMetrics } from "../resource-metrics/no-metrics"; - -export const podMetricTabs: AtLeastOneMetricTab = [ - "CPU", - "Memory", - "Network", - "Filesystem", -]; - -export const PodCharts = observer(() => { - const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {}; - - if (!metrics || !object || !tab) return null; - if (isMetricsEmpty(metrics)) return ; - - const id = object.getId(); - const { - cpuUsage, - memoryUsage, - fsUsage, - fsWrites, - fsReads, - networkReceive, - networkTransmit, - } = mapValues(metrics, metric => normalizeMetrics(metric).data.result[0].values); - - const datasets: Partial> = { - CPU: [ - { - id: `${id}-cpuUsage`, - label: `Usage`, - tooltip: `Container CPU cores usage`, - borderColor: "#3D90CE", - data: cpuUsage.map(([x, y]) => ({ x, y })), - }, - ], - Memory: [ - { - id: `${id}-memoryUsage`, - label: `Usage`, - tooltip: `Container memory usage`, - borderColor: "#c93dce", - data: memoryUsage.map(([x, y]) => ({ x, y })), - }, - ], - Network: [ - { - id: `${id}-networkReceive`, - label: `Receive`, - tooltip: `Bytes received by all containers`, - borderColor: "#64c5d6", - data: networkReceive.map(([x, y]) => ({ x, y })), - }, - { - id: `${id}-networkTransmit`, - label: `Transmit`, - tooltip: `Bytes transmitted from all containers`, - borderColor: "#46cd9e", - data: networkTransmit.map(([x, y]) => ({ x, y })), - }, - ], - Filesystem: [ - { - id: `${id}-fsUsage`, - label: `Usage`, - tooltip: `Bytes consumed on this filesystem`, - borderColor: "#ffc63d", - data: fsUsage.map(([x, y]) => ({ x, y })), - }, - { - id: `${id}-fsWrites`, - label: `Writes`, - tooltip: `Bytes written on this filesystem`, - borderColor: "#ff963d", - data: fsWrites.map(([x, y]) => ({ x, y })), - }, - { - id: `${id}-fsReads`, - label: `Reads`, - tooltip: `Bytes read on this filesystem`, - borderColor: "#fff73d", - data: fsReads.map(([x, y]) => ({ x, y })), - }, - ], - }; - - return ( - - ); -}); diff --git a/src/renderer/components/+workloads-pods/pod-container-env.scss b/src/renderer/components/+workloads-pods/pod-container-env.scss deleted file mode 100644 index 9fec5b8e30..0000000000 --- a/src/renderer/components/+workloads-pods/pod-container-env.scss +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ContainerEnvironment { - .secret-button { - &.loading { - opacity: 0.5; - pointer-events: none; - } - } - - .variable { - padding-bottom: $padding; - - &:last-child { - padding-bottom: 0; - } - - .var-name { - color: var(--textColorPrimary) - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-container-env.tsx b/src/renderer/components/+workloads-pods/pod-container-env.tsx deleted file mode 100644 index 88a7667e6b..0000000000 --- a/src/renderer/components/+workloads-pods/pod-container-env.tsx +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-container-env.scss"; - -import React, { useEffect, useState } from "react"; -import { observer } from "mobx-react"; -import type { Container, EnvVarKeySelector, Secret } from "../../../common/k8s-api/endpoints"; -import { DrawerItem } from "../drawer"; -import { autorun } from "mobx"; -import { Icon } from "../icon"; -import { base64, cssNames, object } from "../../utils"; -import _ from "lodash"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { ConfigMapStore } from "../+config-maps/store"; -import type { SecretStore } from "../+config-secrets/store"; -import configMapStoreInjectable from "../+config-maps/store.injectable"; -import secretStoreInjectable from "../+config-secrets/store.injectable"; - -export interface ContainerEnvironmentProps { - container: Container; - namespace: string; -} - -interface Dependencies { - configMapStore: ConfigMapStore; - secretStore: SecretStore; -} - -const NonInjectedContainerEnvironment = observer((props: Dependencies & ContainerEnvironmentProps) => { - const { - container: { env, envFrom = [] }, - namespace, - configMapStore, - secretStore, - } = props; - - useEffect( () => autorun(() => { - for (const { valueFrom } of env ?? []) { - if (valueFrom?.configMapKeyRef?.name) { - configMapStore.load({ name: valueFrom.configMapKeyRef.name, namespace }); - } - } - - for (const { configMapRef, secretRef } of envFrom ?? []) { - if (secretRef?.name) { - secretStore.load({ name: secretRef.name, namespace }); - } - - if (configMapRef?.name) { - configMapStore.load({ name: configMapRef.name, namespace }); - } - } - }), []); - - const renderEnv = () => { - const orderedEnv = _.sortBy(env, "name"); - - return orderedEnv.map(variable => { - const { name, value, valueFrom } = variable; - let secretValue = null; - - if (value) { - secretValue = value; - } else if (valueFrom) { - const { fieldRef, secretKeyRef, configMapKeyRef } = valueFrom; - - if (fieldRef) { - const { apiVersion, fieldPath } = fieldRef; - - secretValue = `fieldRef(${apiVersion}:${fieldPath})`; - } else if (secretKeyRef?.name) { - secretValue = ( - - ); - } else if (configMapKeyRef?.name) { - const { name, key } = configMapKeyRef; - const configMap = configMapStore.getByName(name, namespace); - - secretValue = configMap - ? configMap.data[key] - : `configMapKeyRef(${name}${key})`; - } - } - - return ( -
- {name} - {` : `} - {secretValue} -
- ); - }); - }; - - const renderEnvFrom = () => ( - envFrom - .flatMap(({ configMapRef, secretRef, prefix }) => { - if (configMapRef?.name) { - return renderEnvFromConfigMap(configMapRef.name, prefix); - } - - if (secretRef?.name) { - return renderEnvFromSecret(secretRef.name, prefix); - } - - return null; - }) - ); - - const renderEnvFromConfigMap = (configMapName: string, prefix: string | undefined) => { - const configMap = configMapStore.getByName(configMapName, namespace); - - if (!configMap) return null; - - return object.entries(configMap.data) - .map(([name, value]) => ( -
- - {prefix} - {name} - - {` : `} - {value} -
- )); - }; - - const renderEnvFromSecret = (secretName: string, prefix: string | undefined) => { - const secret = secretStore.getByName(secretName, namespace); - - if (!secret) return null; - - return Object.keys(secret.data) - .map(key => ( -
- - {prefix} - {key} - - {` : `} - -
- )); - }; - - return ( - - {env && renderEnv()} - {envFrom && renderEnvFrom()} - - ); -}); - -export const ContainerEnvironment = withInjectables(NonInjectedContainerEnvironment, { - getProps: (di, props) => ({ - ...props, - configMapStore: di.inject(configMapStoreInjectable), - secretStore: di.inject(secretStoreInjectable), - }), -}); - -interface SecretKeyProps { - reference: EnvVarKeySelector; - namespace: string; - secretStore: SecretStore; -} - -const SecretKey = (props: SecretKeyProps) => { - const { - reference: { name, key }, - namespace, - secretStore, - } = props; - - const [loading, setLoading] = useState(false); - const [secret, setSecret] = useState(); - - if (!name) { - return null; - } - - const showKey = async (evt: React.MouseEvent) => { - evt.preventDefault(); - setLoading(true); - const secret = await secretStore.load({ name, namespace }); - - setLoading(false); - setSecret(secret); - }; - - const value = secret?.data?.[key]; - - if (value) { - return <>{base64.decode(value)}; - } - - return ( - <> - {`secretKeyRef(${name}.${key})`} -   - - - ); -}; diff --git a/src/renderer/components/+workloads-pods/pod-container-port.scss b/src/renderer/components/+workloads-pods/pod-container-port.scss deleted file mode 100644 index 1c08040ae5..0000000000 --- a/src/renderer/components/+workloads-pods/pod-container-port.scss +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodContainerPort { - &.waiting { - opacity: 0.5; - pointer-events: none; - } - - &:not(:last-child) { - margin-bottom: $margin; - } - - span { - cursor: pointer; - color: var(--primary); - text-decoration: underline; - position: relative; - padding-right: 1em; - } - - .portInput { - display: inline-block !important; - width: 70px; - margin-left: 10px; - margin-right: 10px; - } -} diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx deleted file mode 100644 index eb2464f664..0000000000 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-container-port.scss"; - -import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import type { ContainerPort, Pod } from "../../../common/k8s-api/endpoints"; -import { action, makeObservable, observable, reaction } from "mobx"; -import { cssNames } from "../../utils"; -import type { ShowNotification } from "../notifications"; -import { Button } from "../button"; -import type { ForwardedPort, PortForwardStore } from "../../port-forward"; -import { predictProtocol } from "../../port-forward"; -import { Spinner } from "../spinner"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; -import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable"; -import type { Logger } from "../../../common/logger"; -import aboutPortForwardingInjectable from "../../port-forward/about-port-forwarding.injectable"; -import notifyErrorPortForwardingInjectable from "../../port-forward/notify-error-port-forwarding.injectable"; -import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable"; -import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; - -export interface PodContainerPortProps { - pod: Pod; - port: ContainerPort; -} - -interface Dependencies { - portForwardStore: PortForwardStore; - logger: Logger; - openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void; - aboutPortForwarding: () => void; - notifyErrorPortForwarding: (message: string) => void; - openPortForward: OpenPortForward; - showErrorNotification: ShowNotification; -} - -@observer -class NonInjectedPodContainerPort extends React.Component { - @observable waiting = false; - @observable forwardPort = 0; - @observable isPortForwarded = false; - @observable isActive = false; - - constructor(props: PodContainerPortProps & Dependencies) { - super(props); - makeObservable(this); - this.checkExistingPortForwarding(); - } - - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.pod, () => this.checkExistingPortForwarding()), - ]); - } - - get portForwardStore() { - return this.props.portForwardStore; - } - - @action - async checkExistingPortForwarding() { - const { pod, port } = this.props; - let portForward: ForwardedPort | undefined; - - try { - portForward = await this.portForwardStore.getPortForward({ - kind: "pod", - name: pod.getName(), - namespace: pod.getNs(), - port: port.containerPort, - forwardPort: this.forwardPort, - }); - } catch (error) { - this.isPortForwarded = false; - this.isActive = false; - } - - if (!portForward) { - return; - } - - this.forwardPort = portForward.forwardPort; - this.isPortForwarded = true; - this.isActive = portForward.status === "Active"; - } - - @action - async portForward() { - const { pod, port, openPortForward } = this.props; - let portForward: ForwardedPort = { - kind: "pod", - name: pod.getName(), - namespace: pod.getNs(), - port: port.containerPort, - forwardPort: this.forwardPort, - protocol: predictProtocol(port.name), - status: "Active", - }; - - this.waiting = true; - - try { - // determine how many port-forwards already exist - const { length } = this.portForwardStore.getPortForwards(); - - if (!this.isPortForwarded) { - portForward = await this.portForwardStore.add(portForward); - } else if (!this.isActive) { - portForward = await this.portForwardStore.start(portForward); - } - - if (portForward.status === "Active") { - openPortForward(portForward); - - // if this is the first port-forward show the about notification - if (!length) { - this.props.aboutPortForwarding(); - } - } else { - this.props.notifyErrorPortForwarding(`Error occurred starting port-forward, the local port may not be available or the ${portForward.kind} ${portForward.name} may not be reachable`); - } - } catch (error) { - this.props.logger.error("[POD-CONTAINER-PORT]:", error, portForward); - } finally { - this.checkExistingPortForwarding(); - this.waiting = false; - } - } - - @action - async stopPortForward() { - const { pod, port, showErrorNotification } = this.props; - const portForward: ForwardedPort = { - kind: "pod", - name: pod.getName(), - namespace: pod.getNs(), - port: port.containerPort, - forwardPort: this.forwardPort, - }; - - this.waiting = true; - - try { - await this.portForwardStore.remove(portForward); - } catch (error) { - showErrorNotification(`Error occurred stopping the port-forward from port ${portForward.forwardPort}.`); - } finally { - this.checkExistingPortForwarding(); - this.forwardPort = 0; - this.waiting = false; - } - } - - render() { - const { pod, port } = this.props; - const { name, containerPort, protocol } = port; - const text = `${name ? `${name}: ` : ""}${containerPort}/${protocol}`; - - const portForwardAction = action(async () => { - if (this.isPortForwarded) { - await this.stopPortForward(); - } else { - const portForward: ForwardedPort = { - kind: "pod", - name: pod.getName(), - namespace: pod.getNs(), - port: port.containerPort, - forwardPort: this.forwardPort, - protocol: predictProtocol(port.name), - }; - - this.props.openPortForwardDialog(portForward, { openInBrowser: true, onClose: () => this.checkExistingPortForwarding() }); - } - }); - - return ( -
- this.portForward()}> - {text} - - - {this.waiting && ( - - )} -
- ); - } -} - -export const PodContainerPort = withInjectables(NonInjectedPodContainerPort, { - getProps: (di, props) => ({ - ...props, - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - logger: di.inject(loggerInjectable), - showErrorNotification: di.inject(showErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/pod-details-affinities.tsx b/src/renderer/components/+workloads-pods/pod-details-affinities.tsx deleted file mode 100644 index 4b51771d57..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-affinities.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import yaml from "js-yaml"; -import { DrawerItem, DrawerParamToggler } from "../drawer"; -import type { DaemonSet, Deployment, Job, Pod, ReplicaSet, StatefulSet } from "../../../common/k8s-api/endpoints"; -import { MonacoEditor } from "../monaco-editor"; - -export interface PodDetailsAffinitiesProps { - workload: Pod | Deployment | DaemonSet | StatefulSet | ReplicaSet | Job; -} - -export class PodDetailsAffinities extends React.Component { - render() { - const { workload } = this.props; - const affinitiesNum = workload.getAffinityNumber(); - const affinities = workload.getAffinity(); - - if (!affinitiesNum) return null; - - return ( - - - - - - ); - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-container.scss b/src/renderer/components/+workloads-pods/pod-details-container.scss deleted file mode 100644 index 3e5357ba11..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-container.scss +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetailsContainer { - margin: $margin * 2 0; - - .mount-path { - &:first-child { - margin-top: 0; - } - - display: block; - font-family: $font-monospace; - font-size: 90%; - background: var(--colorVague); - color: var(--textColorSecondary); - border-radius: $radius; - padding: .2em .4em; - margin-top: $margin; - } - - .pod-container-title { - font-weight: bold; - margin-bottom: $margin; - - .StatusBrick { - background: var(--colorTerminated); - margin-right: $margin; - - @include pod-status-bgs; - - &.running:not(.ready) { - background-color: $pod-status-pending-color; - } - } - } - - .status { - color: var(--colorTerminated); - - @include pod-status-colors; - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-container.tsx b/src/renderer/components/+workloads-pods/pod-details-container.tsx deleted file mode 100644 index 947dec01ac..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-container.tsx +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details-container.scss"; - -import React from "react"; -import type { Container, PodContainerStatus, Pod } from "../../../common/k8s-api/endpoints"; -import { DrawerItem } from "../drawer"; -import { cssNames, isDefined } from "../../utils"; -import { StatusBrick } from "../status-brick"; -import { Badge } from "../badge"; -import { ContainerEnvironment } from "./pod-container-env"; -import { PodContainerPort } from "./pod-container-port"; -import { ResourceMetrics } from "../resource-metrics"; -import type { MetricData } from "../../../common/k8s-api/endpoints/metrics.api"; -import { ContainerCharts } from "./container-charts"; -import { LocaleDate } from "../locale-date"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { PortForwardStore } from "../../port-forward"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-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"; - -export interface PodDetailsContainerProps { - pod: Pod; - container: Container; - metrics?: Partial>; -} - -interface Dependencies { - portForwardStore: PortForwardStore; - getActiveClusterEntity: GetActiveClusterEntity; -} - -@observer -class NonInjectedPodDetailsContainer extends React.Component { - - componentDidMount() { - disposeOnUnmount(this, [ - this.props.portForwardStore.watch(), - ]); - } - - renderStatus(state: string, status: PodContainerStatus | null | undefined) { - const { ready = false, state: containerState = {}} = status ?? {}; - const { terminated } = containerState; - - return ( - - {state} - {ready ? ", ready" : ""} - {terminated ? ` - ${terminated.reason} (exit code: ${terminated.exitCode})` : ""} - - ); - } - - renderLastState(lastState: string, status: PodContainerStatus | null | undefined) { - const { lastState: lastContainerState = {}} = status ?? {}; - const { terminated } = lastContainerState; - - if (lastState === "terminated" && terminated) { - return ( - - {lastState} -
- Reason: - {`Reason: ${terminated.reason} - exit code: ${terminated.exitCode}`} -
- {"Started at: "} - {} -
- {"Finished at: "} - {} -
-
- ); - } - - return null; - } - - render() { - const { pod, container, metrics, getActiveClusterEntity } = this.props; - - if (!pod || !container) return null; - const { name, image, imagePullPolicy, ports, volumeMounts, command, args } = container; - const status = pod.getContainerStatuses().find(status => status.name === container.name); - const state = status ? Object.keys(status?.state ?? {})[0] : ""; - const lastState = status ? Object.keys(status?.lastState ?? {})[0] : ""; - const ready = status ? status.ready : ""; - const imageId = status? status.imageID : ""; - const liveness = pod.getLivenessProbe(container); - const readiness = pod.getReadinessProbe(container); - const startup = pod.getStartupProbe(container); - const isInitContainer = !!pod.getInitContainers().find(c => c.name == name); - const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Container); - - return ( -
-
- - {name} -
- {(!isMetricHidden && !isInitContainer && metrics) && ( - - - - )} - {status && ( - - {this.renderStatus(state, status)} - - )} - {lastState && ( - - {this.renderLastState(lastState, status)} - - )} - - - - {imagePullPolicy && imagePullPolicy !== "IfNotPresent" && ( - - {imagePullPolicy} - - )} - {ports && ports.length > 0 && ( - - { - ports - .filter(isDefined) - .map((port) => ( - - )) - } - - )} - {} - {volumeMounts && volumeMounts.length > 0 && ( - - { - volumeMounts.map(mount => { - const { name, mountPath, readOnly } = mount; - - return ( - - {mountPath} - - {`from ${name} (${readOnly ? "ro" : "rw"})`} - - - ); - }) - } - - )} - {liveness.length > 0 && ( - - { - liveness.map((value, index) => ( - - )) - } - - )} - {readiness.length > 0 && ( - - { - readiness.map((value, index) => ( - - )) - } - - )} - {startup.length > 0 && ( - - { - startup.map((value, index) => ( - - )) - } - - )} - {command && ( - - {command.join(" ")} - - )} - - {args && ( - - {args.join(" ")} - - )} -
- ); - } -} - -export const PodDetailsContainer = withInjectables(NonInjectedPodDetailsContainer, { - getProps: (di, props) => ({ - ...props, - portForwardStore: di.inject(portForwardStoreInjectable), - getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/pod-details-list.scss b/src/renderer/components/+workloads-pods/pod-details-list.scss deleted file mode 100644 index fe161b023f..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-list.scss +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetailsList { - position: relative; - - .Table { - margin: 0 (-$margin * 3); - - &.virtual { - height: 500px; - } - - .TableHead.sticky { - top: calc(var(--spacing) * -1); - } - } - - .TableCell { - &:first-child { - margin-left: $margin; - } - - &:last-child { - margin-right: $margin; - } - - &.name { - flex-grow: 2; - } - - &.node { - flex-grow: 2; - } - - &.namespace { - flex-grow: 1.2; - } - - &.cpu { - align-self: center; - - .LineProgress { - color: var(--blue); - } - } - - &.memory { - align-self: center; - - .LineProgress { - color: var(--magenta); - } - } - - &.warning { - @include table-cell-warning; - } - - &.status { - @include pod-status-colors; - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-list.tsx b/src/renderer/components/+workloads-pods/pod-details-list.tsx deleted file mode 100644 index 83e15f794e..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-list.tsx +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details-list.scss"; - -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { reaction } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import type { Pod } from "../../../common/k8s-api/endpoints"; -import { autoBind, bytesToUnits, cssNames, interval, prevDefault } from "../../utils"; -import { LineProgress } from "../line-progress"; -import type { KubeObject } from "../../../common/k8s-api/kube-object"; -import { Table, TableCell, TableHead, TableRow } from "../table"; -import { Spinner } from "../spinner"; -import { DrawerTitle } from "../drawer"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { PodStore } from "./store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import podStoreInjectable from "./store.injectable"; -import type { ShowDetails } from "../kube-detail-params/show-details.injectable"; -import showDetailsInjectable from "../kube-detail-params/show-details.injectable"; - -enum sortBy { - name = "name", - node = "node", - namespace = "namespace", - cpu = "cpu", - memory = "memory", -} - -export interface PodDetailsListProps { - pods: Pod[]; - owner: KubeObject; - maxCpu?: number; - maxMemory?: number; -} - -interface Dependencies { - podStore: PodStore; - showDetails: ShowDetails; -} - -@observer -class NonInjectedPodDetailsList extends React.Component { - constructor(props: PodDetailsListProps & Dependencies) { - super(props); - autoBind(this); - } - - private metricsWatcher = interval(120, () => { - this.props.podStore.loadKubeMetrics(this.props.owner.getNs()); - }); - - componentDidMount() { - this.metricsWatcher.start(true); - disposeOnUnmount(this, [ - reaction(() => this.props.owner, () => this.metricsWatcher.restart(true)), - ]); - } - - componentWillUnmount() { - this.metricsWatcher.stop(); - } - - renderCpuUsage(id: string, usage: number) { - const { maxCpu } = this.props; - const value = usage.toFixed(3); - - if (!maxCpu) { - if (parseFloat(value) === 0) return 0; - - return value; - } - - const tooltip = ( -

- {`CPU: ${Math.ceil(usage * 100 / maxCpu)}%`} -
- {usage.toFixed(3)} -

- ); - - return ( - - ); - } - - renderMemoryUsage(id: string, usage: number) { - const { maxMemory } = this.props; - - if (!maxMemory) return usage ? bytesToUnits(usage) : 0; - - const tooltip = ( -

- {`Memory: ${Math.ceil(usage * 100 / maxMemory)}%`} -
- {bytesToUnits(usage, { precision: 3 })} -

- ); - - return ( - - ); - } - - getTableRow(uid: string) { - const { pods, podStore, showDetails } = this.props; - const pod = pods.find(pod => pod.getId() == uid); - - if (!pod) { - return; - } - - const metrics = podStore.getPodKubeMetrics(pod); - - return ( - showDetails(pod.selfLink, false))} - > - {pod.getName()} - - {pod.getNodeName()} - {pod.getNs()} - - {`${pod.getRunningContainers().length} / ${pod.getContainers().length}`} - - {this.renderCpuUsage(`cpu-${pod.getId()}`, metrics.cpu)} - {this.renderMemoryUsage(`memory-${pod.getId()}`, metrics.memory)} - {pod.getStatusMessage()} - - ); - } - - render() { - const { pods, podStore } = this.props; - - if (!podStore.isLoaded) { - return ( -
- -
- ); - } - - if (!pods.length) { - return null; - } - - const virtual = pods.length > 20; - - return ( -
- Pods - pod.getName(), - [sortBy.node]: pod => pod.getNodeName(), - [sortBy.namespace]: pod => pod.getNs(), - [sortBy.cpu]: pod => podStore.getPodKubeMetrics(pod).cpu, - [sortBy.memory]: pod => podStore.getPodKubeMetrics(pod).memory, - }} - sortByDefault={{ sortBy: sortBy.cpu, orderBy: "desc" }} - sortSyncWithUrl={false} - getTableRow={this.getTableRow} - renderRow={( - virtual - ? undefined - : (pod => this.getTableRow(pod.getId())) - )} - className="box grow" - > - - Name - - Node - Namespace - Ready - CPU - Memory - Status - -
-
- ); - } -} - -export const PodDetailsList = withInjectables(NonInjectedPodDetailsList, { - getProps: (di, props) => ({ - ...props, - podStore: di.inject(podStoreInjectable), - showDetails: di.inject(showDetailsInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/pod-details-secrets.scss b/src/renderer/components/+workloads-pods/pod-details-secrets.scss deleted file mode 100644 index a959afc79d..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-secrets.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetailsSecrets { - > * { - display: block; - margin-bottom: var(--margin); - - &:last-child { - margin-bottom: 0; - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-secrets.tsx b/src/renderer/components/+workloads-pods/pod-details-secrets.tsx deleted file mode 100644 index 8d78987a75..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-secrets.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details-secrets.scss"; - -import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; -import { reaction } from "mobx"; -import { observer } from "mobx-react"; -import type { Pod, Secret, SecretApi } from "../../../common/k8s-api/endpoints"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable"; -import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; -import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; - -export interface PodDetailsSecretsProps { - pod: Pod; -} - -interface Dependencies { - secretApi: SecretApi; - getDetailsUrl: GetDetailsUrl; -} - -const NonInjectedPodDetailsSecrets = observer((props: PodDetailsSecretsProps & Dependencies) => { - const { - pod, - secretApi, - getDetailsUrl, - } = props; - const [secrets, setSecrets] = useState(new Map()); - - useEffect(() => ( - reaction( - () => pod.getSecrets(), - async (secretNames) => { - const results = await Promise.allSettled( - secretNames.map(secretName => secretApi.get({ - name: secretName, - namespace: pod.getNs(), - })), - ); - - setSecrets(new Map( - results - .filter(result => result.status === "fulfilled" && result.value) - .map(result => (result as PromiseFulfilledResult).value) - .map(secret => [secret.getName(), secret]), - )); - }, - { - fireImmediately: true, - }) - ), []); - - const renderSecret = (name: string) => { - const secret = secrets.get(name); - - if (!secret) { - return ( - - {name} - - ); - } - - return ( - - {secret.getName()} - - ); - }; - - return ( -
- {pod.getSecrets().map(renderSecret)} -
- ); -}); - -export const PodDetailsSecrets = withInjectables(NonInjectedPodDetailsSecrets, { - getProps: (di, props) => ({ - ...props, - secretApi: di.inject(secretApiInjectable), - getDetailsUrl: di.inject(getDetailsUrlInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/pod-details-statuses.scss b/src/renderer/components/+workloads-pods/pod-details-statuses.scss deleted file mode 100644 index 589e9d203e..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-statuses.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetailsStatuses { - span { - padding-right: $margin; - - &.running { - color: $pod-status-running-color; - } - &.pending { - color: $pod-status-pending-color; - } - &.succeeded { - color: $pod-status-succeeded-color; - } - &.failed { - color: $pod-status-failed-color; - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-statuses.tsx b/src/renderer/components/+workloads-pods/pod-details-statuses.tsx deleted file mode 100644 index 8011610572..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-statuses.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details-statuses.scss"; -import React from "react"; -import countBy from "lodash/countBy"; -import kebabCase from "lodash/kebabCase"; -import type { Pod } from "../../../common/k8s-api/endpoints"; - -export interface PodDetailsStatusesProps { - pods: Pod[]; -} - -export class PodDetailsStatuses extends React.Component { - render() { - const { pods } = this.props; - - if (!pods.length) return null; - const statuses = countBy(pods.map(pod => pod.getStatus())); - - return ( -
- { - Object.entries(statuses) - .map(([phase, count]) => ( - - {`${phase}: ${count}`} - - )) - } -
- ); - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-tolerations.scss b/src/renderer/components/+workloads-pods/pod-details-tolerations.scss deleted file mode 100644 index 42e5809440..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-tolerations.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetailsTolerations { - grid-template-columns: auto; - - .PodTolerations { - margin-top: var(--margin); - } - - // Expanding value cell to cover 2 columns (whole Drawer width) - - > .name { - grid-row-start: 1; - grid-column-start: 1; - } - - > .value { - grid-row-start: 1; - grid-column-start: 1; - } - - .DrawerParamToggler > .params { - margin-left: var(--drawer-item-title-width); - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx deleted file mode 100644 index bb97ecd5ad..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details-tolerations.scss"; -import React from "react"; -import { DrawerParamToggler, DrawerItem } from "../drawer"; -import type { Toleration, KubeObject } from "../../../common/k8s-api/kube-object"; -import { PodTolerations } from "./pod-tolerations"; - -export interface KubeObjectWithTolerations extends KubeObject { - getTolerations(): Toleration[]; -} - -export interface PodDetailsTolerationsProps { - workload: KubeObjectWithTolerations; -} - -export function PodDetailsTolerations({ workload }: PodDetailsTolerationsProps) { - const tolerations = workload.getTolerations(); - - if (!tolerations.length) return null; - - return ( - - - - - - ); -} diff --git a/src/renderer/components/+workloads-pods/pod-details.scss b/src/renderer/components/+workloads-pods/pod-details.scss deleted file mode 100644 index 68f308c15e..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details.scss +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodDetails { - .status { - @include pod-status-colors; - } - - .volume { - .title { - margin-top: $margin * 2; - margin-bottom: $margin; - font-weight: bold; - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx deleted file mode 100644 index 604cec5cd5..0000000000 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-details.scss"; - -import React from "react"; -import kebabCase from "lodash/kebabCase"; -import { observer } from "mobx-react"; -import { Link } from "react-router-dom"; -import { Pod } from "../../../common/k8s-api/endpoints"; -import type { NodeApi, PriorityClassApi, RuntimeClassApi, ServiceAccountApi } from "../../../common/k8s-api/endpoints"; -import { DrawerItem, DrawerTitle } from "../drawer"; -import { Badge } from "../badge"; -import { cssNames, stopPropagation, toJS } from "../../utils"; -import { PodDetailsContainer } from "./pod-details-container"; -import { PodDetailsAffinities } from "./pod-details-affinities"; -import { PodDetailsTolerations } from "./pod-details-tolerations"; -import { PodDetailsSecrets } from "./pod-details-secrets"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { getItemMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; -import type { Logger } from "../../../common/logger"; -import { PodVolumes } from "./details/volumes/view"; -import type { IAsyncComputed } from "@ogre-tools/injectable-react"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { GetDetailsUrl } 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 runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable"; -import serviceAccountApiInjectable from "../../../common/k8s-api/endpoints/service-account.api.injectable"; -import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import type { PodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics.injectable"; -import podContainerMetricsInjectable from "./container-metrics.injectable"; - -export interface PodDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - getDetailsUrl: GetDetailsUrl; - nodeApi: NodeApi; - priorityClassApi: PriorityClassApi; - runtimeClassApi: RuntimeClassApi; - serviceAccountApi: ServiceAccountApi; - logger: Logger; - containerMetrics: IAsyncComputed; -} - -@observer -class NonInjectedPodDetails extends React.Component { - render() { - const { object: pod, getDetailsUrl, nodeApi, logger, containerMetrics } = this.props; - - if (!pod) { - return null; - } - - if (!(pod instanceof Pod)) { - logger.error("[PodDetails]: passed object that is not an instanceof Pod", pod); - - return null; - } - - const { status, spec } = pod; - const { conditions = [], podIP } = status ?? {}; - const podIPs = pod.getIPs(); - const { nodeName } = spec ?? {}; - const nodeSelector = pod.getNodeSelectors(); - const initContainers = pod.getInitContainers(); - const containers = pod.getContainers(); - - const namespace = pod.getNs(); - const priorityClassName = pod.getPriorityClassName(); - const runtimeClassName = pod.getRuntimeClassName(); - const serviceAccountName = pod.getServiceAccountName(); - - const priorityClassDetailsUrl = getDetailsUrl(this.props.priorityClassApi.formatUrlForNotListing({ - name: priorityClassName, - })); - const runtimeClassDetailsUrl = getDetailsUrl(this.props.runtimeClassApi.formatUrlForNotListing({ - name: runtimeClassName, - })); - const serviceAccountDetailsUrl = getDetailsUrl(this.props.serviceAccountApi.formatUrlForNotListing({ - name: serviceAccountName, - namespace, - })); - - return ( -
- - - {pod.getStatusMessage()} - - - - - {podIP} - - - - - {serviceAccountName} - - - - - {pod.getQosClass()} - - - - - - - - - - - - - {initContainers.length > 0 && ( - <> - Init Containers - {initContainers.map(container => ( - - ))} - - )} - - Containers - {containers.map(container => ( - - ))} - - -
- ); - } -} - -export const PodDetails = withInjectables(NonInjectedPodDetails, { - getProps: (di, props) => ({ - ...props, - getDetailsUrl: di.inject(getDetailsUrlInjectable), - nodeApi: di.inject(nodeApiInjectable), - priorityClassApi: di.inject(priorityClassApiInjectable), - runtimeClassApi: di.inject(runtimeClassApiInjectable), - serviceAccountApi: di.inject(serviceAccountApiInjectable), - logger: di.inject(loggerInjectable), - containerMetrics: di.inject(podContainerMetricsInjectable, props.object), - }), -}); diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.scss b/src/renderer/components/+workloads-pods/pod-tolerations.scss deleted file mode 100644 index 1ea841906d..0000000000 --- a/src/renderer/components/+workloads-pods/pod-tolerations.scss +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PodTolerations { - .TableHead { - background-color: var(--drawerSubtitleBackground); - } - - .TableCell { - white-space: normal; - word-break: normal; - - &.key { - flex-grow: 3; - } - } -} diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-tolerations.tsx deleted file mode 100644 index 7573ccd846..0000000000 --- a/src/renderer/components/+workloads-pods/pod-tolerations.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pod-tolerations.scss"; -import React from "react"; -import uniqueId from "lodash/uniqueId"; - -import type { Toleration } from "../../../common/k8s-api/kube-object"; -import { Table, TableCell, TableHead, TableRow } from "../table"; - -export interface PodTolerationsProps { - tolerations: Toleration[]; -} - -enum sortBy { - Key = "key", - Operator = "operator", - Effect = "effect", - Seconds = "seconds", - Value = "value", -} - -const getTableRow = (toleration: Toleration) => { - const { key, operator, effect, tolerationSeconds, value } = toleration; - - return ( - - {key} - {operator} - {value} - {effect} - {tolerationSeconds} - - ); -}; - -export function PodTolerations({ tolerations }: PodTolerationsProps) { - return ( - toleration.key, - [sortBy.Operator]: toleration => toleration.operator, - [sortBy.Effect]: toleration => toleration.effect, - [sortBy.Seconds]: toleration => toleration.tolerationSeconds, - }} - sortSyncWithUrl={false} - className="PodTolerations" - renderRow={getTableRow} - > - - Key - Operator - Value - Effect - Seconds - -
- ); -} diff --git a/src/renderer/components/+workloads-pods/pods-route-component.injectable.ts b/src/renderer/components/+workloads-pods/pods-route-component.injectable.ts deleted file mode 100644 index 6ab45945cb..0000000000 --- a/src/renderer/components/+workloads-pods/pods-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Pods } from "./pods"; -import podsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const podsRouteComponentInjectable = getInjectable({ - id: "pods-route-component", - - instantiate: (di) => ({ - route: di.inject(podsRouteInjectable), - Component: Pods, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default podsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-pods/pods-sidebar-items.injectable.ts b/src/renderer/components/+workloads-pods/pods-sidebar-items.injectable.ts deleted file mode 100644 index 849db460e7..0000000000 --- a/src/renderer/components/+workloads-pods/pods-sidebar-items.injectable.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 podsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToPodsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable"; - -const podsSidebarItemsInjectable = getInjectable({ - id: "pods-sidebar-items", - - instantiate: (di) => { - const route = di.inject(podsRouteInjectable); - const navigateToPods = di.inject(navigateToPodsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "pods", - parentId: workloadsSidebarItemId, - title: "Pods", - onClick: navigateToPods, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 20, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default podsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-pods/pods.scss b/src/renderer/components/+workloads-pods/pods.scss deleted file mode 100644 index 3555c54701..0000000000 --- a/src/renderer/components/+workloads-pods/pods.scss +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@import "../+workloads/workloads-mixins"; - -.Pods { - .TableCell { - &.name { - flex-grow: 2; - } - - &.age { - flex-grow: 0.5; - } - - &.qos { - flex-grow: 0.8; - } - - &.warning { - @include table-cell-warning; - } - - &.containers { - .StatusBrick { - @include pod-status-bgs; - - &.running:not(.ready) { - background-color: $pod-status-pending-color; - } - } - } - - &.status { - @include pod-status-colors; - flex-grow: 0.7; - } - - - } -} diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx deleted file mode 100644 index ab793359b0..0000000000 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pods.scss"; - -import React, { Fragment } from "react"; -import { observer } from "mobx-react"; -import { Link } from "react-router-dom"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import type { NodeApi, Pod } from "../../../common/k8s-api/endpoints"; -import { StatusBrick } from "../status-brick"; -import { cssNames, getConvertedParts, object, stopPropagation } from "../../utils"; -import startCase from "lodash/startCase"; -import kebabCase from "lodash/kebabCase"; -import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { Badge } from "../badge"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; -import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; -import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; -import type { EventStore } from "../+events/store"; -import type { PodStore } from "./store"; -import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable"; -import eventStoreInjectable from "../+events/store.injectable"; -import podStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - containers = "containers", - restarts = "restarts", - age = "age", - qos = "qos", - node = "node", - owners = "owners", - status = "status", -} - -interface Dependencies { - getDetailsUrl: GetDetailsUrl; - apiManager: ApiManager; - eventStore: EventStore; - podStore: PodStore; - nodeApi: NodeApi; -} - -@observer -class NonInjectedPods extends React.Component { - renderState(name: string, ready: boolean, key: string, data: Partial> | undefined) { - return data && ( - <> -
- {name} - {" "} - - {key} - {ready ? ", ready" : ""} - -
- {object.entries(data).map(([name, value]) => ( -
-
- {startCase(name)} -
-
- {value} -
-
- ))} - - ); - } - - renderContainersStatus(pod: Pod) { - return pod.getContainerStatuses() - .map(({ name, state = {}, ready }) => ( - - - {this.renderState(name, ready, "running", state.running)} - {this.renderState(name, ready, "waiting", state.waiting)} - {this.renderState(name, ready, "terminated", state.terminated)} - - ), - }} - /> - - )); - } - - render() { - const { apiManager, getDetailsUrl, podStore, eventStore, nodeApi } = this.props; - - return ( - - getConvertedParts(pod.getName()), - [columnId.namespace]: pod => pod.getNs(), - [columnId.containers]: pod => pod.getContainerStatuses().length, - [columnId.restarts]: pod => pod.getRestartsCount(), - [columnId.owners]: pod => pod.getOwnerRefs().map(ref => ref.kind), - [columnId.qos]: pod => pod.getQosClass(), - [columnId.node]: pod => pod.getNodeName(), - [columnId.age]: pod => -pod.getCreationTimestamp(), - [columnId.status]: pod => pod.getStatusMessage(), - }} - searchFilters={[ - pod => pod.getSearchFields(), - pod => pod.getStatusMessage(), - pod => pod.status?.podIP, - pod => pod.getNodeName(), - ]} - renderHeaderTitle="Pods" - 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: "Containers", - className: "containers", - sortBy: columnId.containers, - id: columnId.containers, - }, - { - title: "Restarts", - className: "restarts", - sortBy: columnId.restarts, - id: columnId.restarts, - }, - { - title: "Controlled By", - className: "owners", - sortBy: columnId.owners, - id: columnId.owners, - }, - { title: "Node", className: "node", sortBy: columnId.node, id: columnId.node }, - { title: "QoS", className: "qos", sortBy: columnId.qos, id: columnId.qos }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, - ]} - renderTableContents={pod => [ - , - , - , - this.renderContainersStatus(pod), - pod.getRestartsCount(), - pod.getOwnerRefs().map(ref => { - const { kind, name } = ref; - const detailsLink = getDetailsUrl(apiManager.lookupApiLink(ref, pod)); - - return ( - - - {kind} - - - ); - }), - pod.getNodeName() ? ( - - - {pod.getNodeName()} - - - ) - : "", - pod.getQosClass(), - , - { title: pod.getStatusMessage(), className: kebabCase(pod.getStatusMessage()) }, - ]} - /> - - ); - } -} - -export const Pods = withInjectables(NonInjectedPods, { - getProps: (di, props) => ({ - ...props, - apiManager: di.inject(apiManagerInjectable), - getDetailsUrl: di.inject(getDetailsUrlInjectable), - nodeApi: di.inject(nodeApiInjectable), - eventStore: di.inject(eventStoreInjectable), - podStore: di.inject(podStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-pods/store.injectable.ts b/src/renderer/components/+workloads-pods/store.injectable.ts deleted file mode 100644 index 7b2bc23419..0000000000 --- a/src/renderer/components/+workloads-pods/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import { PodStore } from "./store"; -import podMetricsApiInjectable from "../../../common/k8s-api/endpoints/pod-metrics.api.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -const podStoreInjectable = getInjectable({ - id: "pod-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "podStore is only available in certain environements"); - - const api = di.inject(podApiInjectable); - - return new PodStore({ - podMetricsApi: di.inject(podMetricsApiInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default podStoreInjectable; diff --git a/src/renderer/components/+workloads-pods/store.ts b/src/renderer/components/+workloads-pods/store.ts deleted file mode 100644 index 5ce9dca041..0000000000 --- a/src/renderer/components/+workloads-pods/store.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import countBy from "lodash/countBy"; -import { observable } from "mobx"; -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import { cpuUnitsToNumber, unitsToBytes } from "../../utils"; -import type { Pod, PodMetrics, PodApi, PodMetricsApi } from "../../../common/k8s-api/endpoints"; -import type { KubeObject, NamespaceScopedMetadata } from "../../../common/k8s-api/kube-object"; - -export interface PodStoreDependencies extends KubeObjectStoreDependencies { - readonly podMetricsApi: PodMetricsApi; -} - -export class PodStore extends KubeObjectStore { - constructor( - protected readonly dependencies: PodStoreDependencies, - api: PodApi, - opts?: KubeObjectStoreOptions, - ) { - super(dependencies, api, opts); - } - - readonly kubeMetrics = observable.array([]); - - async loadKubeMetrics(namespace?: string) { - try { - const metrics = await this.dependencies.podMetricsApi.list({ namespace }); - - this.kubeMetrics.replace(metrics ?? []); - } catch (error) { - console.warn("loadKubeMetrics failed", error); - } - } - - getPodsByOwner(workload: KubeObject): Pod[] { - return this.items.filter(pod => ( - pod.getOwnerRefs() - .find(owner => owner.uid === workload.getId()) - )); - } - - getPodsByOwnerId(workloadId: string): Pod[] { - return this.items.filter(pod => { - return pod.getOwnerRefs().find(owner => owner.uid === workloadId); - }); - } - - getPodsByNode(node: string) { - if (!this.isLoaded) return []; - - return this.items.filter(pod => pod.spec.nodeName === node); - } - - getStatuses(pods: Pod[]) { - return countBy(pods.map(pod => pod.getStatus()).sort().reverse()); - } - - getPodKubeMetrics(pod: Pod) { - const containers = pod.getContainers(); - const empty = { cpu: 0, memory: 0 }; - const metrics = this.kubeMetrics.find(metric => { - return [ - metric.getName() === pod.getName(), - metric.getNs() === pod.getNs(), - ].every(v => v); - }); - - if (!metrics) return empty; - - return containers.reduce((total, container) => { - const metric = metrics.containers.find(item => item.name == container.name); - let cpu = "0"; - let memory = "0"; - - if (metric && metric.usage) { - cpu = metric.usage.cpu || "0"; - memory = metric.usage.memory || "0"; - } - - return { - cpu: total.cpu + (cpuUnitsToNumber(cpu) ?? 0), - memory: total.memory + unitsToBytes(memory), - }; - }, empty); - } -} diff --git a/src/renderer/components/+workloads-replicasets/index.ts b/src/renderer/components/+workloads-replicasets/index.ts deleted file mode 100644 index f89a481f8e..0000000000 --- a/src/renderer/components/+workloads-replicasets/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./replicasets"; -export * from "./replicaset-details"; diff --git a/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx deleted file mode 100644 index 9c57bea78e..0000000000 --- a/src/renderer/components/+workloads-replicasets/metrics-details-component.injectable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { type IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import type { ReplicaSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import replicaSetMetricsInjectable from "./metrics.injectable"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedReplicaSetMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const ReplicaSetMetricsDetailsComponent = withInjectables>(NonInjectedReplicaSetMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(replicaSetMetricsInjectable, props.object), - ...props, - }), -}); - -const replicaSetMetricsDetailsComponentInjectable = getInjectable({ - id: "replica-set-metrics-details-component", - instantiate: (di) => ({ - Component: ReplicaSetMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.ReplicaSet), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default replicaSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-replicasets/metrics.injectable.ts b/src/renderer/components/+workloads-replicasets/metrics.injectable.ts deleted file mode 100644 index 47a41bd253..0000000000 --- a/src/renderer/components/+workloads-replicasets/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsForReplicaSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-replica-sets.injectable"; - -const replicaSetMetricsInjectable = getInjectable({ - id: "replica-set-metrics", - instantiate: (di, replicaSet) => { - const requestPodMetricsForReplicaSets = di.inject(requestPodMetricsForReplicaSetsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: async () => { - now(60 * 1000); // update every minute - - return requestPodMetricsForReplicaSets([replicaSet], replicaSet.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, replicaSet: ReplicaSet) => replicaSet.getId(), - }), -}); - -export default replicaSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx b/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx deleted file mode 100644 index 93bd14c544..0000000000 --- a/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { OpenReplicaSetScaleDialog } from "./scale-dialog/open.injectable"; -import openReplicaSetScaleDialogInjectable from "./scale-dialog/open.injectable"; - -export interface ReplicaSetMenuProps extends KubeObjectMenuProps { - -} - -interface Dependencies { - openReplicaSetScaleDialog: OpenReplicaSetScaleDialog; -} - -const NonInjectedReplicaSetMenu = ({ - object, - toolbar, - openReplicaSetScaleDialog, -}: Dependencies & ReplicaSetMenuProps) => ( - <> - openReplicaSetScaleDialog(object)}> - - Scale - - -); - -export const ReplicaSetMenu = withInjectables(NonInjectedReplicaSetMenu, { - getProps: (di, props) => ({ - ...props, - openReplicaSetScaleDialog: di.inject(openReplicaSetScaleDialogInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.scss b/src/renderer/components/+workloads-replicasets/replicaset-details.scss deleted file mode 100644 index 2f0a7f74b5..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ReplicaSetDetails { -} diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx deleted file mode 100644 index 72e73c0726..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./replicaset-details.scss"; -import React from "react"; -import { DrawerItem } from "../drawer"; -import { Badge } from "../badge"; -import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; -import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; -import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; -import { disposeOnUnmount, observer } from "mobx-react"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import type { PodStore } from "../+workloads-pods/store"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { ReplicaSetStore } from "./store"; -import replicaSetStoreInjectable from "./store.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface ReplicaSetDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - podStore: PodStore; - replicaSetStore: ReplicaSetStore; - logger: Logger; -} - -@observer -class NonInjectedReplicaSetDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.podStore, - ]), - ]); - } - render() { - const { object: replicaSet, replicaSetStore, logger } = this.props; - - if (!replicaSet) { - return null; - } - - if (!(replicaSet instanceof ReplicaSet)) { - logger.error("[ReplicaSetDetails]: passed object that is not an instanceof ReplicaSet", replicaSet); - - return null; - } - - const { availableReplicas, replicas } = replicaSet.status ?? {}; - const selectors = replicaSet.getSelectors(); - const nodeSelector = replicaSet.getNodeSelectors(); - const images = replicaSet.getImages(); - const childPods = replicaSetStore.getChildPods(replicaSet); - - return ( -
- {selectors.length > 0 && ( - - { - selectors.map(label => ) - } - - )} - {nodeSelector.length > 0 && ( - - { - nodeSelector.map(label => ) - } - - )} - {images.length > 0 && ( - - { - images.map(image =>

{image}

) - } -
- )} - - {`${availableReplicas || 0} current / ${replicas || 0} desired`} - - - - - - - -
- ); - } -} - -export const ReplicaSetDetails = withInjectables(NonInjectedReplicaSetDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - podStore: di.inject(podStoreInjectable), - replicaSetStore: di.inject(replicaSetStoreInjectable), - logger: di.inject(loggerInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-replicasets/replicasets-route-component.injectable.ts b/src/renderer/components/+workloads-replicasets/replicasets-route-component.injectable.ts deleted file mode 100644 index 092d81f6d8..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicasets-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { ReplicaSets } from "./replicasets"; -import replicasetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const replicasetsRouteComponentInjectable = getInjectable({ - id: "replicasets-route-component", - - instantiate: (di) => ({ - route: di.inject(replicasetsRouteInjectable), - Component: ReplicaSets, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default replicasetsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-replicasets/replicasets-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-replicasets/replicasets-sidebar-items.injectable.tsx deleted file mode 100644 index 3fe3c0dca6..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicasets-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 replicasetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToReplicasetsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/replicasets/navigate-to-replicasets.injectable"; - -const replicasetsSidebarItemsInjectable = getInjectable({ - id: "replicasets-sidebar-items", - - instantiate: (di) => { - const route = di.inject(replicasetsRouteInjectable); - const navigateToReplicasets = di.inject(navigateToReplicasetsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "replica-sets", - parentId: workloadsSidebarItemId, - title: "ReplicaSets", - onClick: navigateToReplicasets, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 60, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default replicasetsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-replicasets/replicasets.scss b/src/renderer/components/+workloads-replicasets/replicasets.scss deleted file mode 100644 index 15274677e2..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicasets.scss +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ReplicaSets { - .TableCell { - &.name { - flex-grow: 2; - } - - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx deleted file mode 100644 index 92eb0c45ac..0000000000 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./replicasets.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { ReplicaSetStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import eventStoreInjectable from "../+events/store.injectable"; -import replicaSetStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - desired = "desired", - current = "current", - ready = "ready", - age = "age", -} - -interface Dependencies { - replicaSetStore: ReplicaSetStore; - eventStore: EventStore; -} - -const NonInjectedReplicaSets = observer((props: Dependencies) => { - const { - eventStore, - replicaSetStore, - } = props; - - return ( - - replicaSet.getName(), - [columnId.namespace]: replicaSet => replicaSet.getNs(), - [columnId.desired]: replicaSet => replicaSet.getDesired(), - [columnId.current]: replicaSet => replicaSet.getCurrent(), - [columnId.ready]: replicaSet => replicaSet.getReady(), - [columnId.age]: replicaSet => -replicaSet.getCreationTimestamp(), - }} - searchFilters={[ - replicaSet => replicaSet.getSearchFields(), - ]} - renderHeaderTitle="Replica Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { className: "warning", showWithColumn: columnId.name }, - { - title: "Namespace", - className: "namespace", - sortBy: columnId.namespace, - id: columnId.namespace, - }, - { - title: "Desired", - className: "desired", - sortBy: columnId.desired, - id: columnId.desired, - }, - { - title: "Current", - className: "current", - sortBy: columnId.current, - id: columnId.current, - }, - { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={replicaSet => [ - replicaSet.getName(), - , - , - replicaSet.getDesired(), - replicaSet.getCurrent(), - replicaSet.getReady(), - , - ]} - /> - - ); -}); - -export const ReplicaSets = withInjectables(NonInjectedReplicaSets, { - getProps: (di, props) => ({ - ...props, - eventStore: di.inject(eventStoreInjectable), - replicaSetStore: di.inject(replicaSetStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.scss b/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.scss deleted file mode 100644 index 266ccffaa7..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.scss +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ReplicaSetScaleDialog { - .Wizard { - .header { - span { - color: #a0a0a0; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - .WizardStep { - .step-content { - min-height: 90px; - overflow: hidden; - } - } - - .current-scale { - font-weight: bold - } - - .desired-scale { - flex: 1.1 0; - } - - .slider-container { - flex: 1 0; - } - - .plus-minus-container { - margin-left: $margin * 2; - .Icon { - --color-active: black; - } - } - - .warning { - color: var(--colorSoftError); - font-size: small; - display: flex; - align-items: center; - - .Icon { - margin: 0; - margin-right: $margin; - } - } - } -} diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.test.tsx b/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.test.tsx deleted file mode 100755 index d1e2aefd66..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.test.tsx +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "@testing-library/jest-dom/extend-expect"; - -import { ReplicaSetScaleDialog } from "./dialog"; -import { waitFor, fireEvent } from "@testing-library/react"; -import React from "react"; -import type { ReplicaSetApi } from "../../../../common/k8s-api/endpoints/replica-set.api"; -import { ReplicaSet } from "../../../../common/k8s-api/endpoints/replica-set.api"; -import type { OpenReplicaSetScaleDialog } from "./open.injectable"; -import replicaSetApiInjectable from "../../../../common/k8s-api/endpoints/replica-set.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import { type DiRender, renderFor } from "../../test-utils/renderFor"; -import openReplicaSetScaleDialogInjectable from "./open.injectable"; - -const dummyReplicaSet = new ReplicaSet({ - apiVersion: "v1", - kind: "dummy", - metadata: { - uid: "dummy", - name: "dummy", - creationTimestamp: "dummy", - resourceVersion: "dummy", - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/dummy", - }, - spec: { - replicas: 1, - selector: { - matchLabels: { "label": "label" }, - }, - template: { - metadata: { - labels: { - app: "label", - }, - }, - spec: { - containers: [{ - name: "dummy", - image: "dummy", - imagePullPolicy: "Always", - }], - initContainers: [{ - name: "dummy", - image: "dummy", - imagePullPolicy: "Always", - }], - priority: 1, - serviceAccountName: "dummy", - serviceAccount: "dummy", - securityContext: {}, - schedulerName: "dummy", - }, - }, - minReadySeconds: 1, - }, - status: { - replicas: 1, - fullyLabeledReplicas: 1, - readyReplicas: 1, - availableReplicas: 1, - observedGeneration: 1, - conditions: [{ - type: "dummy", - status: "True", - lastTransitionTime: "dummy", - reason: "dummy", - message: "dummy", - }], - }, -}); - -describe("", () => { - let replicaSetApi: ReplicaSetApi; - let openReplicaSetScaleDialog: OpenReplicaSetScaleDialog; - let render: DiRender; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - render = renderFor(di); - replicaSetApi = di.inject(replicaSetApiInjectable); - openReplicaSetScaleDialog = di.inject(openReplicaSetScaleDialogInjectable); - }); - - it("renders w/o errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("init with a dummy replica set and mocked current/desired scale", async () => { - // mock replicaSetApi.getReplicas() which will be called - // when rendered. - const initReplicas = 1; - - replicaSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const { getByTestId } = render(); - - openReplicaSetScaleDialog(dummyReplicaSet); - // we need to wait for the replicaSetScaleDialog to show up - // because there is an in which renders null at start. - await waitFor(async () => { - const [currentScale, desiredScale] = await Promise.all([ - getByTestId("current-scale"), - getByTestId("desired-scale"), - ]); - - expect(currentScale).toHaveTextContent(`${initReplicas}`); - expect(desiredScale).toHaveTextContent(`${initReplicas}`); - }); - }); - - it("changes the desired scale when clicking the icon buttons +/-", async () => { - const initReplicas = 1; - - replicaSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const component = render(); - - openReplicaSetScaleDialog(dummyReplicaSet); - await waitFor(async () => { - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas}`); - }); - - const up = await component.findByTestId("desired-replicas-up"); - const down = await component.findByTestId("desired-replicas-down"); - - fireEvent.click(up); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas + 1}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas + 1}`); - - fireEvent.click(down); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas}`); - - // edge case, desiredScale must >= 0 - let times = 10; - - for (let i = 0; i < times; i++) { - fireEvent.click(down); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("0"); - expect((component.baseElement.querySelector("input")?.value)).toBe("0"); - - // edge case, desiredScale must <= scaleMax (100) - times = 120; - - for (let i = 0; i < times; i++) { - fireEvent.click(up); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("100"); - expect((component.baseElement.querySelector("input")?.value)).toBe("100"); - expect(await component.findByTestId("warning")) - .toHaveTextContent("High number of replicas may cause cluster performance issues"); - }); -}); diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx b/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx deleted file mode 100644 index 9999917bdb..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/dialog.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./dialog.scss"; - -import React, { Component } from "react"; -import type { IObservableValue } from "mobx"; -import { computed, observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import { Icon } from "../../icon"; -import { Slider } from "../../slider"; -import { cssNames } from "../../../utils"; -import type { ReplicaSet, ReplicaSetApi } from "../../../../common/k8s-api/endpoints"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import replicaSetApiInjectable from "../../../../common/k8s-api/endpoints/replica-set.api.injectable"; -import replicaSetScaleDialogStateInjectable from "./state.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface ReplicaSetScaleDialogProps extends Partial { -} - -interface Dependencies { - replicaSetApi: ReplicaSetApi; - state: IObservableValue; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedReplicaSetScaleDialog extends Component { - @observable ready = false; - @observable currentReplicas = 0; - @observable desiredReplicas = 0; - - constructor(props: ReplicaSetScaleDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - close = () => { - this.props.state.set(undefined); - }; - - onOpen = async (replicaSet: ReplicaSet) => { - this.currentReplicas = await this.props.replicaSetApi.getReplicas({ - namespace: replicaSet.getNs(), - name: replicaSet.getName(), - }); - this.desiredReplicas = this.currentReplicas; - this.ready = true; - }; - - onClose = () => { - this.ready = false; - }; - - onChange = (evt: React.ChangeEvent, value: number) => { - this.desiredReplicas = value; - }; - - @computed get scaleMax() { - const { currentReplicas } = this; - const defaultMax = 50; - - return currentReplicas <= defaultMax - ? defaultMax * 2 - : currentReplicas * 2; - } - - scale = async (replicaSet: ReplicaSet) => { - const { currentReplicas, desiredReplicas, close } = this; - - try { - if (currentReplicas !== desiredReplicas) { - await this.props.replicaSetApi.scale({ - name: replicaSet.getName(), - namespace: replicaSet.getNs(), - }, desiredReplicas); - } - close(); - } catch (err) { - this.props.showCheckedErrorNotification(err, "Unknown error occured while scaling ReplicaSet"); - } - }; - - private readonly scaleMin = 0; - - desiredReplicasUp = () => { - this.desiredReplicas = Math.min(this.scaleMax, this.desiredReplicas + 1); - }; - - desiredReplicasDown = () => { - this.desiredReplicas = Math.max(this.scaleMin, this.desiredReplicas - 1); - }; - - renderContents(replicaSet: ReplicaSet) { - const { currentReplicas, desiredReplicas, onChange, scaleMax } = this; - const warning = currentReplicas < 10 && desiredReplicas > 90; - - return ( - - {"Scale Replica Set "} - {replicaSet.getName()} - - )} - done={this.close} - > - this.scale(replicaSet)} - nextLabel="Scale" - disabledNext={!this.ready} - > -
- {`Current replica scale: ${currentReplicas}`} -
-
-
- {`Desired number of replicas: ${desiredReplicas}`} -
-
- -
-
- - -
-
- {warning && ( -
- - High number of replicas may cause cluster performance issues -
- )} -
-
- ); - } - - render() { - const { className, state, ...dialogProps } = this.props; - const replicaSet = state.get(); - - return ( - this.onOpen(replicaSet))} - onClose={this.onClose} - close={this.close} - > - {replicaSet && this.renderContents(replicaSet)} - - ); - } -} - -export const ReplicaSetScaleDialog = withInjectables(NonInjectedReplicaSetScaleDialog, { - getProps: (di, props) => ({ - ...props, - replicaSetApi: di.inject(replicaSetApiInjectable), - state: di.inject(replicaSetScaleDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/open.injectable.ts b/src/renderer/components/+workloads-replicasets/scale-dialog/open.injectable.ts deleted file mode 100644 index 4075a3e35f..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/open.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 { ReplicaSet } from "../../../../common/k8s-api/endpoints"; -import replicaSetScaleDialogStateInjectable from "./state.injectable"; - -export type OpenReplicaSetScaleDialog = (obj: ReplicaSet) => void; - -const openReplicaSetScaleDialogInjectable = getInjectable({ - id: "open-replica-set-scale-dialog", - instantiate: (di): OpenReplicaSetScaleDialog => { - const state = di.inject(replicaSetScaleDialogStateInjectable); - - return (obj) => state.set(obj); - }, -}); - -export default openReplicaSetScaleDialogInjectable; diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts deleted file mode 100644 index a6f2d8c8f5..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; -import { ReplicaSetScaleDialog } from "./dialog"; - -const replicasetScaleDialogClusterFrameChildComponentInjectable = getInjectable({ - id: "replicaset-scale-dialog-cluster-frame-child-component", - - instantiate: () => ({ - id: "replicaset-scale-dialog", - shouldRender: computed(() => true), - Component: ReplicaSetScaleDialog, - }), - - injectionToken: clusterFrameChildComponentInjectionToken, -}); - -export default replicasetScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/state.injectable.ts b/src/renderer/components/+workloads-replicasets/scale-dialog/state.injectable.ts deleted file mode 100644 index 9bd8316bec..0000000000 --- a/src/renderer/components/+workloads-replicasets/scale-dialog/state.injectable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { ReplicaSet } from "../../../../common/k8s-api/endpoints"; - -const replicaSetScaleDialogStateInjectable = getInjectable({ - id: "replica-set-scale-dialog-state", - instantiate: () => observable.box(), -}); - -export default replicaSetScaleDialogStateInjectable; diff --git a/src/renderer/components/+workloads-replicasets/store.injectable.ts b/src/renderer/components/+workloads-replicasets/store.injectable.ts deleted file mode 100644 index 34887a0590..0000000000 --- a/src/renderer/components/+workloads-replicasets/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import replicaSetApiInjectable from "../../../common/k8s-api/endpoints/replica-set.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import { ReplicaSetStore } from "./store"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -const replicaSetStoreInjectable = getInjectable({ - id: "replica-set-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "replicaSetStore is only available in certain environments"); - - const api = di.inject(replicaSetApiInjectable); - - return new ReplicaSetStore({ - getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default replicaSetStoreInjectable; diff --git a/src/renderer/components/+workloads-replicasets/store.ts b/src/renderer/components/+workloads-replicasets/store.ts deleted file mode 100644 index 1a6ac96610..0000000000 --- a/src/renderer/components/+workloads-replicasets/store.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import type { Deployment, ReplicaSet, ReplicaSetApi } from "../../../common/k8s-api/endpoints"; -import { PodStatusPhase } from "../../../common/k8s-api/endpoints/pod.api"; -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; - -export interface ReplicaSetStoreDependencies extends KubeObjectStoreDependencies { - getPodsByOwnerId: GetPodsByOwnerId; -} - -export class ReplicaSetStore extends KubeObjectStore { - constructor(protected readonly dependencies: ReplicaSetStoreDependencies, api: ReplicaSetApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - getChildPods(replicaSet: ReplicaSet) { - return this.dependencies.getPodsByOwnerId(replicaSet.getId()); - } - - getStatuses(replicaSets: ReplicaSet[]) { - const status = { running: 0, failed: 0, pending: 0 }; - - for (const replicaSet of replicaSets) { - const statuses = new Set(this.getChildPods(replicaSet).map(pod => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - } - - return status; - } - - getReplicaSetsByOwner(deployment: Deployment) { - return this.items.filter(replicaSet => - !!replicaSet.getOwnerRefs().find(owner => owner.uid === deployment.getId()), - ); - } -} diff --git a/src/renderer/components/+workloads-statefulsets/index.ts b/src/renderer/components/+workloads-statefulsets/index.ts deleted file mode 100644 index 788a5a7ff2..0000000000 --- a/src/renderer/components/+workloads-statefulsets/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./statefulsets"; -export * from "./statefulset-details"; diff --git a/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx b/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx deleted file mode 100644 index ad4bb6d677..0000000000 --- a/src/renderer/components/+workloads-statefulsets/metrics-details-component.injectable.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { type IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; -import React from "react"; -import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { ClusterMetricsResourceType } from "../../../common/cluster-types"; -import type { StatefulSet } from "../../../common/k8s-api/endpoints"; -import type { StatefulSetPodMetricData } from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; -import metricsDetailsComponentEnabledInjectable from "../../api/catalog/entity/metrics-details-component-enabled.injectable"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { kubeObjectDetailItemInjectionToken } from "../kube-object-details/kube-object-detail-items/kube-object-detail-item-injection-token"; -import { ResourceMetrics } from "../resource-metrics"; -import statefulSetMetricsInjectable from "./metrics.injectable"; - -interface Dependencies { - metrics: IAsyncComputed; -} - -const NonInjectedStatefulSetMetricsDetailsComponent = ({ - object, - metrics, -}: KubeObjectDetailsProps & Dependencies) => ( - - - -); - -const StatefulSetMetricsDetailsComponent = withInjectables>(NonInjectedStatefulSetMetricsDetailsComponent, { - getProps: (di, props) => ({ - metrics: di.inject(statefulSetMetricsInjectable, props.object), - ...props, - }), -}); - -const statefulSetMetricsDetailsComponentInjectable = getInjectable({ - id: "stateful-set-metrics-details-component", - instantiate: (di) => ({ - Component: StatefulSetMetricsDetailsComponent, - enabled: di.inject(metricsDetailsComponentEnabledInjectable, ClusterMetricsResourceType.StatefulSet), - orderNumber: -1, - }), - injectionToken: kubeObjectDetailItemInjectionToken, -}); - -export default statefulSetMetricsDetailsComponentInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts b/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts deleted file mode 100644 index b12cd152e9..0000000000 --- a/src/renderer/components/+workloads-statefulsets/metrics.injectable.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { asyncComputed } from "@ogre-tools/injectable-react"; -import { now } from "mobx-utils"; -import type { StatefulSet } from "../../../common/k8s-api/endpoints"; -import requestPodMetricsForStatefulSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-stateful-sets.injectable"; - -const statefulSetMetricsInjectable = getInjectable({ - id: "stateful-set-metrics", - instantiate: (di, statefulSet) => { - const requestPodMetricsForStatefulSets = di.inject(requestPodMetricsForStatefulSetsInjectable); - - return asyncComputed({ - getValueFromObservedPromise: async () => { - now(60 * 1000); - - return requestPodMetricsForStatefulSets([statefulSet], statefulSet.getNs()); - }, - }); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, statefulSet: StatefulSet) => statefulSet.getId(), - }), -}); - -export default statefulSetMetricsInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/scale/dialog-state.injectable.ts b/src/renderer/components/+workloads-statefulsets/scale/dialog-state.injectable.ts deleted file mode 100644 index 8d8634a6bf..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/dialog-state.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { observable } from "mobx"; -import type { StatefulSet } from "../../../../common/k8s-api/endpoints"; - -const statefulSetDialogStateInjectable = getInjectable({ - id: "stateful-set-dialog-state", - instantiate: () => observable.box(), -}); - -export default statefulSetDialogStateInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/scale/dialog.scss b/src/renderer/components/+workloads-statefulsets/scale/dialog.scss deleted file mode 100644 index c4f3037031..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/dialog.scss +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.StatefulSetScaleDialog { - .Wizard { - .header { - span { - color: #a0a0a0; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - .WizardStep { - .step-content { - min-height: 90px; - overflow: hidden; - } - } - - .current-scale { - font-weight: bold - } - - .desired-scale { - flex: 1.1 0; - } - - .slider-container { - flex: 1 0; - } - - .plus-minus-container { - margin-left: $margin * 2; - .Icon { - --color-active: black; - } - } - - .warning { - color: var(--colorSoftError); - font-size: small; - display: flex; - align-items: center; - - .Icon { - margin: 0; - margin-right: $margin; - } - } - } -} diff --git a/src/renderer/components/+workloads-statefulsets/scale/dialog.test.tsx b/src/renderer/components/+workloads-statefulsets/scale/dialog.test.tsx deleted file mode 100755 index 5d09b032e3..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/dialog.test.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "@testing-library/jest-dom/extend-expect"; - -import type { StatefulSetApi } from "../../../../common/k8s-api/endpoints"; -import { StatefulSet } from "../../../../common/k8s-api/endpoints"; -import { StatefulSetScaleDialog } from "./dialog"; -import { waitFor, fireEvent } from "@testing-library/react"; -import React from "react"; -import type { OpenStatefulSetScaleDialog } from "./open-dialog.injectable"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; -import openStatefulSetScaleDialogInjectable from "./open-dialog.injectable"; -import { type DiRender, renderFor } from "../../test-utils/renderFor"; - -const dummyStatefulSet = new StatefulSet({ - apiVersion: "v1", - kind: "dummy", - metadata: { - uid: "dummy", - name: "dummy", - creationTimestamp: "dummy", - resourceVersion: "dummy", - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/dummy", - }, - spec: { - serviceName: "dummy", - replicas: 1, - selector: { - matchLabels: { "label": "label" }, - }, - template: { - metadata: { - labels: { - app: "app", - }, - }, - spec: { - containers: [{ - name: "dummy", - image: "dummy", - ports: [{ - containerPort: 1234, - name: "dummy", - }], - volumeMounts: [{ - name: "dummy", - mountPath: "dummy", - }], - }], - tolerations: [{ - key: "dummy", - operator: "dummy", - effect: "dummy", - tolerationSeconds: 1, - }], - }, - }, - volumeClaimTemplates: [{ - metadata: { - name: "dummy", - }, - spec: { - accessModes: ["dummy"], - resources: { - requests: { - storage: "dummy", - }, - }, - }, - }], - }, - status: { - observedGeneration: 1, - replicas: 1, - currentReplicas: 1, - readyReplicas: 1, - currentRevision: "dummy", - updateRevision: "dummy", - collisionCount: 1, - }, -}); - -describe("", () => { - let statefulSetApi: StatefulSetApi; - let openStatefulSetDialog: OpenStatefulSetScaleDialog; - let render: DiRender; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - render = renderFor(di); - statefulSetApi = di.inject(statefulSetApiInjectable); - openStatefulSetDialog = di.inject(openStatefulSetScaleDialogInjectable); - }); - - it("renders w/o errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("init with a dummy stateful set and mocked current/desired scale", async () => { - // mock statefulSetApi.getReplicas() which will be called - // when rendered. - const initReplicas = 1; - - statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const { getByTestId } = render(); - - openStatefulSetDialog(dummyStatefulSet); - // we need to wait for the StatefulSetScaleDialog to show up - // because there is an in which renders null at start. - await waitFor(async () => { - const [currentScale, desiredScale] = await Promise.all([ - getByTestId("current-scale"), - getByTestId("desired-scale"), - ]); - - expect(currentScale).toHaveTextContent(`${initReplicas}`); - expect(desiredScale).toHaveTextContent(`${initReplicas}`); - }); - }); - - it("changes the desired scale when clicking the icon buttons +/-", async () => { - const initReplicas = 1; - - statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const component = render(); - - openStatefulSetDialog(dummyStatefulSet); - await waitFor(async () => { - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas}`); - }); - - const up = await component.findByTestId("desired-replicas-up"); - const down = await component.findByTestId("desired-replicas-down"); - - fireEvent.click(up); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas + 1}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas + 1}`); - - fireEvent.click(down); - expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); - expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); - expect((component.baseElement.querySelector("input")?.value)).toBe(`${initReplicas}`); - - // edge case, desiredScale must >= 0 - let times = 10; - - for (let i = 0; i < times; i++) { - fireEvent.click(down); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("0"); - expect((component.baseElement.querySelector("input")?.value)).toBe("0"); - - // edge case, desiredScale must <= scaleMax (100) - times = 120; - - for (let i = 0; i < times; i++) { - fireEvent.click(up); - } - expect(await component.findByTestId("desired-scale")).toHaveTextContent("100"); - expect((component.baseElement.querySelector("input")?.value)).toBe("100"); - expect(await component.findByTestId("warning")) - .toHaveTextContent("High number of replicas may cause cluster performance issues"); - }); -}); diff --git a/src/renderer/components/+workloads-statefulsets/scale/dialog.tsx b/src/renderer/components/+workloads-statefulsets/scale/dialog.tsx deleted file mode 100644 index 39d8969e03..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/dialog.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./dialog.scss"; - -import type { StatefulSet, StatefulSetApi } from "../../../../common/k8s-api/endpoints"; -import React, { Component } from "react"; -import type { IObservableValue } from "mobx"; -import { computed, makeObservable, observable } from "mobx"; -import { observer } from "mobx-react"; -import type { DialogProps } from "../../dialog"; -import { Dialog } from "../../dialog"; -import { Wizard, WizardStep } from "../../wizard"; -import { Icon } from "../../icon"; -import { Slider } from "../../slider"; -import { cssNames } from "../../../utils"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable"; -import statefulSetDialogStateInjectable from "./dialog-state.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface StatefulSetScaleDialogProps extends Partial { -} - -interface Dependencies { - statefulSetApi: StatefulSetApi; - state: IObservableValue; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedStatefulSetScaleDialog extends Component { - @observable ready = false; - @observable currentReplicas = 0; - @observable desiredReplicas = 0; - - constructor(props: StatefulSetScaleDialogProps & Dependencies) { - super(props); - makeObservable(this); - } - - close = () => { - this.props.state.set(undefined); - }; - - onOpen = async (statefulSet: StatefulSet) => { - this.currentReplicas = await this.props.statefulSetApi.getReplicas({ - namespace: statefulSet.getNs(), - name: statefulSet.getName(), - }); - this.desiredReplicas = this.currentReplicas; - this.ready = true; - }; - - onClose = () => { - this.ready = false; - }; - - onChange = (evt: React.ChangeEvent, value: number) => { - this.desiredReplicas = value; - }; - - @computed get scaleMax() { - const { currentReplicas } = this; - const defaultMax = 50; - - return currentReplicas <= defaultMax - ? defaultMax * 2 - : currentReplicas * 2; - } - - scale = async (statefulSet: StatefulSet) => { - const { currentReplicas, desiredReplicas, close } = this; - - try { - if (currentReplicas !== desiredReplicas) { - await this.props.statefulSetApi.scale({ - name: statefulSet.getName(), - namespace: statefulSet.getNs(), - }, desiredReplicas); - } - close(); - } catch (error) { - this.props.showCheckedErrorNotification(error, "Unknown error occured while scaling StatefulSet"); - } - }; - - private readonly scaleMin = 0; - - desiredReplicasUp = () => { - this.desiredReplicas = Math.min(this.scaleMax, this.desiredReplicas + 1); - }; - - desiredReplicasDown = () => { - this.desiredReplicas = Math.max(this.scaleMin, this.desiredReplicas - 1); - }; - - renderContents(statefulSet: StatefulSet) { - const { currentReplicas, desiredReplicas, onChange, scaleMax } = this; - const warning = currentReplicas < 10 && desiredReplicas > 90; - - return ( - - Scale Stateful Set - {statefulSet.getName()} - - )} - done={this.close} - > - this.scale(statefulSet)} - nextLabel="Scale" - disabledNext={!this.ready} - > -
- {`Current replica scale: ${currentReplicas}`} -
-
-
- {`Desired number of replicas: ${desiredReplicas}`} -
-
- -
-
- - -
-
- {warning && ( -
- - High number of replicas may cause cluster performance issues -
- )} -
-
- ); - } - - render() { - const { className, state, ...dialogProps } = this.props; - const statefulSet = state.get(); - - return ( - this.onOpen(statefulSet))} - onClose={this.onClose} - close={this.close} - > - {statefulSet && this.renderContents(statefulSet)} - - ); - } -} - -export const StatefulSetScaleDialog = withInjectables(NonInjectedStatefulSetScaleDialog, { - getProps: (di, props) => ({ - ...props, - statefulSetApi: di.inject(statefulSetApiInjectable), - state: di.inject(statefulSetDialogStateInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-statefulsets/scale/open-dialog.injectable.ts b/src/renderer/components/+workloads-statefulsets/scale/open-dialog.injectable.ts deleted file mode 100644 index 8f213a9a18..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/open-dialog.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { StatefulSet } from "../../../../common/k8s-api/endpoints"; -import { getInjectable } from "@ogre-tools/injectable"; -import statefulSetDialogStateInjectable from "./dialog-state.injectable"; - -export type OpenStatefulSetScaleDialog = (obj: StatefulSet) => void; - -const openStatefulSetScaleDialogInjectable = getInjectable({ - id: "open-stateful-set-scale-dialog", - instantiate: (di): OpenStatefulSetScaleDialog => { - const state = di.inject(statefulSetDialogStateInjectable); - - return (obj) => state.set(obj); - }, -}); - -export default openStatefulSetScaleDialogInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts deleted file mode 100644 index 7a8b12686d..0000000000 --- a/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { StatefulSetScaleDialog } from "./dialog"; -import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; - -const statefulsetScaleDialogClusterFrameChildComponentInjectable = getInjectable({ - id: "statefulset-scale-dialog-cluster-frame-child-component", - - instantiate: () => ({ - id: "statefulset-scale-dialog", - shouldRender: computed(() => true), - Component: StatefulSetScaleDialog, - }), - - injectionToken: clusterFrameChildComponentInjectionToken, -}); - -export default statefulsetScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.scss b/src/renderer/components/+workloads-statefulsets/statefulset-details.scss deleted file mode 100644 index 0c04bb9fb3..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.StatefulSetDetails { -} diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx deleted file mode 100644 index 5c9eff2e84..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./statefulset-details.scss"; - -import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { Badge } from "../badge"; -import { DrawerItem } from "../drawer"; -import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; -import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; -import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; -import type { StatefulSetStore } from "./store"; -import type { KubeObjectDetailsProps } from "../kube-object-details"; -import { StatefulSet } from "../../../common/k8s-api/endpoints"; -import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import type { Logger } from "../../../common/logger"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; -import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import type { PodStore } from "../+workloads-pods/store"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import statefulSetStoreInjectable from "./store.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; - -export interface StatefulSetDetailsProps extends KubeObjectDetailsProps { -} - -interface Dependencies { - subscribeStores: SubscribeStores; - podStore: PodStore; - statefulSetStore: StatefulSetStore; - logger: Logger; -} - -@observer -class NonInjectedStatefulSetDetails extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.subscribeStores([ - this.props.podStore, - ]), - ]); - } - - render() { - const { object: statefulSet, statefulSetStore, logger } = this.props; - - if (!statefulSet) { - return null; - } - - if (!(statefulSet instanceof StatefulSet)) { - logger.error("[StatefulSetDetails]: passed object that is not an instanceof StatefulSet", statefulSet); - - return null; - } - - const images = statefulSet.getImages(); - const selectors = statefulSet.getSelectors(); - const nodeSelector = statefulSet.getNodeSelectors(); - const childPods = statefulSetStore.getChildPods(statefulSet); - - return ( -
- {selectors.length && ( - - { - selectors.map(label => ) - } - - )} - {nodeSelector.length > 0 && ( - - { - nodeSelector.map(label => ( - - )) - } - - )} - {images.length > 0 && ( - - { - images.map(image =>

{image}

) - } -
- )} - - - - - - -
- ); - } -} - -export const StatefulSetDetails = withInjectables(NonInjectedStatefulSetDetails, { - getProps: (di, props) => ({ - ...props, - subscribeStores: di.inject(subscribeStoresInjectable), - podStore: di.inject(podStoreInjectable), - statefulSetStore: di.inject(statefulSetStoreInjectable), - logger: di.inject(loggerInjectable), - }), -}); - diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-menu.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-menu.tsx deleted file mode 100644 index 942c9674e6..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulset-menu.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import type { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import statefulSetApiInjectable from "../../../common/k8s-api/endpoints/stateful-set.api.injectable"; -import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; -import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; -import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; - -export interface StatefulSetMenuProps extends KubeObjectMenuProps {} - -interface Dependencies { - statefulsetApi: StatefulSetApi; - openConfirmDialog: OpenConfirmDialog; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -const NonInjectedStatefulSetMenu = ({ - statefulsetApi, - object, - toolbar, - showCheckedErrorNotification, - openConfirmDialog, -}: Dependencies & StatefulSetMenuProps) => ( - <> - openConfirmDialog({ - ok: async () => - { - try { - await statefulsetApi.restart({ - namespace: object.getNs(), - name: object.getName(), - }); - } catch (err) { - showCheckedErrorNotification(err, "Unknown error occured while restarting statefulset"); - } - }, - labelOk: "Restart", - message: ( -

- {"Are you sure you want to restart statefulset "} - {object.getName()} - ? -

- ), - })} - > - - Restart -
- -); - -export const StatefulSetMenu = withInjectables(NonInjectedStatefulSetMenu, { - getProps: (di, props) => ({ - ...props, - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - statefulsetApi: di.inject(statefulSetApiInjectable), - openConfirmDialog: di.inject(openConfirmDialogInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets-route-component.injectable.ts b/src/renderer/components/+workloads-statefulsets/statefulsets-route-component.injectable.ts deleted file mode 100644 index 0f461e4735..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulsets-route-component.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { StatefulSets } from "./statefulsets"; -import statefulsetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable"; -import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; - -const statefulsetsRouteComponentInjectable = getInjectable({ - id: "statefulsets-route-component", - - instantiate: (di) => ({ - route: di.inject(statefulsetsRouteInjectable), - Component: StatefulSets, - }), - - injectionToken: routeSpecificComponentInjectionToken, -}); - -export default statefulsetsRouteComponentInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets-sidebar-items.injectable.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets-sidebar-items.injectable.tsx deleted file mode 100644 index db78a8eac8..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulsets-sidebar-items.injectable.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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 statefulsetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable"; -import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; -import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToStatefulsetsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable"; - -const statefulsetsSidebarItemsInjectable = getInjectable({ - id: "statefulsets-sidebar-items", - - instantiate: (di) => { - const route = di.inject(statefulsetsRouteInjectable); - const navigateToStatefulsets = di.inject(navigateToStatefulsetsInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); - - return computed(() => [ - { - id: "stateful-sets", - parentId: workloadsSidebarItemId, - title: "StatefulSets", - onClick: navigateToStatefulsets, - isActive: routeIsActive, - isVisible: route.isEnabled, - orderNumber: 50, - }, - ]); - }, - - injectionToken: sidebarItemsInjectionToken, -}); - -export default statefulsetsSidebarItemsInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.scss b/src/renderer/components/+workloads-statefulsets/statefulsets.scss deleted file mode 100644 index ce9277c6bb..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.scss +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.StatefulSets { - .TableCell { - &.name { - flex-grow: 2; - } - - &.pods { - flex-grow: 1; - } - - &.warning { - @include table-cell-warning; - } - - - } -} diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx deleted file mode 100644 index 0e35cd6584..0000000000 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./statefulsets.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import type { StatefulSet } from "../../../common/k8s-api/endpoints"; -import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; -import { KubeObjectAge } from "../kube-object/age"; -import type { StatefulSetStore } from "./store"; -import type { EventStore } from "../+events/store"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import eventStoreInjectable from "../+events/store.injectable"; -import statefulSetStoreInjectable from "./store.injectable"; -import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; - -enum columnId { - name = "name", - namespace = "namespace", - pods = "pods", - age = "age", - replicas = "replicas", -} - -interface Dependencies { - statefulSetStore: StatefulSetStore; - eventStore: EventStore; -} - -const renderPodCounts = (statefulSet: StatefulSet) => { - const { readyReplicas, currentReplicas } = statefulSet.status ?? {}; - - return `${readyReplicas || 0}/${currentReplicas || 0}`; -}; - -const NonInjectedStatefulSets = observer((props: Dependencies) => { - const { - eventStore, - statefulSetStore, - } = props; - - return ( - - statefulSet.getName(), - [columnId.namespace]: statefulSet => statefulSet.getNs(), - [columnId.age]: statefulSet => -statefulSet.getCreationTimestamp(), - [columnId.replicas]: statefulSet => statefulSet.getReplicas(), - }} - searchFilters={[ - statefulSet => statefulSet.getSearchFields(), - ]} - renderHeaderTitle="Stateful Sets" - renderTableHeader={[ - { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, - { - title: "Namespace", - className: "namespace", - sortBy: columnId.namespace, - id: columnId.namespace, - }, - { title: "Pods", className: "pods", id: columnId.pods }, - { - title: "Replicas", - className: "replicas", - sortBy: columnId.replicas, - id: columnId.replicas, - }, - { className: "warning", showWithColumn: columnId.replicas }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, - ]} - renderTableContents={statefulSet => [ - statefulSet.getName(), - , - renderPodCounts(statefulSet), - statefulSet.getReplicas(), - , - , - ]} - /> - - ); -}); - -export const StatefulSets = withInjectables(NonInjectedStatefulSets, { - getProps: (di, props) => ({ - ...props, - eventStore: di.inject(eventStoreInjectable), - statefulSetStore: di.inject(statefulSetStoreInjectable), - }), -}); diff --git a/src/renderer/components/+workloads-statefulsets/store.injectable.ts b/src/renderer/components/+workloads-statefulsets/store.injectable.ts deleted file mode 100644 index b8fef46c51..0000000000 --- a/src/renderer/components/+workloads-statefulsets/store.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 assert from "assert"; -import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; -import statefulSetApiInjectable from "../../../common/k8s-api/endpoints/stateful-set.api.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { StatefulSetStore } from "./store"; - -const statefulSetStoreInjectable = getInjectable({ - id: "stateful-set-store", - instantiate: (di) => { - assert(di.inject(storesAndApisCanBeCreatedInjectable), "statefulSetStore is only available in certain environment"); - - const api = di.inject(statefulSetApiInjectable); - - return new StatefulSetStore({ - getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), - context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), - logger: di.inject(loggerInjectable), - }, api); - }, - injectionToken: kubeObjectStoreInjectionToken, -}); - -export default statefulSetStoreInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/store.ts b/src/renderer/components/+workloads-statefulsets/store.ts deleted file mode 100644 index bdedcdcaf7..0000000000 --- a/src/renderer/components/+workloads-statefulsets/store.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable"; -import type { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints"; -import { PodStatusPhase } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store"; -import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; - -interface Dependencies extends KubeObjectStoreDependencies { - getPodsByOwnerId: GetPodsByOwnerId; -} - -export class StatefulSetStore extends KubeObjectStore { - constructor(protected readonly dependencies: Dependencies, api: StatefulSetApi, opts?: KubeObjectStoreOptions) { - super(dependencies, api, opts); - } - - getChildPods(statefulSet: StatefulSet) { - return this.dependencies.getPodsByOwnerId(statefulSet.getId()); - } - - getStatuses(statefulSets: StatefulSet[]) { - const status = { running: 0, failed: 0, pending: 0 }; - - for (const statefulSet of statefulSets) { - const statuses = new Set(this.getChildPods(statefulSet).map(pod => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - } - - return status; - } -} diff --git a/src/renderer/components/+workloads/workloads-mixins.scss b/src/renderer/components/+workloads/workloads-mixins.scss deleted file mode 100644 index 2f4b20fff1..0000000000 --- a/src/renderer/components/+workloads/workloads-mixins.scss +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Pods -$pod-status-running-color: var(--colorOk); -$pod-status-pending-color: var(--colorWarning); -$pod-status-evicted-color: var(--colorError); -$pod-status-succeeded-color: var(--colorSuccess); -$pod-status-failed-color: var(--colorError); -$pod-status-terminated-color: var(--colorTerminated); -$pod-status-terminating-color: var(--colorTerminated); -$pod-status-unknown-color: var(--colorVague); -$pod-status-complete-color: var(--colorSuccess); -$pod-status-crash-loop-color: var(--colorError); -$pod-scheduled: var(--colorOk); -$pod-ready: var(--colorOk); -$pod-initialized: var(--colorOk); -$pod-unschedulable: var(--colorError); -$pod-containers-ready: var(--colorInfo); -$pod-error: var(--colorError); -$pod-container-creating: var(--colorInfo); - -// Deployments -$deployment-available: var(--colorOk); -$deployment-progressing: var(--colorInfo); -$deployment-replicafailure: var(--colorError); - -// Jobs -$job-complete: var(--colorSuccess); -$job-failed: var(--colorError); - -// Cronjob -$cronjob-scheduled: var(--colorSuccess); -$cronjob-suspended: var(--colorTerminated); - -// Pod Statuses -$pod-status-color-list: ( - running: $pod-status-running-color, - pending: $pod-status-pending-color, - evicted: $pod-status-evicted-color, - waiting: $pod-status-pending-color, - succeeded: $pod-status-succeeded-color, - failed: $pod-status-failed-color, - terminating: $pod-status-terminating-color, - terminated: $pod-status-terminated-color, - completed: $pod-status-complete-color, - crash-loop-back-off: $pod-status-crash-loop-color, - error: $pod-error, - container-creating: $pod-container-creating, -); - -// Job Conditions -$job-condition-color-list: ( - complete: $job-complete, - failed: $job-failed, -); - -// Cronjob Conditions -$cronjob-condition-color-list: ( - scheduled: $cronjob-scheduled, - suspended: $cronjob-suspended, -); - -@mixin pod-status-bgs { - @each $status, $color in $pod-status-color-list { - &.#{$status} { - color: white; - background: $color; - } - } -} - -@mixin pod-status-colors { - @each $status, $color in $pod-status-color-list { - &.#{$status} { - color: $color; - } - } -} - -@mixin job-condition-bgs { - @each $condition, $color in $job-condition-color-list { - .#{$condition} { - color: white; - background: $color; - } - } -} - -@mixin job-condition-colors { - @each $condition, $color in $job-condition-color-list { - &.#{$condition} { - color: $color; - } - } -} diff --git a/src/renderer/components/+workloads/workloads-sidebar-items.injectable.tsx b/src/renderer/components/+workloads/workloads-sidebar-items.injectable.tsx deleted file mode 100644 index 933dced46a..0000000000 --- a/src/renderer/components/+workloads/workloads-sidebar-items.injectable.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import type { - SidebarItemRegistration } from "../layout/sidebar-items.injectable"; -import { - sidebarItemsInjectionToken, -} from "../layout/sidebar-items.injectable"; -import { Icon } from "../icon"; -import React from "react"; -import { noop } from "lodash/fp"; - -export const workloadsSidebarItemId = "workloads"; - -const workloadsSidebarItemsInjectable = getInjectable({ - id: "workloads-sidebar-items", - - instantiate: () => - computed((): SidebarItemRegistration[] => [ - { - id: workloadsSidebarItemId, - parentId: null, - title: "Workloads", - getIcon: () => , - onClick: noop, - orderNumber: 20, - }, - ]), - - injectionToken: sidebarItemsInjectionToken, -}); - -export default workloadsSidebarItemsInjectable; diff --git a/src/renderer/components/__tests__/cronjob.store.test.ts b/src/renderer/components/__tests__/cronjob.store.test.ts deleted file mode 100644 index 322e609953..0000000000 --- a/src/renderer/components/__tests__/cronjob.store.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { CronJobStore } from "../+workloads-cronjobs/store"; -import cronJobStoreInjectable from "../+workloads-cronjobs/store.injectable"; -import { CronJob } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const scheduledCronJob = new CronJob({ - apiVersion: "foo", - kind: "CronJob", - metadata: { - name: "scheduledCronJob", - resourceVersion: "scheduledCronJob", - uid: "scheduledCronJob", - namespace: "default", - selfLink: "/apis/batch/v1beta1/cronjobs/default/scheduledCronJob", - }, - spec: { - schedule: "test", - concurrencyPolicy: "test", - suspend: false, - jobTemplate: { - metadata: {}, - spec: { - template: { - metadata: {}, - spec: { - containers: [], - restartPolicy: "restart", - terminationGracePeriodSeconds: 1, - dnsPolicy: "no", - hostPID: true, - schedulerName: "string", - }, - }, - }, - }, - successfulJobsHistoryLimit: 1, - failedJobsHistoryLimit: 1, - }, -}); - -const suspendedCronJob = new CronJob({ - apiVersion: "foo", - kind: "CronJob", - metadata: { - name: "suspendedCronJob", - resourceVersion: "suspendedCronJob", - uid: "suspendedCronJob", - namespace: "default", - selfLink: "/apis/batch/v1beta1/cronjobs/default/suspendedCronJob", - }, - spec: { - schedule: "test", - concurrencyPolicy: "test", - suspend: true, - jobTemplate: { - metadata: {}, - spec: { - template: { - metadata: {}, - spec: { - containers: [], - restartPolicy: "restart", - terminationGracePeriodSeconds: 1, - dnsPolicy: "no", - hostPID: true, - schedulerName: "string", - }, - }, - }, - }, - successfulJobsHistoryLimit: 1, - failedJobsHistoryLimit: 1, - }, -}); - -const otherSuspendedCronJob = new CronJob({ - apiVersion: "foo", - kind: "CronJob", - metadata: { - name: "otherSuspendedCronJob", - resourceVersion: "otherSuspendedCronJob", - uid: "otherSuspendedCronJob", - namespace: "default", - selfLink: "/apis/batch/v1beta1/cronjobs/default/otherSuspendedCronJob", - }, - spec: { - schedule: "test", - concurrencyPolicy: "test", - suspend: true, - jobTemplate: { - metadata: {}, - spec: { - template: { - metadata: {}, - spec: { - containers: [], - restartPolicy: "restart", - terminationGracePeriodSeconds: 1, - dnsPolicy: "no", - hostPID: true, - schedulerName: "string", - }, - }, - }, - }, - successfulJobsHistoryLimit: 1, - failedJobsHistoryLimit: 1, - }, -}); - -describe("CronJob Store tests", () => { - let cronJobStore: CronJobStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - cronJobStore = di.inject(cronJobStoreInjectable); - }); - - it("gets CronJob statuses in proper sorting order", () => { - const statuses = Object.entries(cronJobStore.getStatuses([ - suspendedCronJob, - otherSuspendedCronJob, - scheduledCronJob, - ])); - - expect(statuses).toEqual([ - ["scheduled", 1], - ["suspended", 2], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(cronJobStore.getStatuses([scheduledCronJob])); - - expect(statuses).toEqual([ - ["scheduled", 1], - ["suspended", 0], - ]); - - statuses = Object.entries(cronJobStore.getStatuses([suspendedCronJob])); - - expect(statuses).toEqual([ - ["scheduled", 0], - ["suspended", 1], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/daemonset.store.test.ts b/src/renderer/components/__tests__/daemonset.store.test.ts deleted file mode 100644 index da5a167ffd..0000000000 --- a/src/renderer/components/__tests__/daemonset.store.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; -import type { DaemonSetStore } from "../+workloads-daemonsets/store"; -import daemonSetStoreInjectable from "../+workloads-daemonsets/store.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import { DaemonSet, Pod } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const runningDaemonSet = new DaemonSet({ - apiVersion: "foo", - kind: "DaemonSet", - metadata: { - name: "runningDaemonSet", - resourceVersion: "runningDaemonSet", - uid: "runningDaemonSet", - namespace: "default", - selfLink: "/apis/apps/v1/daemonsets/default/runningDaemonSet", - }, -}); - -const failedDaemonSet = new DaemonSet({ - apiVersion: "foo", - kind: "DaemonSet", - metadata: { - name: "failedDaemonSet", - resourceVersion: "failedDaemonSet", - uid: "failedDaemonSet", - namespace: "default", - selfLink: "/apis/apps/v1/daemonsets/default/failedDaemonSet", - }, -}); - -const pendingDaemonSet = new DaemonSet({ - apiVersion: "foo", - kind: "DaemonSet", - metadata: { - name: "pendingDaemonSet", - resourceVersion: "pendingDaemonSet", - uid: "pendingDaemonSet", - namespace: "default", - selfLink: "/apis/apps/v1/daemonsets/default/pendingDaemonSet", - }, -}); - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - ownerReferences: [{ - uid: "runningDaemonSet", - apiVersion: "apps/v1", - kind: "DaemonSet", - name: "running", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - ownerReferences: [{ - uid: "pendingDaemonSet", - apiVersion: "apps/v1", - kind: "DaemonSet", - name: "pending", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - ownerReferences: [{ - uid: "failedDaemonSet", - apiVersion: "apps/v1", - kind: "DaemonSet", - name: "failed", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("DaemonSet Store tests", () => { - let daemonSetStore: DaemonSetStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - const podStore = di.inject(podStoreInjectable); - - daemonSetStore = di.inject(daemonSetStoreInjectable); - podStore.items = observable.array([ - runningPod, - failedPod, - pendingPod, - ]); - }); - - it("gets DaemonSet statuses in proper sorting order", () => { - const statuses = Object.entries(daemonSetStore.getStatuses([ - failedDaemonSet, - runningDaemonSet, - pendingDaemonSet, - ])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 1], - ["pending", 1], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(daemonSetStore.getStatuses([runningDaemonSet])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(daemonSetStore.getStatuses([failedDaemonSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 1], - ["pending", 0], - ]); - - statuses = Object.entries(daemonSetStore.getStatuses([pendingDaemonSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 0], - ["pending", 1], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/deployments.store.test.ts b/src/renderer/components/__tests__/deployments.store.test.ts deleted file mode 100644 index dc50f85ccf..0000000000 --- a/src/renderer/components/__tests__/deployments.store.test.ts +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; -import type { DeploymentStore } from "../+workloads-deployments/store"; -import deploymentStoreInjectable from "../+workloads-deployments/store.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { PodSpec } from "../../../common/k8s-api/endpoints"; -import { Deployment, Pod } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const spec: PodSpec = { - containers: [{ - name: "some", - image: "someimage", - resources: { - requests: { - cpu: "2", - memory: "2Gi", - }, - }, - terminationMessagePath: "test", - terminationMessagePolicy: "File", - imagePullPolicy: "Always", - }], - restartPolicy: "restart", - terminationGracePeriodSeconds: 1200, - dnsPolicy: "dns", - serviceAccountName: "test", - serviceAccount: "test", - securityContext: {}, - schedulerName: "test", -}; - -const runningDeployment = new Deployment({ - apiVersion: "foo", - kind: "Deployment", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - namespace: "default", - selfLink: "/apis/apps/v1/deployments/default/foobar", - }, - spec: { - replicas: 1, - selector: { matchLabels: {}}, - strategy: { - type: "test", - rollingUpdate: { - maxSurge: 1, - maxUnavailable: 1, - }, - }, - template: { - metadata: { - labels: { - "name": "kube-state-metrics", - }, - }, - spec, - }, - }, -}); - -const failedDeployment = new Deployment({ - apiVersion: "foo", - kind: "Deployment", - metadata: { - name: "failedDeployment", - resourceVersion: "failedDeployment", - uid: "failedDeployment", - namespace: "default", - selfLink: "/apis/apps/v1/deployments/default/failedDeployment", - }, - spec: { - replicas: 1, - selector: { matchLabels: {}}, - strategy: { - type: "test", - rollingUpdate: { - maxSurge: 1, - maxUnavailable: 1, - }, - }, - template: { - metadata: { - labels: { - "name": "failedpods", - }, - }, - spec, - }, - }, -}); - -const pendingDeployment = new Deployment({ - apiVersion: "foo", - kind: "Deployment", - metadata: { - name: "pendingDeployment", - resourceVersion: "pendingDeployment", - uid: "pendingDeployment", - namespace: "default", - selfLink: "/apis/apps/v1/deployments/default/pendingDeployment", - }, - spec: { - replicas: 1, - selector: { matchLabels: {}}, - strategy: { - type: "test", - rollingUpdate: { - maxSurge: 1, - maxUnavailable: 1, - }, - }, - template: { - metadata: { - labels: { - "mydeployment": "true", - }, - }, - spec, - }, - }, -}); - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - labels: { - "name": "kube-state-metrics", - }, - namespace: "default", - selfLink: "/api/v1/pods/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - labels: { - "mydeployment": "true", - }, - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - labels: { - "name": "failedpods", - }, - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("Deployment Store tests", () => { - let deploymentStore: DeploymentStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - const podStore = di.inject(podStoreInjectable); - - // Add pods to pod store - podStore.items = observable.array([ - runningPod, - failedPod, - pendingPod, - ]); - deploymentStore = di.inject(deploymentStoreInjectable); - }); - - it("gets Deployment statuses in proper sorting order", () => { - const statuses = Object.entries(deploymentStore.getStatuses([ - failedDeployment, - runningDeployment, - pendingDeployment, - ])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 1], - ["pending", 1], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(deploymentStore.getStatuses([runningDeployment])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(deploymentStore.getStatuses([failedDeployment])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 1], - ["pending", 0], - ]); - - statuses = Object.entries(deploymentStore.getStatuses([pendingDeployment])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 0], - ["pending", 1], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/job.store.test.ts b/src/renderer/components/__tests__/job.store.test.ts deleted file mode 100644 index 5a000d18ae..0000000000 --- a/src/renderer/components/__tests__/job.store.test.ts +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; -import type { JobStore } from "../+workloads-jobs/store"; -import jobStoreInjectable from "../+workloads-jobs/store.injectable"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import { Job, Pod } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const runningJob = new Job({ - apiVersion: "foo", - kind: "Job", - metadata: { - name: "runningJob", - resourceVersion: "runningJob", - uid: "runningJob", - namespace: "default", - selfLink: "/apis/batch/v1/jobs/default/runningJob", - }, -}); - -const failedJob = new Job({ - apiVersion: "foo", - kind: "Job", - metadata: { - name: "failedJob", - resourceVersion: "failedJob", - uid: "failedJob", - namespace: "default", - selfLink: "/apis/batch/v1/jobs/default/failedJob", - }, -}); - -const pendingJob = new Job({ - apiVersion: "foo", - kind: "Job", - metadata: { - name: "pendingJob", - resourceVersion: "pendingJob", - uid: "pendingJob", - namespace: "default", - selfLink: "/apis/batch/v1/jobs/default/pendingJob", - }, -}); - -const succeededJob = new Job({ - apiVersion: "foo", - kind: "Job", - metadata: { - name: "succeededJob", - resourceVersion: "succeededJob", - uid: "succeededJob", - namespace: "default", - selfLink: "/apis/batch/v1/jobs/default/succeededJob", - }, -}); - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - ownerReferences: [{ - uid: "runningJob", - apiVersion: "v1", - kind: "Pod", - name: "running", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - ownerReferences: [{ - uid: "pendingJob", - apiVersion: "v1", - kind: "Pod", - name: "pending", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - ownerReferences: [{ - uid: "failedJob", - apiVersion: "v1", - kind: "Pod", - name: "failed", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -const succeededPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-succeeded", - resourceVersion: "foobar", - uid: "foobar-succeeded", - ownerReferences: [{ - uid: "succeededJob", - apiVersion: "v1", - kind: "Pod", - name: "succeeded", - }], - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-succeeded", - }, - status: { - phase: "Succeeded", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("Job Store tests", () => { - let jobStore: JobStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - jobStore = di.inject(jobStoreInjectable); - - const podStore = di.inject(podStoreInjectable); - - // Add pods to pod store - podStore.items = observable.array([ - runningPod, - failedPod, - pendingPod, - succeededPod, - ]); - }); - - it("gets Job statuses in proper sorting order", () => { - const statuses = Object.entries(jobStore.getStatuses([ - failedJob, - succeededJob, - runningJob, - pendingJob, - ])); - - expect(statuses).toEqual([ - ["succeeded", 1], - ["running", 1], - ["failed", 1], - ["pending", 1], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(jobStore.getStatuses([succeededJob])); - - expect(statuses).toEqual([ - ["succeeded", 1], - ["running", 0], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(jobStore.getStatuses([runningJob])); - - expect(statuses).toEqual([ - ["succeeded", 0], - ["running", 1], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(jobStore.getStatuses([failedJob])); - - expect(statuses).toEqual([ - ["succeeded", 0], - ["running", 0], - ["failed", 1], - ["pending", 0], - ]); - - statuses = Object.entries(jobStore.getStatuses([pendingJob])); - - expect(statuses).toEqual([ - ["succeeded", 0], - ["running", 0], - ["failed", 0], - ["pending", 1], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/nodes.api.test.ts b/src/renderer/components/__tests__/nodes.api.test.ts deleted file mode 100644 index c152dc7b18..0000000000 --- a/src/renderer/components/__tests__/nodes.api.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { formatNodeTaint } from "../../../common/k8s-api/endpoints"; - -describe("formatNodeTaint tests", () => { - it("should use value if defined", () => { - expect(formatNodeTaint({ - effect: "Foo", - key: "hello", - timeAdded: "pre", - value: "a", - })).toBe("hello=a:Foo"); - }); - - it("should not use value if not defined", () => { - expect(formatNodeTaint({ - effect: "Foo", - key: "hello", - timeAdded: "pre", - })).toBe("hello:Foo"); - }); -}); diff --git a/src/renderer/components/__tests__/pods.store.test.ts b/src/renderer/components/__tests__/pods.store.test.ts deleted file mode 100644 index 229c94c2d8..0000000000 --- a/src/renderer/components/__tests__/pods.store.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { Pod } from "../../../common/k8s-api/endpoints"; -import type { PodStore } from "../+workloads-pods/store"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -const evictedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-evicted", - resourceVersion: "foobar", - uid: "foobar-evicted", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-evicted", - }, - status: { - phase: "Failed", - reason: "Evicted", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -const succeededPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-succeeded", - resourceVersion: "foobar", - uid: "foobar-succeeded", - namespace: "default", - selfLink: "/api/v1/pods/default/foobar-succeeded", - }, - status: { - phase: "Succeeded", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("Pod Store tests", () => { - let podStore: PodStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - podStore = di.inject(podStoreInjectable); - }); - - it("gets Pod statuses in proper sorting order", () => { - const statuses = Object.entries(podStore.getStatuses([ - pendingPod, - runningPod, - succeededPod, - failedPod, - evictedPod, - evictedPod, - ])); - - expect(statuses).toEqual([ - ["Succeeded", 1], - ["Running", 1], - ["Pending", 1], - ["Failed", 1], - ["Evicted", 2], - ]); - }); - - it("counts statuses properly", () => { - const statuses = Object.entries(podStore.getStatuses([ - pendingPod, - pendingPod, - pendingPod, - runningPod, - failedPod, - failedPod, - ])); - - expect(statuses).toEqual([ - ["Running", 1], - ["Pending", 3], - ["Failed", 2], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/replicaset.store.test.ts b/src/renderer/components/__tests__/replicaset.store.test.ts deleted file mode 100644 index 3186725981..0000000000 --- a/src/renderer/components/__tests__/replicaset.store.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import replicasetsStoreInjectable from "../+workloads-replicasets/store.injectable"; -import type { ReplicaSetStore } from "../+workloads-replicasets/store"; -import { ReplicaSet, Pod } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const runningReplicaSet = new ReplicaSet({ - apiVersion: "foo", - kind: "ReplicaSet", - metadata: { - name: "runningReplicaSet", - resourceVersion: "runningReplicaSet", - uid: "runningReplicaSet", - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/runningReplicaSet", - }, -}); - -const failedReplicaSet = new ReplicaSet({ - apiVersion: "foo", - kind: "ReplicaSet", - metadata: { - name: "failedReplicaSet", - resourceVersion: "failedReplicaSet", - uid: "failedReplicaSet", - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/failedReplicaSet", - }, -}); - -const pendingReplicaSet = new ReplicaSet({ - apiVersion: "foo", - kind: "ReplicaSet", - metadata: { - name: "pendingReplicaSet", - resourceVersion: "pendingReplicaSet", - uid: "pendingReplicaSet", - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/pendingReplicaSet", - }, -}); - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - ownerReferences: [{ - uid: "runningReplicaSet", - apiVersion: "v1", - kind: "ReplicaSet", - name: "running", - }], - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - ownerReferences: [{ - uid: "pendingReplicaSet", - apiVersion: "v1", - kind: "ReplicaSet", - name: "pending", - }], - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - ownerReferences: [{ - uid: "failedReplicaSet", - apiVersion: "v1", - kind: "ReplicaSet", - name: "failed", - }], - namespace: "default", - selfLink: "/apis/apps/v1/replicasets/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("ReplicaSet Store tests", () => { - let replicaSetStore: ReplicaSetStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - const podStore = di.inject(podStoreInjectable); - - replicaSetStore = di.inject(replicasetsStoreInjectable); - podStore.items = observable.array([ - runningPod, - failedPod, - pendingPod, - ]); - }); - - it("gets ReplicaSet statuses in proper sorting order", () => { - const statuses = Object.entries(replicaSetStore.getStatuses([ - failedReplicaSet, - runningReplicaSet, - pendingReplicaSet, - ])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 1], - ["pending", 1], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(replicaSetStore.getStatuses([runningReplicaSet])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(replicaSetStore.getStatuses([failedReplicaSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 1], - ["pending", 0], - ]); - - statuses = Object.entries(replicaSetStore.getStatuses([pendingReplicaSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 0], - ["pending", 1], - ]); - }); -}); diff --git a/src/renderer/components/__tests__/statefulset.store.test.ts b/src/renderer/components/__tests__/statefulset.store.test.ts deleted file mode 100644 index 742955884c..0000000000 --- a/src/renderer/components/__tests__/statefulset.store.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { observable } from "mobx"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { StatefulSetStore } from "../+workloads-statefulsets/store"; -import statefulSetStoreInjectable from "../+workloads-statefulsets/store.injectable"; -import { StatefulSet, Pod } from "../../../common/k8s-api/endpoints"; -import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { getDiForUnitTesting } from "../../getDiForUnitTesting"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; -import createClusterInjectable from "../../cluster/create-cluster.injectable"; - -const runningStatefulSet = new StatefulSet({ - apiVersion: "foo", - kind: "StatefulSet", - metadata: { - name: "runningStatefulSet", - resourceVersion: "runningStatefulSet", - uid: "runningStatefulSet", - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/runningStatefulSet", - }, -}); - -const failedStatefulSet = new StatefulSet({ - apiVersion: "foo", - kind: "StatefulSet", - metadata: { - name: "failedStatefulSet", - resourceVersion: "failedStatefulSet", - uid: "failedStatefulSet", - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/failedStatefulSet", - }, -}); - -const pendingStatefulSet = new StatefulSet({ - apiVersion: "foo", - kind: "StatefulSet", - metadata: { - name: "pendingStatefulSet", - resourceVersion: "pendingStatefulSet", - uid: "pendingStatefulSet", - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/pendingStatefulSet", - }, -}); - -const runningPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar", - resourceVersion: "foobar", - uid: "foobar", - ownerReferences: [{ - uid: "runningStatefulSet", - apiVersion: "v1", - kind: "StatefulSet", - name: "running", - }], - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/foobar", - }, - status: { - phase: "Running", - conditions: [ - { - type: "Initialized", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - { - type: "Ready", - status: "True", - lastProbeTime: 1, - lastTransitionTime: "1", - }, - ], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - containerStatuses: [], - initContainerStatuses: [], - }, -}); - -const pendingPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-pending", - resourceVersion: "foobar", - uid: "foobar-pending", - ownerReferences: [{ - uid: "pendingStatefulSet", - apiVersion: "v1", - kind: "StatefulSet", - name: "pending", - }], - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/foobar-pending", - }, -}); - -const failedPod = new Pod({ - apiVersion: "foo", - kind: "Pod", - metadata: { - name: "foobar-failed", - resourceVersion: "foobar", - uid: "foobar-failed", - ownerReferences: [{ - uid: "failedStatefulSet", - apiVersion: "v1", - kind: "StatefulSet", - name: "failed", - }], - namespace: "default", - selfLink: "/apis/apps/v1/statefulsets/default/foobar-failed", - }, - status: { - phase: "Failed", - conditions: [], - hostIP: "10.0.0.1", - podIP: "10.0.0.1", - startTime: "now", - }, -}); - -describe("StatefulSet Store tests", () => { - let statefulSetStore: StatefulSetStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); - di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); - di.override(storesAndApisCanBeCreatedInjectable, () => true); - - const createCluster = di.inject(createClusterInjectable); - - di.override(hostedClusterInjectable, () => createCluster({ - contextName: "some-context-name", - id: "some-cluster-id", - kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", - })); - - statefulSetStore = di.inject(statefulSetStoreInjectable); - - const podStore = di.inject(podStoreInjectable); - - // Add pods to pod store - podStore.items = observable.array([ - runningPod, - failedPod, - pendingPod, - ]); - }); - - it("gets StatefulSet statuses in proper sorting order", () => { - const statuses = Object.entries(statefulSetStore.getStatuses([ - failedStatefulSet, - runningStatefulSet, - pendingStatefulSet, - ])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 1], - ["pending", 1], - ]); - }); - - it("returns 0 for other statuses", () => { - let statuses = Object.entries(statefulSetStore.getStatuses([runningStatefulSet])); - - expect(statuses).toEqual([ - ["running", 1], - ["failed", 0], - ["pending", 0], - ]); - - statuses = Object.entries(statefulSetStore.getStatuses([failedStatefulSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 1], - ["pending", 0], - ]); - - statuses = Object.entries(statefulSetStore.getStatuses([pendingStatefulSet])); - - expect(statuses).toEqual([ - ["running", 0], - ["failed", 0], - ["pending", 1], - ]); - }); -}); diff --git a/src/renderer/components/activate-entity-command/activate-entity-command.tsx b/src/renderer/components/activate-entity-command/activate-entity-command.tsx deleted file mode 100644 index 631415d804..0000000000 --- a/src/renderer/components/activate-entity-command/activate-entity-command.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { IComputedValue } from "mobx"; -import { observer } from "mobx-react"; -import React from "react"; -import { broadcastMessage } from "../../../common/ipc"; -import { catalogEntityRunListener } from "../../../common/ipc/catalog"; -import type { CatalogEntity } from "../../api/catalog-entity"; -import catalogEnitiesInjectable from "../../api/catalog/entity/entities.injectable"; -import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; -import { Select } from "../select"; - -interface Dependencies { - closeCommandOverlay: () => void; - entities: IComputedValue; -} - -const NonInjectedActivateEntityCommand = observer(({ - closeCommandOverlay, - entities, -}: Dependencies) => ( - this.onChangeUrl(v)} - onSubmit={(v) => this.onSubmitUrl(v)} - showValidationLine={true} - /> - { this.nameHidden && ( - - Please provide a web link URL (Press "Enter" to continue or "Escape" to cancel) - - )} - { !this.nameHidden && ( - <> - this.onSubmit(v)} - dirty={true} - /> - - Please provide a name for the web link (Press "Enter" to confirm or "Escape" to cancel) - - - )} - - ); - } -} - -export const WeblinkAddCommand = withInjectables(NonInjectedWeblinkAddCommand, { - getProps: (di, props) => ({ - ...props, - closeCommandOverlay: di.inject(commandOverlayInjectable).close, - weblinkStore: di.inject(weblinkStoreInjectable), - }), -}); diff --git a/src/renderer/components/chart/bar-chart.tsx b/src/renderer/components/chart/bar-chart.tsx deleted file mode 100644 index e5c0e901d8..0000000000 --- a/src/renderer/components/chart/bar-chart.tsx +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import merge from "lodash/merge"; -import moment from "moment"; -import Color from "color"; -import { observer } from "mobx-react"; -import type { ChartOptions, ChartTooltipCallback, ChartTooltipItem, Scriptable } from "chart.js"; -import type { ChartProps } from "./chart"; -import { Chart, ChartKind } from "./chart"; -import { bytesToUnits, cssNames, isObject } from "../../utils"; -import { ZebraStripesPlugin } from "./zebra-stripes.plugin"; -import type { LensTheme } from "../../themes/lens-theme"; -import { NoMetrics } from "../resource-metrics/no-metrics"; -import assert from "assert"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { IComputedValue } from "mobx"; -import activeThemeInjectable from "../../themes/active.injectable"; - -export interface BarChartProps extends ChartProps { - name?: string; - timeLabelStep?: number; // Minute labels appearance step -} - -const getBarColor: Scriptable = ({ dataset }) => Color(dataset?.borderColor).alpha(0.2).string(); - -interface Dependencies { - activeTheme: IComputedValue; -} - -const NonInjectedBarChart = observer(({ - activeTheme, - name, - data, - className, - timeLabelStep = 10, - plugins, - options: customOptions, - ...settings -}: Dependencies & BarChartProps) => { - const { textColorPrimary, borderFaintColor, chartStripesColor } = activeTheme.get().colors; - const { datasets: rawDatasets = [], ...rest } = data; - const datasets = rawDatasets - .filter(set => set.data?.length) - .map(item => ({ - type: ChartKind.BAR, - borderWidth: { top: 3 }, - barPercentage: 1, - categoryPercentage: 1, - ...item, - })); - - plugins ??= [new ZebraStripesPlugin({ - stripeColor: chartStripesColor, - interval: datasets[0]?.data?.length, - })]; - - if (datasets.length === 0) { - return ; - } - - const formatTimeLabels = (timestamp: string, index: number) => { - const label = moment(parseInt(timestamp)).format("HH:mm"); - const offset = " "; - - if (index == 0) return offset + label; - if (index == 60) return label + offset; - - return index % timeLabelStep == 0 ? label : ""; - }; - - const barOptions: ChartOptions = { - maintainAspectRatio: false, - responsive: true, - scales: { - xAxes: [{ - type: "time", - offset: true, - gridLines: { - display: false, - }, - stacked: true, - ticks: { - callback: formatTimeLabels, - autoSkip: false, - source: "data", - backdropColor: "white", - fontColor: textColorPrimary, - fontSize: 11, - maxRotation: 0, - minRotation: 0, - }, - bounds: "data", - time: { - unit: "minute", - displayFormats: { - minute: "x", - }, - parser: timestamp => moment.unix(parseInt(timestamp)), - }, - }], - yAxes: [{ - position: "right", - gridLines: { - color: borderFaintColor, - drawBorder: false, - tickMarkLength: 0, - zeroLineWidth: 0, - }, - ticks: { - maxTicksLimit: 6, - fontColor: textColorPrimary, - fontSize: 11, - padding: 8, - min: 0, - }, - }], - }, - tooltips: { - mode: "index", - position: "cursor", - callbacks: { - title([tooltip]: ChartTooltipItem[]) { - const xLabel = tooltip?.xLabel; - const skipLabel = xLabel == null || new Date(xLabel).getTime() > Date.now(); - - if (skipLabel) return ""; - - return String(xLabel); - }, - labelColor: ({ datasetIndex }) => ( - typeof datasetIndex === "number" - ? { - borderColor: "darkgray", - backgroundColor: datasets[datasetIndex].borderColor as string, - } - : { - borderColor: "darkgray", - backgroundColor: "gray", - } - ), - }, - }, - animation: { - duration: 0, - }, - elements: { - rectangle: { - backgroundColor: getBarColor.bind(null), - }, - }, - }; - - return ( - - ); -}); - -export const BarChart = withInjectables(NonInjectedBarChart, { - getProps: (di, props) => ({ - ...props, - activeTheme: di.inject(activeThemeInjectable), - }), -}); - -const tooltipCallbackWith = (precision: number): ChartTooltipCallback["label"] => ( - ({ datasetIndex, index }, { datasets = [] }) => { - if (typeof datasetIndex !== "number" || typeof index !== "number") { - return ""; - } - - const { label, data } = datasets[datasetIndex]; - - if (!label || !data) { - return ""; - } - - const value = data[index]; - - assert(isObject(value) && !Array.isArray(value) && typeof value.y === "number"); - - return `${label}: ${bytesToUnits(parseInt(value.y.toString()), { precision })}`; - } -); - -// Default options for all charts containing memory units (network, disk, memory, etc) -export const memoryOptions: ChartOptions = { - scales: { - yAxes: [{ - ticks: { - callback: (value) => { - if (typeof value == "string") { - const float = parseFloat(value); - - if (float < 1) { - return float.toFixed(3); - } - - return bytesToUnits(parseInt(value)); - } - - return bytesToUnits(value); - }, - stepSize: 1, - }, - }], - }, - tooltips: { - callbacks: { - label: tooltipCallbackWith(3), - }, - }, -}; - -// Default options for all charts with cpu units or other decimal numbers -export const cpuOptions: ChartOptions = { - scales: { - yAxes: [{ - ticks: { - callback: (value) => { - const float = parseFloat(`${value}`); - - if (float == 0) return "0"; - if (float < 10) return float.toFixed(3); - if (float < 100) return float.toFixed(2); - - return float.toFixed(1); - }, - }, - }], - }, - tooltips: { - callbacks: { - label: tooltipCallbackWith(2), - }, - }, -}; diff --git a/src/renderer/components/chart/chart.scss b/src/renderer/components/chart/chart.scss deleted file mode 100644 index 11718ec6e3..0000000000 --- a/src/renderer/components/chart/chart.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.Chart { - position: relative; - - &.BarChart { - margin-left: -$margin * 2.2; - height: 100%; - overflow: hidden; - } - - .legend { - .LegendBadge { - background: transparent; - transition: background-color 250ms; - white-space: normal; - - &:hover { - background: var(--colorVague); - cursor: default; - } - } - } - - .zebra-cover { - position: absolute; - pointer-events: none; - } -} diff --git a/src/renderer/components/chart/chart.tsx b/src/renderer/components/chart/chart.tsx deleted file mode 100644 index 3d54b90302..0000000000 --- a/src/renderer/components/chart/chart.tsx +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./chart.scss"; -import type { CSSProperties } from "react"; -import React from "react"; -import type { PluginServiceRegistrationOptions } from "chart.js"; -import ChartJS from "chart.js"; -import { remove } from "lodash"; -import { cssNames } from "../../utils"; -import { StatusBrick } from "../status-brick"; -import { Badge } from "../badge"; - -export interface ChartData extends ChartJS.ChartData { - datasets?: ChartDataSets[]; -} - -export interface ChartDataSets extends ChartJS.ChartDataSets { - id?: string; - tooltip?: string; -} - -export interface ChartProps { - data: ChartData; - options?: ChartJS.ChartOptions; // Passed to ChartJS instance - width?: number | string; - height?: number | string; - type?: ChartKind; - showChart?: boolean; // Possible to show legend only if false - showLegend?: boolean; - legendPosition?: "bottom"; - legendColors?: string[]; // Hex colors for each of the labels in data object - plugins?: PluginServiceRegistrationOptions[]; - redraw?: boolean; // If true - recreate chart instance with no animation - title?: string; - className?: string; - "data-testid"?: string; -} - -export enum ChartKind { - PIE = "pie", - BAR = "bar", - LINE = "line", - DOUGHNUT = "doughnut", -} - -const defaultProps: Partial = { - type: ChartKind.DOUGHNUT, - options: {}, - showChart: true, - showLegend: true, - legendPosition: "bottom", - plugins: [], - redraw: false, -}; - -export class Chart extends React.Component { - static defaultProps = defaultProps as object; - - private canvas = React.createRef(); - private chart: ChartJS | null = null; - // ChartJS adds _meta field to any data object passed to it. - // We clone new data prop into currentChartData to compare props and prevProps - private currentChartData?: ChartData; - - componentDidMount() { - const { showChart } = this.props; - - if (showChart) { - this.renderChart(); - } - } - - componentDidUpdate() { - const { showChart, redraw } = this.props; - - if (redraw) { - this.chart?.destroy(); - this.renderChart(); - } else if (showChart) { - if (!this.chart) { - this.renderChart(); - } else { - this.updateChart(); - } - } - } - - memoizeDataProps() { - const { data } = this.props; - - this.currentChartData = { - ...data, - datasets: data.datasets && data.datasets.map(set => { - return { - ...set, - }; - }), - }; - } - - updateChart() { - const { options } = this.props; - - if (!this.chart) return; - - this.chart.options = ChartJS.helpers.configMerge(this.chart.options, options); - - this.memoizeDataProps(); - - const datasets: ChartDataSets[] = (this.chart.config.data ??= {}).datasets ??= []; - const nextDatasets: ChartDataSets[] = (this.currentChartData ??= {}).datasets ??= []; - - // Remove stale datasets if they're not available in nextDatasets - if (datasets.length > nextDatasets.length) { - const sets = [...datasets]; - - sets.forEach(set => { - if (!nextDatasets.find(next => next.id === set.id)) { - remove(datasets, (item => item.id === set.id)); - } - }); - } - - // Mutating inner chart datasets to enable seamless transitions - nextDatasets.forEach((next, datasetIndex) => { - const index = datasets.findIndex(set => set.id === next.id); - - if (index !== -1) { - const data = datasets[index].data = (datasets[index].data ?? []).slice(); // "Clean" mobx observables data to use in ChartJS - const nextData = next.data ??= []; - - data.splice(next.data.length); - - for (let dataIndex = 0; dataIndex < nextData.length; dataIndex += 1) { - data[dataIndex] = nextData[dataIndex]; - } - - // Merge other fields - const { data: _data, ...props } = next; - - datasets[index] = { - ...datasets[index], - ...props, - }; - } else { - datasets[datasetIndex] = next; - } - }); - this.chart.update(); - } - - renderLegend() { - if (!this.props.showLegend) return null; - const { data, legendColors } = this.props; - const { labels, datasets } = data; - const labelElem = (title: string | undefined, color: string | CSSProperties["backgroundColor"], tooltip?: string) => ( - - - {title} -
- )} - tooltip={tooltip} - expandable={false} - /> - ); - - return ( -
- { - labels - ? labels.map((label, index) => { - const { backgroundColor = [] } = datasets?.[0] ?? {}; - const color = legendColors ? legendColors[index] : (backgroundColor as string[])[index]; - - return labelElem(label as string, color); - }) - : datasets?.map(({ borderColor, label, tooltip }) => - labelElem(label, borderColor as string, tooltip), - ) - } -
- ); - } - - renderChart() { - const { type, options, plugins } = this.props; - const canvas = this.canvas.current; - - if (!canvas) { - return; - } - - this.memoizeDataProps(); - - this.chart = new ChartJS(canvas, { - type, - plugins, - options: { - ...options, - legend: { - display: false, - }, - }, - data: this.currentChartData, - }); - } - - render() { - const { width, height, showChart, title, className, "data-testid": dataTestId } = this.props; - - return ( -
- {title &&
{title}
} - {showChart && ( -
- -
-
- )} - {this.renderLegend()} -
- ); - } -} diff --git a/src/renderer/components/chart/index.ts b/src/renderer/components/chart/index.ts deleted file mode 100644 index d84743cb68..0000000000 --- a/src/renderer/components/chart/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./chart"; -export * from "./pie-chart"; -export * from "./bar-chart"; diff --git a/src/renderer/components/chart/options.ts b/src/renderer/components/chart/options.ts deleted file mode 100644 index 106664d26d..0000000000 --- a/src/renderer/components/chart/options.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ChartOptions, ChartPoint } from "chart.js"; -import { bytesToUnits, isDefined } from "../../utils"; - -export type MetricsTab = "CPU" | "Memory" | "Disk" | "Pods" | "Network" | "Filesystem" | "Duration"; - -const memoryLikeOptions: ChartOptions = { - scales: { - yAxes: [{ - ticks: { - callback: (value: number | string): string => { - if (typeof value == "string") { - const float = parseFloat(value); - - if (float < 1) { - return float.toFixed(3); - } - - return bytesToUnits(parseInt(value)); - } - - return bytesToUnits(value); - }, - stepSize: 1, - }, - }], - }, - tooltips: { - callbacks: { - label: ({ datasetIndex, index }, { datasets }) => { - if (!isDefined(datasetIndex) || !isDefined(index) || !isDefined(datasets)) { - return ""; - } - - const { label, data } = datasets[datasetIndex]; - - if (!data) { - return label ?? ""; - } - - const value = data[index] as { y: number }; - - return `${label}: ${bytesToUnits(parseInt(value.y.toString()), { precision: 3 })}`; - }, - }, - }, -}; - -export const metricTabOptions: Record = { - Memory: memoryLikeOptions, - Disk: memoryLikeOptions, - Network: memoryLikeOptions, - Filesystem: memoryLikeOptions, - CPU: { - scales: { - yAxes: [{ - ticks: { - callback: (value: number | string): string => { - const float = parseFloat(`${value}`); - - if (float == 0) return "0"; - if (float < 10) return float.toFixed(3); - if (float < 100) return float.toFixed(2); - - return float.toFixed(1); - }, - }, - }], - }, - tooltips: { - callbacks: { - label: ({ datasetIndex, index }, { datasets }) => { - if (!isDefined(datasetIndex) || !isDefined(index) || !isDefined(datasets)) { - return ""; - } - - const { label, data } = datasets[datasetIndex]; - - if (!data) { - return label ?? ""; - } - - const value = data[index] as ChartPoint; - - return `${label}: ${parseFloat(value.y as string).toPrecision(2)}`; - }, - }, - }, - }, - Pods: { - scales: { - yAxes: [{ - ticks: { - callback: value => value, - }, - }], - }, - tooltips: { - callbacks: { - label: ({ datasetIndex, index }, { datasets }) => { - if (!isDefined(datasetIndex) || !isDefined(index) || !isDefined(datasets)) { - return ""; - } - - const { label, data } = datasets[datasetIndex]; - - if (!data) { - return label ?? ""; - } - - const value = data[index] as ChartPoint; - - return `${label}: ${value.y}`; - }, - }, - }, - }, - Duration: { - scales: { - yAxes: [{ - ticks: { - callback: value => value, - }, - }], - }, - tooltips: { - callbacks: { - label: ({ datasetIndex, index }, { datasets }) => { - if (!isDefined(datasetIndex) || !isDefined(index) || !isDefined(datasets)) { - return ""; - } - - const { label, data } = datasets[datasetIndex]; - - if (!data) { - return label ?? ""; - } - - const value = data[index] as { y: string }; - - return `${label}: ${parseFloat(value.y).toFixed(3)} sec`; - }, - }, - }, - }, -}; diff --git a/src/renderer/components/chart/pie-chart.scss b/src/renderer/components/chart/pie-chart.scss deleted file mode 100644 index d2edcf5aa9..0000000000 --- a/src/renderer/components/chart/pie-chart.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.PieChart { - .chart-container { - width: 120px; - min-height: 150px; - } - - .legend { - margin-top: $margin; - flex-direction: column; - - > * { - &.LegendBadge:hover { - background-color: transparent; - } - - margin-bottom: $margin; - } - } -} diff --git a/src/renderer/components/chart/pie-chart.tsx b/src/renderer/components/chart/pie-chart.tsx deleted file mode 100644 index de1461631a..0000000000 --- a/src/renderer/components/chart/pie-chart.tsx +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./pie-chart.scss"; -import React from "react"; -import { observer } from "mobx-react"; -import type { ChartOptions } from "chart.js"; -import ChartJS from "chart.js"; -import type { ChartProps } from "./chart"; -import { Chart } from "./chart"; -import { cssNames } from "../../utils"; -import type { LensTheme } from "../../themes/lens-theme"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { IComputedValue } from "mobx"; -import activeThemeInjectable from "../../themes/active.injectable"; - -export interface PieChartProps extends ChartProps { -} - -export interface PieChartData extends ChartJS.ChartData { - datasets?: PieChartDataSets[]; -} - -export type DatasetTooltipLabel = (percent: string) => string | string; - -interface PieChartDataSets extends ChartJS.ChartDataSets { - id?: string; - tooltipLabels?: DatasetTooltipLabel[]; -} - -function getCutout(length: number | undefined): number { - switch (length) { - case 0: - case 1: - return 88; - case 2: - return 76; - case 3: - return 63; - default: - return 50; - } -} - -interface Dependencies { - activeTheme: IComputedValue; -} - -const NonInjectedPieChart = observer(({ - activeTheme, - data, - className, - options, - showChart, - ...chartProps -}: Dependencies & PieChartProps) => { - const { contentColor } = activeTheme.get().colors; - const opts: ChartOptions = { - maintainAspectRatio: false, - tooltips: { - mode: "index", - callbacks: { - title: () => "", - label: (tooltipItem: { datasetIndex: number; index: number }, data: PieChartData) => { - const dataset = data.datasets?.[tooltipItem.datasetIndex] ?? {}; - const datasetData = (dataset.data ?? []) as number[]; - const total = datasetData.reduce((acc, cur) => acc + cur, 0); - const percent = Math.round((datasetData[tooltipItem.index] as number / total) * 100); - const percentLabel = isNaN(percent) ? "N/A" : `${percent}%`; - const tooltipLabelCustomizer = dataset.tooltipLabels?.[tooltipItem.index]; - - return tooltipLabelCustomizer - ? tooltipLabelCustomizer(percentLabel) - : `${dataset.label}: ${percentLabel}`; - }, - }, - filter: ({ datasetIndex, index }, { datasets = [] }) => { - if (datasetIndex === undefined) { - return false; - } - - const { data = [] } = datasets[datasetIndex]; - - if (datasets.length === 1) return true; - - return index !== data.length - 1; - }, - position: "cursor", - }, - elements: { - arc: { - borderWidth: 1, - borderColor: contentColor, - }, - }, - cutoutPercentage: getCutout(data.datasets?.length), - responsive: true, - ...options, - }; - - return ( - - ); -}); - -export const PieChart = withInjectables(NonInjectedPieChart, { - getProps: (di, props) => ({ - ...props, - activeTheme: di.inject(activeThemeInjectable), - }), -}); - -ChartJS.Tooltip.positioners.cursor = function (elements: any, position: { x: number; y: number }) { - return position; -}; diff --git a/src/renderer/components/chart/zebra-stripes.plugin.ts b/src/renderer/components/chart/zebra-stripes.plugin.ts deleted file mode 100644 index 020429530f..0000000000 --- a/src/renderer/components/chart/zebra-stripes.plugin.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Plugin for drawing stripe bars on top of any timeseries barchart -// Based on cover DIV element with repeating-linear-gradient style - -import type ChartJS from "chart.js"; -import type { Moment } from "moment"; -import moment from "moment"; -import type { PluginServiceRegistrationOptions } from "chart.js"; - -const defaultOptions = { - stripeColor: "#ffffff08", - interval: 10, -}; - -export interface ZebraStripesOptions { - stripeColor: string; - interval: number; -} - -export class ZebraStripesPlugin implements PluginServiceRegistrationOptions { - updated: Moment | null = null; - options: ZebraStripesOptions; - - constructor(options?: Partial) { - this.options = Object.assign({}, defaultOptions, options); - } - - getOptions(chart: ChartJS): ZebraStripesOptions | undefined { - return chart.options.plugins?.ZebraStripes; - } - - getLastUpdate(chart: ChartJS) { - const data = chart.data.datasets?.[0]?.data?.[0] as ChartJS.ChartPoint; - - return moment.unix(parseInt(data.x as string)); - } - - getStripesElem(chart: ChartJS) { - return chart.canvas?.parentElement?.querySelector(".zebra-cover"); - } - - removeStripesElem(chart: ChartJS) { - const elem = this.getStripesElem(chart); - - if (elem) { - chart.canvas?.parentElement?.removeChild(elem); - } - } - - updateOptions(chart: ChartJS) { - this.options = { - ...defaultOptions, - ...this.getOptions(chart), - }; - } - - getStripeMinutes() { - return this.options.interval < 10 ? 0 : 10; - } - - renderStripes(chart: ChartJS) { - if (!chart.data.datasets?.length) return; - const { interval, stripeColor } = this.options; - const { top, left, bottom, right } = chart.chartArea; - const step = (right - left) / interval; - const stripeWidth = step * this.getStripeMinutes(); - const cover = document.createElement("div"); - const styles = cover.style; - - if (this.getStripesElem(chart)) return; - - cover.className = "zebra-cover"; - styles.width = `${right - left}px`; - styles.left = `${left}px`; - styles.top = `${top}px`; - styles.height = `${bottom - top}px`; - styles.backgroundImage = ` - repeating-linear-gradient(to right, ${stripeColor} 0px, ${stripeColor} ${stripeWidth}px, - transparent ${stripeWidth}px, transparent ${stripeWidth * 2 + step}px) - `; - chart.canvas?.parentElement?.appendChild(cover); - } - - afterInit(chart: ChartJS) { - if (!chart.data.datasets?.length) return; - this.updateOptions(chart); - this.updated = this.getLastUpdate(chart); - } - - afterUpdate(chart: ChartJS) { - this.updateOptions(chart); - this.renderStripes(chart); - } - - resize(chart: ChartJS) { - this.removeStripesElem(chart); - } - - afterDatasetUpdate(chart: ChartJS): void { - this.updated ??= this.getLastUpdate(chart); - - const { interval } = this.options; - const { left, right } = chart.chartArea; - const step = (right - left) / interval; - const diff = moment(this.updated).diff(this.getLastUpdate(chart), "minutes"); - const minutes = Math.abs(diff); - - this.removeStripesElem(chart); - this.renderStripes(chart); - - if (minutes > 0) { - // Move position regarding to difference in time - const cover = this.getStripesElem(chart); - - if (cover) { - cover.style.backgroundPositionX = `${-step * minutes}px`; - } - } - } -} diff --git a/src/renderer/components/checkbox/checkbox.scss b/src/renderer/components/checkbox/checkbox.scss deleted file mode 100644 index a32de49858..0000000000 --- a/src/renderer/components/checkbox/checkbox.scss +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -@include theme-light { - .Checkbox { - --checkbox-color-active: var(--blue); - --checkbox-bgc-active: none; - } -} - -.Checkbox { - --checkbox-color: var(--textColorPrimary); // tick color [√] - --checkbox-color-active: var(--contentColor); - --checkbox-bgc-active: var(--blue); - - flex-shrink: 0; - - &:hover { - input:not(:checked):not(:disabled) { - ~ .tick:after { - opacity: 1; - } - } - } - - input[type="checkbox"] { - display: none; - - &:checked { - ~ .box { - color: var(--checkbox-color-active); - background: var(--checkbox-bgc-active); - border-color: var(--checkbox-bgc-active); - - &:after { - opacity: 1; - } - } - } - - &:disabled { - ~ .box { - color: var(--checkbox-color); - } - - ~ * { - opacity: .5; - pointer-events: none; - } - } - - &:not(:disabled) ~ * { - cursor: pointer; - } - } - - .label { - margin-right: $margin; - } - - > .box { - $boxSize: round(1.7 * $unit); - - position: relative; - width: $boxSize; - height: $boxSize; - border-radius: 2px; - color: var(--checkbox-color); - border: 2px solid currentColor; - flex-shrink: 0; - - &:after { - content: ""; - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 60%; - border: 2px solid currentColor; - border-top: 0; - border-right: 0; - transform: rotate(-45deg); - opacity: 0; - } - - + * { - margin-left: .5em; - } - } -} diff --git a/src/renderer/components/checkbox/checkbox.tsx b/src/renderer/components/checkbox/checkbox.tsx deleted file mode 100644 index ecba55378e..0000000000 --- a/src/renderer/components/checkbox/checkbox.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./checkbox.scss"; -import React from "react"; -import type { SingleOrMany } from "../../utils"; -import { cssNames, noop } from "../../utils"; - -export interface CheckboxProps { - className?: string; - label?: React.ReactNode; - inline?: boolean; - disabled?: boolean; - value?: boolean; - onChange?(value: boolean, evt: React.ChangeEvent): void; - children?: SingleOrMany; -} - -export function Checkbox({ label, inline, className, value, children, onChange = noop, disabled, ...inputProps }: CheckboxProps) { - const componentClass = cssNames("Checkbox flex align-center", className, { - inline, - checked: value, - disabled, - }); - - return ( - - ); -} diff --git a/src/renderer/components/checkbox/index.ts b/src/renderer/components/checkbox/index.ts deleted file mode 100644 index 9885e31923..0000000000 --- a/src/renderer/components/checkbox/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./checkbox"; diff --git a/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts deleted file mode 100644 index 3f40455527..0000000000 --- a/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import { ClusterFrameHandler } from "./cluster-frame-handler"; -import emitClusterVisibilityInjectable from "./emit-cluster-visibility.injectable"; - -const clusterFrameHandlerInjectable = getInjectable({ - id: "cluster-frame-handler", - instantiate: (di) => new ClusterFrameHandler({ - emitClusterVisibility: di.inject(emitClusterVisibilityInjectable), - getClusterById: di.inject(getClusterByIdInjectable), - logger: di.inject(loggerInjectable), - }), -}); - -export default clusterFrameHandlerInjectable; diff --git a/src/renderer/components/cluster-manager/cluster-frame-handler.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.ts deleted file mode 100644 index f1b169915f..0000000000 --- a/src/renderer/components/cluster-manager/cluster-frame-handler.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, makeObservable, observable, when } from "mobx"; -import type { ClusterId } from "../../../common/cluster-types"; -import type { Disposer } from "../../utils"; -import { getClusterFrameUrl, onceDefined } from "../../utils"; -import assert from "assert"; -import type { Logger } from "../../../common/logger"; -import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; -import type { EmitClusterVisibility } from "./emit-cluster-visibility.injectable"; - -export interface LensView { - isLoaded: boolean; - frame: HTMLIFrameElement; -} - -interface Dependencies { - readonly logger: Logger; - getClusterById: GetClusterById; - emitClusterVisibility: EmitClusterVisibility; -} - -export class ClusterFrameHandler { - private readonly views = observable.map(); - - constructor(protected readonly dependencies: Dependencies) { - makeObservable(this); - } - - public hasLoadedView(clusterId: string): boolean { - return Boolean(this.views.get(clusterId)?.isLoaded); - } - - @action - public initView(clusterId: ClusterId) { - const cluster = this.dependencies.getClusterById(clusterId); - - if (!cluster) { - this.dependencies.logger.warn(`[LENS-VIEW]: not initializing view; unknown clusterId="${clusterId}"`); - - return; - } - - const parentElem = document.getElementById("lens-views"); - - assert(parentElem, "DOM with #lens-views must be present"); - - if (this.views.has(clusterId)) { - return; - } - - this.dependencies.logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`); - const iframe = document.createElement("iframe"); - - iframe.id = `cluster-frame-${cluster.id}`; - iframe.name = cluster.contextName; - iframe.setAttribute("src", getClusterFrameUrl(clusterId)); - iframe.addEventListener("load", action(() => { - this.dependencies.logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`); - const view = this.views.get(clusterId); - - assert(view, `view for ${clusterId} MUST still exist here`); - view.isLoaded = true; - }), { once: true }); - this.views.set(clusterId, { frame: iframe, isLoaded: false }); - parentElem.appendChild(iframe); - - this.dependencies.logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`); - - const dispose = when( - () => cluster.ready, - () => this.dependencies.logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`), - ); - - when( - // cluster.disconnect is set to `false` when the cluster starts to connect - () => !cluster.disconnected, - () => { - when( - () => { - const cluster = this.dependencies.getClusterById(clusterId); - - return Boolean(!cluster || (cluster.disconnected && this.views.get(clusterId)?.isLoaded)); - }, - () => { - this.dependencies.logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`); - this.views.delete(clusterId); - - // Must only remove iframe from DOM after it unloads old code. Else it crashes - iframe.addEventListener("load", () => parentElem.removeChild(iframe), { - once: true, - }); - - // This causes the old code to be unloaded. - iframe.setAttribute("src", ""); - - dispose(); - }, - ); - }, - ); - } - - private prevVisibleClusterChange?: Disposer; - - public setVisibleCluster(clusterId: ClusterId | null): void { - // Clear the previous when ASAP - this.prevVisibleClusterChange?.(); - - this.dependencies.logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${clusterId}`); - this.dependencies.emitClusterVisibility(null); - - for (const { frame: view } of this.views.values()) { - view.classList.add("hidden"); - } - - const cluster = clusterId - ? this.dependencies.getClusterById(clusterId) - : undefined; - - if (cluster && clusterId) { - this.prevVisibleClusterChange = onceDefined( - () => { - const view = this.views.get(clusterId); - - if (cluster.available && cluster.ready && view?.isLoaded) { - return view; - } - - return undefined; - }, - (view: LensView) => { - this.dependencies.logger.info(`[LENS-VIEW]: cluster id=${clusterId} should now be visible`); - view.frame.classList.remove("hidden"); - view.frame.focus(); - this.dependencies.emitClusterVisibility(clusterId); - }, - ); - } - } - - public clearVisibleCluster() { - this.setVisibleCluster(null); - } -} diff --git a/src/renderer/components/cluster-manager/cluster-manager-root-frame-child-component.injectable.tsx b/src/renderer/components/cluster-manager/cluster-manager-root-frame-child-component.injectable.tsx deleted file mode 100644 index 9db066e1ad..0000000000 --- a/src/renderer/components/cluster-manager/cluster-manager-root-frame-child-component.injectable.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import React from "react"; -import { getInjectable } from "@ogre-tools/injectable"; -import { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token"; -import { ClusterManager } from "./cluster-manager"; -import { computed } from "mobx"; -import { ErrorBoundary } from "../error-boundary"; - -const clusterManagerRootFrameChildComponentInjectable = getInjectable({ - id: "cluster-manager-root-frame-child-component", - - instantiate: () => ({ - id: "cluster-manager", - - shouldRender: computed(() => true), - - Component: () => ( - - - - ), - }), - - injectionToken: rootFrameChildComponentInjectionToken, -}); - -export default clusterManagerRootFrameChildComponentInjectable; diff --git a/src/renderer/components/cluster-manager/cluster-manager.scss b/src/renderer/components/cluster-manager/cluster-manager.scss deleted file mode 100644 index 7644d1f4d3..0000000000 --- a/src/renderer/components/cluster-manager/cluster-manager.scss +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.ClusterManager { - --hotbar-width: 75px; - - display: grid; - grid-template-areas: - "topbar topbar" - "menu main" - "status-bar status-bar"; - grid-template-rows: auto 1fr min-content; - grid-template-columns: min-content 1fr; - - main { - grid-area: main; - position: relative; - display: flex; - flex-direction: column; - overflow-y: auto; - } - - .HotbarMenu { - grid-area: menu; - } - - .error { - z-index: 1; - } - - #lens-views { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - display: flex; - background-color: var(--mainBackground); - - iframe { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - display: flex; - flex: 1; - - // when updating font settings in the "Preferences -> Terminal" cluster's iframe - // must be accessible in DOM (e.g. elem.getBoundingClientRect() must work) - &.hidden { - opacity: 0; - pointer-events: none; - } - } - } -} diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx deleted file mode 100644 index ab418ec827..0000000000 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./cluster-manager.scss"; - -import React from "react"; -import { Redirect } from "react-router"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { StatusBar } from "../status-bar/status-bar"; -import { HotbarMenu } from "../hotbar/hotbar-menu"; -import { DeleteClusterDialog } from "../delete-cluster-dialog"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import { TopBar } from "../layout/top-bar/top-bar"; -import catalogPreviousActiveTabStorageInjectable from "../+catalog/catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable"; -import type { IComputedValue } from "mobx"; -import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; -import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable"; -import { buildURL } from "../../../common/utils/buildUrl"; -import type { StorageLayer } from "../../utils"; -import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable"; -import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable"; -import currentPathInjectable from "../../routes/current-path.injectable"; - -interface Dependencies { - catalogPreviousActiveTabStorage: StorageLayer; - currentRouteComponent: IComputedValue; - welcomeUrl: string; - watchForGeneralEntityNavigation: WatchForGeneralEntityNavigation; - currentPath: IComputedValue; -} - -@observer -class NonInjectedClusterManager extends React.Component { - componentDidMount() { - disposeOnUnmount(this, [ - this.props.watchForGeneralEntityNavigation(), - ]); - } - - renderMainComponent() { - const Component = this.props.currentRouteComponent.get(); - - if (Component) { - return ; - } - - const currentPath = this.props.currentPath.get(); - - if (currentPath !== this.props.welcomeUrl) { - return ; - } - - return ( -
-

ERROR!!

-

- No matching route for the current path: - {" "} - {currentPath} - {" "} - which is the welcomeUrl. This is a bug. -

-
- ); - } - - render() { - return ( -
- -
-
- {this.renderMainComponent()} -
- - - -
- ); - } -} - -export const ClusterManager = withInjectables(NonInjectedClusterManager, { - getProps: (di) => ({ - catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable), - currentRouteComponent: di.inject(currentRouteComponentInjectable), - welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path), - watchForGeneralEntityNavigation: di.inject(watchForGeneralEntityNavigationInjectable), - currentPath: di.inject(currentPathInjectable), - }), -}); diff --git a/src/renderer/components/cluster-manager/cluster-status.module.scss b/src/renderer/components/cluster-manager/cluster-status.module.scss deleted file mode 100644 index 194ef467db..0000000000 --- a/src/renderer/components/cluster-manager/cluster-status.module.scss +++ /dev/null @@ -1,39 +0,0 @@ -.status { - --flex-gap: 16px; - - position: relative; - min-width: 350px; - margin: auto; - text-align: center; - z-index: 1; - background: var(--mainBackground); - width: 100%; - height: 100%; - - pre { - max-width: 70vw; - max-height: 40vh; - white-space: pre-line; - overflow: auto; - line-height: 1.7; - - p { - margin-bottom: var(--margin); - } - } - - a { - cursor: pointer; - } -} - -.spinner { - --spinner-size: 38px; - --spinner-border: calc(var(--spinner-size) / 10); -} - -.icon { - --size: 70px; - margin: auto; - color: var(--colorError); -} \ No newline at end of file diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx deleted file mode 100644 index 1183b4f52b..0000000000 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./cluster-status.module.scss"; - -import { computed, observable, makeObservable } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import React from "react"; -import { ipcRendererOn } from "../../../common/ipc"; -import type { Cluster } from "../../../common/cluster/cluster"; -import type { IClassName } from "../../utils"; -import { isBoolean, hasTypedProperty, isObject, isString, cssNames } from "../../utils"; -import { Button } from "../button"; -import { Icon } from "../icon"; -import { Spinner } from "../spinner"; -import type { KubeAuthUpdate } from "../../../common/cluster-types"; -import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry"; -import { requestClusterActivation } from "../../ipc"; -import type { NavigateToEntitySettings } from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import navigateToEntitySettingsInjectable from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; -import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; - -export interface ClusterStatusProps { - className?: IClassName; - cluster: Cluster; -} - -interface Dependencies { - navigateToEntitySettings: NavigateToEntitySettings; - entityRegistry: CatalogEntityRegistry; -} - -@observer -class NonInjectedClusterStatus extends React.Component { - @observable authOutput: KubeAuthUpdate[] = []; - @observable isReconnecting = false; - - constructor(props: ClusterStatusProps & Dependencies) { - super(props); - makeObservable(this); - } - - get cluster(): Cluster { - return this.props.cluster; - } - - @computed get entity() { - return this.props.entityRegistry.getById(this.cluster.id); - } - - @computed get hasErrors(): boolean { - return this.authOutput.some(({ isError }) => isError); - } - - componentDidMount() { - disposeOnUnmount(this, [ - ipcRendererOn(`cluster:${this.cluster.id}:connection-update`, (evt, res: unknown) => { - if ( - isObject(res) - && hasTypedProperty(res, "message", isString) - && hasTypedProperty(res, "isError", isBoolean) - ) { - this.authOutput.push(res); - } else { - console.warn(`Got invalid connection update for ${this.cluster.id}`, { update: res }); - } - }), - ]); - } - - componentDidUpdate(prevProps: Readonly): void { - if (prevProps.cluster.id !== this.props.cluster.id) { - this.isReconnecting = false; - this.authOutput = []; - } - } - - reconnect = async () => { - this.authOutput = []; - this.isReconnecting = true; - - try { - await requestClusterActivation(this.cluster.id, true); - } catch (error) { - this.authOutput.push({ - message: String(error), - isError: true, - }); - } finally { - this.isReconnecting = false; - } - }; - - manageProxySettings = () => { - this.props.navigateToEntitySettings(this.cluster.id, "proxy"); - }; - - renderAuthenticationOutput() { - return ( -
-        {
-          this.authOutput.map(({ message, isError }, index) => (
-            

- {message.trim()} -

- )) - } -
- ); - } - - renderStatusIcon() { - if (this.hasErrors) { - return ; - } - - return ( - <> - -
-          

- {this.isReconnecting ? "Reconnecting" : "Connecting"} - … -

-
- - ); - } - - renderReconnectionHelp() { - if (this.hasErrors && !this.isReconnecting) { - return ( - <> -
- ); - } - - renderDeleteMessage({ cluster }: DeleteClusterDialogState) { - if (cluster.isInLocalKubeconfig()) { - return ( -
- {"Delete the "} - - {cluster.getMeta().name} - - {" context from Lens's internal kubeconfig?"} -
- ); - } - - return ( -
- {"Delete the "} - - {cluster.getMeta().name} - - {" context from "} - - {cluster.kubeConfigPath} - - ? -
- ); - } - - getWarningMessage({ cluster, config }: DeleteClusterDialogState) { - if (cluster.isInLocalKubeconfig()) { - return ( -

- Are you sure you want to delete it? It can be re-added through the copy/paste mechanism. -

- ); - } - - const contexts = config.contexts.filter(context => context.name !== cluster.contextName); - - if (!contexts.length) { - return ( -

- This will remove the last context in kubeconfig. There will be no active context. -

- ); - } - - if (isCurrentContext(config, cluster)) { - return ( -

- This will remove active context in kubeconfig. Use drop down below to select a different one. -

- ); - } - - return ( -

The contents of kubeconfig file will be changed!

- ); - } - - renderWarning(state: DeleteClusterDialogState) { - return ( -
- - {this.getWarningMessage(state)} -
- ); - } - - renderContents(state: DeleteClusterDialogState) { - const { config, cluster, showContextSwitch } = state; - const contexts = state.config.contexts.filter(context => context.name !== state.cluster.contextName); - const disableDelete = this.shouldDeleteBeDisabled(state); - const currentContext = isCurrentContext(config, cluster); - - return ( - <> -
- {this.renderDeleteMessage(state)} - {this.renderWarning(state)} - {contexts.length > 0 && ( - <> -
-
- - Select current-context - {" "} - {!currentContext && "(optional)"} - - )} - value={showContextSwitch} - onChange={value => { - this.props.state.set({ - ...state, - showContextSwitch: currentContext || value, - }); - }} - /> -
- {this.renderCurrentContextSwitch(state)} - - )} -
-
-
- - ); - } - - private close = () => this.props.state.set(undefined); - - render() { - const state = this.props.state.get(); - - return ( - - {state && this.renderContents(state)} - - ); - } -} - -export const DeleteClusterDialog = withInjectables(NonInjectedDeleteClusterDialog, { - getProps: (di) => ({ - hotbarStore: di.inject(hotbarStoreInjectable), - state: di.inject(deleteClusterDialogStateInjectable), - requestSetClusterAsDeleting: di.inject(requestSetClusterAsDeletingInjectable), - requestClearClusterAsDeleting: di.inject(requestClearClusterAsDeletingInjectable), - requestDeleteCluster: di.inject(requestDeleteClusterInjectable), - saveKubeconfig: di.inject(saveKubeconfigInjectable), - showErrorNotification: di.inject(showErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/dialog/dialog.scss b/src/renderer/components/dialog/dialog.scss deleted file mode 100644 index 4efb628881..0000000000 --- a/src/renderer/components/dialog/dialog.scss +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -.Dialog { - position: fixed; - overflow: auto; - left: 0; - top: 0; - width: 100%; - height: 100%; - padding: $unit * 5; - z-index: $zIndex-dialog; - overscroll-behavior: none; // prevent swiping with touch-pad on MacOSX - overflow: auto; - - &.modal { - background: transparentize(#222, .5); - } - - h5 { - color: white; - } -} diff --git a/src/renderer/components/dialog/dialog.tsx b/src/renderer/components/dialog/dialog.tsx deleted file mode 100644 index 0d7bfb24c7..0000000000 --- a/src/renderer/components/dialog/dialog.tsx +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./dialog.scss"; - -import React from "react"; -import { createPortal } from "react-dom"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { reaction } from "mobx"; -import { Animate } from "../animate"; -import { cssNames, noop, stopPropagation } from "../../utils"; -import type { ObservableHistory } from "mobx-observable-history"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import observableHistoryInjectable from "../../navigation/observable-history.injectable"; -import requestAnimationFrameInjectable from "../animate/request-animation-frame.injectable"; - -// todo: refactor + handle animation-end in props.onClose()? - -export interface DialogProps { - className?: string; - isOpen?: boolean; - open?: () => void; - close?: () => void; - onOpen?: () => void; - onClose?: () => void; - modal?: boolean; - pinned?: boolean; - animated?: boolean; - "data-testid"?: string; - children?: React.ReactNode | React.ReactNode[]; -} - -interface DialogState { - isOpen: boolean; -} - -interface Dependencies { - navigation: ObservableHistory; - requestAnimationFrame: (callback: () => void) => void; -} - -@observer -class NonInjectedDialog extends React.PureComponent { - private readonly contentElem = React.createRef(); - private readonly ref = React.createRef(); - - static defaultProps = { - isOpen: false, - open: noop, - close: noop, - onOpen: noop, - onClose: noop, - modal: true, - animated: true, - pinned: false, - }; - - public state: DialogState = { - isOpen: this.props.isOpen ?? false, - }; - - get elem() { - return this.ref.current; - } - - get isOpen() { - return this.state.isOpen; - } - - componentDidMount() { - if (this.isOpen) { - this.onOpen(); - } - - disposeOnUnmount(this, [ - reaction(() => this.props.navigation.toString(), () => this.close()), - ]); - } - - componentDidUpdate(prevProps: DialogProps) { - const { isOpen } = this.props; - - if (isOpen !== prevProps.isOpen) { - this.toggle(isOpen ?? false); - } - } - - componentWillUnmount() { - if (this.isOpen) this.onClose(); - } - - toggle(isOpen: boolean) { - if (isOpen) this.open(); - else this.close(); - } - - open() { - this.props.requestAnimationFrame(this.onOpen); // wait for render(), bind close-event to this.elem - this.setState({ isOpen: true }); - this.props.open?.(); - } - - close() { - this.onClose(); // must be first to get access to dialog's content from outside - this.setState({ isOpen: false }); - this.props.close?.(); - } - - onOpen = () => { - this.props.onOpen?.(); - - if (!this.props.pinned) { - if (this.elem) this.elem.addEventListener("click", this.onClickOutside); - // Using document.body target to handle keydown event before Drawer does - document.body.addEventListener("keydown", this.onEscapeKey); - } - }; - - onClose = () => { - this.props.onClose?.(); - - if (!this.props.pinned) { - if (this.elem) this.elem.removeEventListener("click", this.onClickOutside); - document.body.removeEventListener("keydown", this.onEscapeKey); - } - }; - - onEscapeKey = (evt: KeyboardEvent) => { - const escapeKey = evt.code === "Escape"; - - if (escapeKey) { - this.close(); - evt.stopPropagation(); - } - }; - - onClickOutside = (evt: MouseEvent) => { - const target = evt.target as HTMLElement; - - if (!this.contentElem.current?.contains(target)) { - this.close(); - evt.stopPropagation(); - } - }; - - render() { - const { modal, animated, pinned, "data-testid": testId, className } = this.props; - - let dialog = ( -
-
- {this.props.children} -
-
- ); - - if (animated) { - dialog = ( - - {dialog} - - ); - } else if (!this.isOpen) { - return null; - } - - return createPortal(dialog, document.body); - } -} - -export const Dialog = withInjectables((props) => , { - getProps: (di, props) => ({ - ...props, - navigation: di.inject(observableHistoryInjectable), - requestAnimationFrame: di.inject(requestAnimationFrameInjectable), - }), -}); diff --git a/src/renderer/components/dialog/index.ts b/src/renderer/components/dialog/index.ts deleted file mode 100644 index 9aa342f9ba..0000000000 --- a/src/renderer/components/dialog/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./dialog"; diff --git a/src/renderer/components/dialog/logs-dialog.scss b/src/renderer/components/dialog/logs-dialog.scss deleted file mode 100644 index 692152b84a..0000000000 --- a/src/renderer/components/dialog/logs-dialog.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.LogsDialog { - .Wizard { - --wizard-width: 70vw; - } - - .WizardStep { - .step-content { - padding: var(--wizard-spacing); - } - - code { - max-height: 50vh; - } - } -} diff --git a/src/renderer/components/dialog/logs-dialog.tsx b/src/renderer/components/dialog/logs-dialog.tsx deleted file mode 100644 index cd1861db77..0000000000 --- a/src/renderer/components/dialog/logs-dialog.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./logs-dialog.scss"; - -import React from "react"; -import type { DialogProps } from "../dialog"; -import { Dialog } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import type { ShowNotification } from "../notifications"; -import { Button } from "../button"; -import { Icon } from "../icon"; -import { clipboard } from "electron"; -import { kebabCase } from "lodash/fp"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; - -export interface LogsDialogProps extends DialogProps { - title: string; - logs: string; -} - -interface Dependencies { - showSuccessNotification: ShowNotification; -} - -const NonInjectedLogsDialog = (props: LogsDialogProps & Dependencies) => { - const { - title, - logs, - showSuccessNotification, - ...dialogProps - } = props; - - return ( - - {title}} - done={dialogProps.close} - > - - - -
- )} - > - - {logs || "There are no logs available."} - - - - - ); -}; - -export const LogsDialog = withInjectables(NonInjectedLogsDialog, { - getProps: (di, props) => ({ - ...props, - showSuccessNotification: di.inject(showSuccessNotificationInjectable), - }), -}); diff --git a/src/renderer/components/dock/__test__/dock-store.test.ts b/src/renderer/components/dock/__test__/dock-store.test.ts deleted file mode 100644 index 1fd7e102e8..0000000000 --- a/src/renderer/components/dock/__test__/dock-store.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import hostedClusterIdInjectable from "../../../cluster-frame-context/hosted-cluster-id.injectable"; -import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DockStore, DockTab } from "../dock/store"; -import { TabKind } from "../dock/store"; -import dockStoreInjectable from "../dock/store.injectable"; - -const initialTabs: DockTab[] = [ - { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false }, - { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource", pinned: false }, - { id: "edit", kind: TabKind.EDIT_RESOURCE, title: "Edit resource", pinned: false }, - { id: "install", kind: TabKind.INSTALL_CHART, title: "Install chart", pinned: false }, - { id: "logs", kind: TabKind.POD_LOGS, title: "Logs", pinned: false }, -]; - -describe("DockStore", () => { - let dockStore: DockStore; - - beforeEach(() => { - const di = getDiForUnitTesting({ doGeneralOverrides: true }); - - di.override(hostedClusterIdInjectable, () => "some-cluster-id"); - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - - dockStore = di.inject(dockStoreInjectable); - }); - - it("closes tab and selects one from right", () => { - dockStore.tabs = initialTabs; - dockStore.closeTab(dockStore.tabs[0].id); - - expect(dockStore.selectedTabId).toBe("create"); - - dockStore.selectTab("edit"); - dockStore.closeTab("edit"); - - expect(dockStore.selectedTabId).toBe("install"); - }); - - it("closes last tab and selects one from right", () => { - dockStore.tabs = initialTabs; - dockStore.selectTab("logs"); - dockStore.closeTab("logs"); - - expect(dockStore.selectedTabId).toBe("install"); - }); - - it("closes tab and selects the last one", () => { - dockStore.tabs = [ - { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false }, - { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource", pinned: false }, - ]; - dockStore.closeTab("terminal"); - - expect(dockStore.selectedTabId).toBe("create"); - }); - - it("closes last tab and selects none", () => { - dockStore.tabs = [ - { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource", pinned: false }, - ]; - dockStore.closeTab("create"); - - expect(dockStore.selectedTabId).toBeUndefined(); - }); - - it("doesn't change selected tab if other tab closed", () => { - dockStore.tabs = initialTabs; - dockStore.closeTab("install"); - - expect(dockStore.selectedTabId).toBe("terminal"); - }); -}); diff --git a/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts b/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts deleted file mode 100644 index 599696109c..0000000000 --- a/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { TabId } from "../dock/store"; -import createResourceTabStoreInjectable from "./store.injectable"; - -const clearCreateResourceTabDataInjectable = getInjectable({ - id: "clear-create-resource-tab-data", - - instantiate: (di) => { - const createResourceTabStore = di.inject(createResourceTabStoreInjectable); - - return (tabId: TabId): void => { - createResourceTabStore.clearData(tabId); - }; - }, -}); - -export default clearCreateResourceTabDataInjectable; diff --git a/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts b/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts deleted file mode 100644 index 1218109286..0000000000 --- a/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 dockStoreInjectable from "../dock/store.injectable"; -import type { DockTabCreateSpecific } from "../dock/store"; -import { TabKind } from "../dock/store"; - -const createResourceTabInjectable = getInjectable({ - id: "create-resource-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - - return (tabParams: DockTabCreateSpecific = {}) => - dockStore.createTab({ - title: "Create resource", - ...tabParams, - kind: TabKind.CREATE_RESOURCE, - }); - }, -}); - -export default createResourceTabInjectable; diff --git a/src/renderer/components/dock/create-resource/create-resource-templates.injectable.ts b/src/renderer/components/dock/create-resource/create-resource-templates.injectable.ts deleted file mode 100644 index ecb00e5efb..0000000000 --- a/src/renderer/components/dock/create-resource/create-resource-templates.injectable.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 userCreateResourceTemplatesInjectable from "./user-templates.injectable"; -import lensCreateResourceTemplatesInjectable from "./lens-templates.injectable"; -import type { GroupBase } from "react-select"; - -export type RawTemplates = [group: string, items: [file: string, contents: string][]]; - -const createResourceTemplatesInjectable = getInjectable({ - id: "create-resource-templates", - - instantiate: async (di) => { - const lensResourceTemplates = await di.inject(lensCreateResourceTemplatesInjectable); - const userResourceTemplates = di.inject(userCreateResourceTemplatesInjectable); - - return computed((): GroupBase<{ label: string; value: string }>[] => { - const res = [ - ...userResourceTemplates.get(), - lensResourceTemplates, - ]; - - return res.map(([group, items]) => ({ - label: group, - options: items.map(([label, value]) => ({ label, value })), - })); - }); - }, -}); - -export default createResourceTemplatesInjectable; diff --git a/src/renderer/components/dock/create-resource/has-correct-extension.ts b/src/renderer/components/dock/create-resource/has-correct-extension.ts deleted file mode 100644 index 47768161d6..0000000000 --- a/src/renderer/components/dock/create-resource/has-correct-extension.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -const extensionMatchers = [ - /\.yaml$/, - /\.yml$/, - /\.json$/, -]; - -/** - * Check if a fileName matches a yaml or json file name structure - * @param fileName The fileName to check - */ -export function hasCorrectExtension(fileName: string): boolean { - return extensionMatchers.some(matcher => matcher.test(fileName)); -} diff --git a/src/renderer/components/dock/create-resource/lens-templates.injectable.ts b/src/renderer/components/dock/create-resource/lens-templates.injectable.ts deleted file mode 100644 index 5e97fdb4d7..0000000000 --- a/src/renderer/components/dock/create-resource/lens-templates.injectable.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 { hasCorrectExtension } from "./has-correct-extension"; -import readFileInjectable from "../../../../common/fs/read-file.injectable"; -import readDirectoryInjectable from "../../../../common/fs/read-directory.injectable"; -import type { RawTemplates } from "./create-resource-templates.injectable"; -import joinPathsInjectable from "../../../../common/path/join-paths.injectable"; -import parsePathInjectable from "../../../../common/path/parse.injectable"; -import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable"; - -const lensCreateResourceTemplatesInjectable = getInjectable({ - id: "lens-create-resource-templates", - - instantiate: async (di): Promise => { - const readFile = di.inject(readFileInjectable); - const readDir = di.inject(readDirectoryInjectable); - const joinPaths = di.inject(joinPathsInjectable); - const parsePath = di.inject(parsePathInjectable); - const resourcesDirectory = di.inject(lensResourcesDirInjectable); - - /** - * Mapping between file names and their contents - */ - const templates: [file: string, contents: string][] = []; - const templatesFolder = joinPaths(resourcesDirectory, "templates/create-resource"); - - for (const dirEntry of await readDir(templatesFolder)) { - if (hasCorrectExtension(dirEntry)) { - templates.push([parsePath(dirEntry).name, await readFile(joinPaths(templatesFolder, dirEntry))]); - } - } - - return ["lens", templates]; - }, -}); - -export default lensCreateResourceTemplatesInjectable; diff --git a/src/renderer/components/dock/create-resource/store.injectable.ts b/src/renderer/components/dock/create-resource/store.injectable.ts deleted file mode 100644 index 4cad7384d8..0000000000 --- a/src/renderer/components/dock/create-resource/store.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 { CreateResourceTabStore } from "./store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; - -const createResourceTabStoreInjectable = getInjectable({ - id: "create-resource-tab-store", - - instantiate: (di) => new CreateResourceTabStore({ - createStorage: di.inject(createStorageInjectable), - }), -}); - -export default createResourceTabStoreInjectable; diff --git a/src/renderer/components/dock/create-resource/store.ts b/src/renderer/components/dock/create-resource/store.ts deleted file mode 100644 index 13e0757314..0000000000 --- a/src/renderer/components/dock/create-resource/store.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DockTabStoreDependencies } from "../dock-tab-store/dock-tab.store"; -import { DockTabStore } from "../dock-tab-store/dock-tab.store"; - -export interface CreateResourceTabStoreDependencies extends DockTabStoreDependencies { -} - -export class CreateResourceTabStore extends DockTabStore { - constructor(protected readonly dependencies: CreateResourceTabStoreDependencies) { - super(dependencies, { - storageKey: "create_resource", - }); - } -} diff --git a/src/renderer/components/dock/create-resource/user-templates.injectable.ts b/src/renderer/components/dock/create-resource/user-templates.injectable.ts deleted file mode 100644 index 65b896f95d..0000000000 --- a/src/renderer/components/dock/create-resource/user-templates.injectable.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * 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, observable } from "mobx"; -import { delay, getOrInsert, isErrnoException, waitForPath } from "../../../utils"; -import { readFile } from "fs/promises"; -import { hasCorrectExtension } from "./has-correct-extension"; -import type { RawTemplates } from "./create-resource-templates.injectable"; -import joinPathsInjectable from "../../../../common/path/join-paths.injectable"; -import watchInjectable from "../../../../common/fs/watch/watch.injectable"; -import getRelativePathInjectable from "../../../../common/path/get-relative-path.injectable"; -import homeDirectoryPathInjectable from "../../../../common/os/home-directory-path.injectable"; -import getDirnameOfPathInjectable from "../../../../common/path/get-dirname.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; -import parsePathInjectable from "../../../../common/path/parse.injectable"; - -const userCreateResourceTemplatesInjectable = getInjectable({ - id: "user-create-resource-templates", - instantiate: (di) => { - const joinPaths = di.inject(joinPathsInjectable); - const watch = di.inject(watchInjectable); - const getRelativePath = di.inject(getRelativePathInjectable); - const homeDirectoryPath = di.inject(homeDirectoryPathInjectable); - const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); - const logger = di.inject(loggerInjectable); - const parsePath = di.inject(parsePathInjectable); - - const userTemplatesFolder = joinPaths(homeDirectoryPath, ".k8slens", "templates"); - const groupTemplates = (templates: Map): RawTemplates[] => { - const res = new Map(); - - for (const [filePath, contents] of templates) { - const rawRelative = getDirnameOfPath(getRelativePath(userTemplatesFolder, filePath)); - const title = rawRelative === "." - ? "ungrouped" - : rawRelative; - - getOrInsert(res, title, []).push([parsePath(filePath).name, contents]); - } - - return [...res.entries()]; - }; - - /** - * Map between filePaths and template contents - */ - const templates = observable.map(); - - const onAddOrChange = async (filePath: string) => { - if (!hasCorrectExtension(filePath)) { - // ignore non yaml or json files - return; - } - - try { - const contents = await readFile(filePath, "utf-8"); - - templates.set(filePath, contents); - } catch (error) { - if (isErrnoException(error) && error.code === "ENOENT") { - // ignore, file disappeared - } else { - logger.warn(`[USER-CREATE-RESOURCE-TEMPLATES]: encountered error while reading ${filePath}`, error); - } - } - }; - const onUnlink = (filePath: string) => { - templates.delete(filePath); - }; - - (async () => { - for (let i = 1;; i *= 2) { - try { - await waitForPath(userTemplatesFolder); - break; - } catch (error) { - logger.warn(`[USER-CREATE-RESOURCE-TEMPLATES]: encountered error while waiting for ${userTemplatesFolder} to exist, waiting and trying again`, error); - await delay(i * 1000); // exponential backoff in seconds - } - } - - /** - * NOTE: There is technically a race condition here of the form "time-of-check to time-of-use" - */ - watch(userTemplatesFolder, { - disableGlobbing: true, - ignorePermissionErrors: true, - usePolling: false, - awaitWriteFinish: { - pollInterval: 100, - stabilityThreshold: 1000, - }, - ignoreInitial: false, - atomic: 150, // for "atomic writes" - }) - .on("add", onAddOrChange) - .on("change", onAddOrChange) - .on("unlink", onUnlink) - .on("error", error => { - logger.warn(`[USER-CREATE-RESOURCE-TEMPLATES]: encountered error while watching files under ${userTemplatesFolder}`, error); - }); - })(); - - return computed(() => groupTemplates(templates)); - }, -}); - -export default userCreateResourceTemplatesInjectable; diff --git a/src/renderer/components/dock/create-resource/view.tsx b/src/renderer/components/dock/create-resource/view.tsx deleted file mode 100644 index 00221a7b32..0000000000 --- a/src/renderer/components/dock/create-resource/view.tsx +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import type { SelectOption } from "../../select"; -import { Select } from "../../select"; -import yaml, { dump } from "js-yaml"; -import type { IComputedValue } from "mobx"; -import { makeObservable, observable } from "mobx"; -import { observer } from "mobx-react"; -import type { CreateResourceTabStore } from "./store"; -import { EditorPanel } from "../editor-panel"; -import { InfoPanel } from "../info-panel"; -import type { ShowNotification } from "../../notifications"; -import type { Logger } from "../../../../common/logger"; -import type { ApiManager } from "../../../../common/k8s-api/api-manager"; -import { isObject, prevDefault } from "../../../utils"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import createResourceTabStoreInjectable from "./store.injectable"; -import createResourceTemplatesInjectable from "./create-resource-templates.injectable"; -import { Spinner } from "../../spinner"; -import type { GroupBase } from "react-select"; -import type { Navigate } from "../../../navigation/navigate.injectable"; -import type { GetDetailsUrl } from "../../kube-detail-params/get-details-url.injectable"; -import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable"; -import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable"; -import navigateInjectable from "../../../navigation/navigate.injectable"; -import type { RequestKubeObjectCreation } from "../../../../common/k8s-api/endpoints/resource-applier.api/request-update.injectable"; -import requestKubeObjectCreationInjectable from "../../../../common/k8s-api/endpoints/resource-applier.api/request-update.injectable"; -import loggerInjectable from "../../../../common/logger.injectable"; -import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; -import showSuccessNotificationInjectable from "../../notifications/show-success-notification.injectable"; -import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; - -export interface CreateResourceProps { - tabId: string; -} - -interface Dependencies { - createResourceTemplates: IComputedValue[]>; - createResourceTabStore: CreateResourceTabStore; - apiManager: ApiManager; - logger: Logger; - navigate: Navigate; - getDetailsUrl: GetDetailsUrl; - requestKubeObjectCreation: RequestKubeObjectCreation; - showSuccessNotification: ShowNotification; - showCheckedErrorNotification: ShowCheckedErrorNotification; -} - -@observer -class NonInjectedCreateResource extends React.Component { - @observable error = ""; - - constructor(props: CreateResourceProps & Dependencies) { - super(props); - makeObservable(this); - } - - get tabId() { - return this.props.tabId; - } - - get data() { - return this.props.createResourceTabStore.getData(this.tabId) ?? ""; - } - - onChange = (value: string) => { - this.error = ""; // reset first, validation goes later - this.props.createResourceTabStore.setData(this.tabId, value); - }; - - onError = (error: Error | string) => { - this.error = error.toString(); - }; - - create = async (): Promise => { - const { apiManager, getDetailsUrl, navigate, requestKubeObjectCreation } = this.props; - - if (this.error || !this.data?.trim()) { - // do not save when field is empty or there is an error - return; - } - - // skip empty documents - const resources = yaml.loadAll(this.data).filter(isObject); - - if (resources.length === 0) { - return this.props.logger.info("Nothing to create"); - } - - const creatingResources = resources.map(async (resource) => { - const result = await requestKubeObjectCreation(dump(resource)); - - if (!result.callWasSuccessful) { - this.props.logger.warn("Failed to create resource", { resource }, result.error); - this.props.showCheckedErrorNotification(result.error, "Unknown error occured while creating resources"); - - return; - } - - const { kind, apiVersion, metadata: { name, namespace }} = result.response; - - const close = this.props.showSuccessNotification(( -

- {kind} - {" "} - { - const resourceLink = apiManager.lookupApiLink({ kind, apiVersion, name, namespace }); - - navigate(getDetailsUrl(resourceLink)); - close(); - })} - > - {name} - - {" successfully created."} -

- )); - }); - - await Promise.allSettled(creatingResources); - }; - - renderControls() { - return ( -
- , false> - id="create-resource-resource-templates-input" - controlShouldRenderValue={false} // always keep initial placeholder - className="TemplateSelect" - placeholder="Select Template ..." - options={this.props.createResourceTemplates.get()} - formatGroupLabel={group => group.label} - menuPlacement="top" - themeName="outlined" - onChange={(option) => { - if (option) { - this.props.createResourceTabStore.setData(this.tabId, option.value); - } - }} - /> -
- ); - } - - render() { - const { tabId, data, error } = this; - - return ( -
- - -
- ); - } -} - -export const CreateResource = withInjectables(NonInjectedCreateResource, { - getPlaceholder: () => , - - getProps: async (di, props) => ({ - ...props, - createResourceTabStore: di.inject(createResourceTabStoreInjectable), - createResourceTemplates: await di.inject(createResourceTemplatesInjectable), - apiManager: di.inject(apiManagerInjectable), - logger: di.inject(loggerInjectable), - getDetailsUrl: di.inject(getDetailsUrlInjectable), - navigate: di.inject(navigateInjectable), - requestKubeObjectCreation: di.inject(requestKubeObjectCreationInjectable), - showSuccessNotification: di.inject(showSuccessNotificationInjectable), - showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), - }), -}); diff --git a/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts b/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts deleted file mode 100644 index 0fa8a3bdf8..0000000000 --- a/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 { DockTabStoreOptions } from "./dock-tab.store"; -import { DockTabStore } from "./dock-tab.store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; - -const createDockTabStoreInjectable = getInjectable({ - id: "create-dock-tab-store", - - instantiate: (di) => { - const dependencies = { - createStorage: di.inject(createStorageInjectable), - }; - - return (options: DockTabStoreOptions = {}) => new DockTabStore(dependencies, options); - }, -}); - -export default createDockTabStoreInjectable; diff --git a/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts b/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts deleted file mode 100644 index d6ec705d2a..0000000000 --- a/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, observable, reaction } from "mobx"; -import type { StorageLayer } from "../../../utils"; -import { autoBind, toJS } from "../../../utils"; -import type { CreateStorage } from "../../../utils/create-storage/create-storage.injectable"; -import type { TabId } from "../dock/store"; - -export interface DockTabStoreOptions { - autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true - storageKey?: string; // save data to persistent storage under the key -} - -export type DockTabStorageState = Record; - -export interface DockTabStoreDependencies { - createStorage: CreateStorage; -} - -export class DockTabStore { - protected readonly storage?: StorageLayer>; - private readonly data = observable.map(); - - constructor(protected readonly dependencies: DockTabStoreDependencies, protected readonly options: DockTabStoreOptions) { - autoBind(this); - this.options.autoInit ??= true; - - const { storageKey, autoInit } = this.options; - - if (autoInit && storageKey) { - const storage = this.storage = this.dependencies.createStorage(storageKey, {}); - - this.data.replace(storage.get()); - reaction(() => this.toJSON(), data => storage.set(data)); - } - } - - protected finalizeDataForSave(data: T): T { - return data; - } - - protected toJSON(): DockTabStorageState { - const deepCopy = toJS(this.data); - - deepCopy.forEach((tabData, key) => { - deepCopy.set(key, this.finalizeDataForSave(tabData)); - }); - - return Object.fromEntries(deepCopy); - } - - protected getAllData() { - return this.data.toJSON(); - } - - findTabIdFromData(inspecter: (val: T) => any): TabId | undefined { - for (const [tabId, data] of this.data) { - if (inspecter(data)) { - return tabId; - } - } - - return undefined; - } - - isReady(tabId: TabId): boolean { - return this.getData(tabId) !== undefined; - } - - getData(tabId: TabId) { - return this.data.get(tabId); - } - - setData(tabId: TabId, data: T) { - this.data.set(tabId, data); - } - - clearData(tabId: TabId) { - this.data.delete(tabId); - } - - @action - reset() { - for (const tabId of this.data.keys()) { - this.clearData(tabId); - } - this.storage?.reset(); - } -} diff --git a/src/renderer/components/dock/dock-tab.module.scss b/src/renderer/components/dock/dock-tab.module.scss deleted file mode 100644 index 64bd4768ff..0000000000 --- a/src/renderer/components/dock/dock-tab.module.scss +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.DockTab { - --color-active: var(--dockTabActiveBackground); - --color-text-active: var(--textColorAccent); - --color-border-active: var(--primary); - - padding: var(--padding); - height: 32px; - position: relative; - border-right: 1px solid var(--dockTabBorderColor); - background-size: 1px 3ch; - overflow: hidden; - - /* Allow tabs to shrink and take all parent space */ - min-width: var(--min-tab-width); - flex-grow: 1; - flex-basis: 0; - max-width: fit-content; - - &:last-child { - border-right: none; - } - - &.pinned { - padding-right: var(--padding); - } - - &:last-child { - padding-right: var(--padding); - } - - &:global(.active) { - background-color: var(--color-active); - background-image: none; - border-bottom: 1px solid var(--color-border-active); - color: var(--color-text-active)!important; - - .close { - opacity: 1; - } - - &::before { - display: none; - } - } - - &::before { - content: " "; - display: block; - position: absolute; - width: 8px; - height: 100%; - right: 0; - background: linear-gradient(90deg, transparent 0%, var(--dockHeadBackground) 65%); - } - - &::after { - display: none; - } - - &:not(:global(.active)):hover { - background-color: var(--dockTabActiveBackground); - background-image: none; - color: var(--textColorAccent); - - .close { - opacity: 1; - background: linear-gradient(90deg, transparent 0%, var(--dockTabActiveBackground) 25%); - } - - &::before { - display: none; - } - } -} - -.close { - position: absolute; - right: 0px; - width: 4ch; - opacity: 0; - text-align: center; - background: linear-gradient(90deg, transparent 0%, var(--color-active) 25%); -} - -.tabIcon { - opacity: 0; -} - -.title { - overflow: hidden; - text-overflow: ellipsis; - margin-right: calc(var(--margin) * 3); -} diff --git a/src/renderer/components/dock/dock-tab.tsx b/src/renderer/components/dock/dock-tab.tsx deleted file mode 100644 index 5a672e2fb3..0000000000 --- a/src/renderer/components/dock/dock-tab.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./dock-tab.module.scss"; - -import React from "react"; -import { observer } from "mobx-react"; -import { autoBind, cssNames, prevDefault, isMiddleClick } from "../../utils"; -import type { DockStore, DockTab as DockTabModel } from "./dock/store"; -import type { TabProps } from "../tabs"; -import { Tab } from "../tabs"; -import { Icon } from "../icon"; -import { Menu, MenuItem } from "../menu"; -import { observable } from "mobx"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import dockStoreInjectable from "./dock/store.injectable"; -import { Tooltip, TooltipPosition } from "../tooltip"; -import isMacInjectable from "../../../common/vars/is-mac.injectable"; - -export interface DockTabProps extends TabProps { - moreActions?: React.ReactNode; -} - -interface Dependencies { - dockStore: DockStore; - isMac: boolean; -} - -@observer -class NonInjectedDockTab extends React.Component { - private readonly menuVisible = observable.box(false); - - constructor(props: DockTabProps & Dependencies) { - super(props); - autoBind(this); - } - - close(id: string) { - this.props.dockStore.closeTab(id); - } - - renderMenu(tabId: string) { - const { closeTab, closeAllTabs, closeOtherTabs, closeTabsToTheRight, tabs, getTabIndex } = this.props.dockStore; - const closeAllDisabled = tabs.length === 1; - const closeOtherDisabled = tabs.length === 1; - const closeRightDisabled = getTabIndex(tabId) === tabs.length - 1; - - return ( - this.menuVisible.set(true)} - close={() => this.menuVisible.set(false)} - toggleEvent="contextmenu" - > - closeTab(tabId)}> - Close - - closeAllTabs()} disabled={closeAllDisabled}> - Close all tabs - - closeOtherTabs(tabId)} disabled={closeOtherDisabled}> - Close other tabs - - closeTabsToTheRight(tabId)} disabled={closeRightDisabled}> - Close tabs to the right - - - ); - } - - render() { - const { className, moreActions, dockStore, active, isMac, ...tabProps } = this.props; - - if (!tabProps.value) { - return; - } - - const { title, pinned, id } = tabProps.value; - const close = prevDefault(() => this.close(id)); - - return ( - <> - this.menuVisible.set(true)} - label={( -
- {title} - {moreActions} - {!pinned && ( -
- -
- )} - - {title} - -
- )} - data-testid={`dock-tab-for-${id}`} - /> - {this.renderMenu(id)} - - ); - } -} - -export const DockTab = withInjectables(NonInjectedDockTab, { - getProps: (di, props) => ({ - dockStore: di.inject(dockStoreInjectable), - isMac: di.inject(isMacInjectable), - ...props, - }), -}); diff --git a/src/renderer/components/dock/dock-tabs.module.scss b/src/renderer/components/dock/dock-tabs.module.scss deleted file mode 100644 index 4a68a00c8a..0000000000 --- a/src/renderer/components/dock/dock-tabs.module.scss +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.dockTabs { - --min-tab-width: 120px; - - overflow: hidden; -} - -.tabs { - width: 100%; - - display: flex; - overflow: hidden; - - &:empty { - display: none; - } - - &:global(.scrollable) { - overflow: auto; - overflow-x: overlay; /* Set scrollbar inside content area */ - - &::-webkit-scrollbar-thumb { - background-color: transparent; - } - - &:hover { - &::-webkit-scrollbar { - width: 100%; - height: 3px; - } - - &::-webkit-scrollbar-thumb { - border-radius: 0; - height: 3px; - background-color: rgba(106, 115, 125, 0.2); - } - } - - &::before, &::after { - content: "\00A0"; - position: sticky; - min-width: 8px; - z-index: 1; - } - - &::before { - left: 0; - background: linear-gradient(270deg, transparent 0%, var(--dockHeadBackground) 65%); - } - - &::after { - right: 0; - background: linear-gradient(90deg, transparent 0%, var(--dockHeadBackground) 65%); - } - } -} diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx deleted file mode 100644 index 87b4aa756b..0000000000 --- a/src/renderer/components/dock/dock-tabs.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./dock-tabs.module.scss"; - -import React, { Fragment, useEffect, useRef, useState } from "react"; -import { Tabs } from "../tabs/tabs"; -import { DockTab } from "./dock-tab"; -import type { DockTab as DockTabModel } from "./dock/store"; -import { TabKind } from "./dock/store"; -import { TerminalTab } from "./terminal/dock-tab"; -import { cssVar } from "../../utils"; -import { useResizeObserver } from "../../hooks"; - -export interface DockTabsProps { - tabs: DockTabModel[]; - autoFocus: boolean; - selectedTab: DockTabModel | undefined; - onChangeTab: (tab: DockTabModel) => void; -} - -export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: DockTabsProps) => { - const elem = useRef(null); - const minTabSize = useRef(0); - const [showScrollbar, setShowScrollbar] = useState(false); - - const getTabElements = (): HTMLDivElement[] => { - return Array.from(elem.current?.querySelectorAll(".Tabs .Tab") ?? []); - }; - - const renderTab = (tab?: DockTabModel) => { - if (!tab) { - return null; - } - - switch (tab.kind) { - case TabKind.CREATE_RESOURCE: - case TabKind.EDIT_RESOURCE: - return ; - case TabKind.INSTALL_CHART: - case TabKind.UPGRADE_CHART: - return ; - case TabKind.POD_LOGS: - return ; - case TabKind.TERMINAL: - return ; - } - }; - - const scrollActiveTabIntoView = () => { - const tab = elem.current?.querySelector(".Tab.active"); - - tab?.scrollIntoView(); - }; - - const updateScrollbarVisibility = () => { - const allTabsShrunk = getTabElements().every(tab => tab.offsetWidth == minTabSize.current); - - setShowScrollbar(allTabsShrunk); - }; - - const scrollTabsWithMouseWheel = (left: number) => { - elem.current?.children[0]?.scrollBy({ left }); - }; - - const onMouseWheel = (event: React.WheelEvent) => { - scrollTabsWithMouseWheel(event.deltaY); - }; - - useEffect(() => { - if (elem.current) { - const cssVars = cssVar(elem.current); - - minTabSize.current = cssVars.get("--min-tab-width").valueOf(); - } - }); - - useResizeObserver(elem.current, () => { - scrollActiveTabIntoView(); - updateScrollbarVisibility(); - }); - - return ( -
- - {tabs.map(tab => {renderTab(tab)})} - -
- ); -}; diff --git a/src/renderer/components/dock/dock.scss b/src/renderer/components/dock/dock.scss deleted file mode 100644 index 216923523f..0000000000 --- a/src/renderer/components/dock/dock.scss +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.Dock { - position: relative; - background: var(--dockHeadBackground); - display: flex; - flex-direction: column; - - &:not(:focus-within) .DockTab.active { - &::after { - color: var(--halfGray); - } - - &:hover::after { - color: var(--line-color-active); - } - } - - &.isOpen { - &.fullSize { - position: fixed; - top: 0; - right: 0; - left: 0; - bottom: 0; - z-index: 100; - } - } - - &:not(.isOpen) { - height: auto !important; - - .Tab { - --color-active: var(--colorVague); - --color-text-active: inherit; - --color-border-active: transparent; - - &:not(:focus):after { - display: none; - } - } - } - - .tabs-container { - padding: 0 $padding * 2; - border-top: 1px solid var(--borderColor); - flex-shrink: 0; - - .Tabs:empty + .toolbar { - padding-left: 0; - } - - .toolbar { - min-height: $unit * 4; - padding-left: $padding; - user-select: none; - - &.pl-0 { - padding-left: 0; - } - } - } - - .tab-content { - position: relative; - flex: 1; - overflow: hidden; - transition: flex-basis 25ms ease-in; - background: var(--dockInfoBackground); - - > *:not(.Spinner) { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - } - } -} diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx deleted file mode 100644 index ee4ff542b3..0000000000 --- a/src/renderer/components/dock/dock.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import "./dock.scss"; -import React from "react"; -import { observer } from "mobx-react"; -import { cssNames } from "../../utils"; -import { Icon } from "../icon"; -import { MenuItem } from "../menu"; -import { MenuActions } from "../menu/menu-actions"; -import { ResizeDirection, ResizingAnchor } from "../resizing-anchor"; -import { CreateResource } from "./create-resource/view"; -import { DockTabs } from "./dock-tabs"; -import type { DockStore, DockTab } from "./dock/store"; -import { TabKind } from "./dock/store"; -import { EditResource } from "./edit-resource/view"; -import { InstallChart } from "./install-chart/view"; -import { LogsDockTab } from "./logs/view"; -import { TerminalWindow } from "./terminal/view"; -import { UpgradeChart } from "./upgrade-chart/view"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import createResourceTabInjectable from "./create-resource/create-resource-tab.injectable"; -import dockStoreInjectable from "./dock/store.injectable"; -import createTerminalTabInjectable from "./terminal/create-terminal-tab.injectable"; -import { ErrorBoundary } from "../error-boundary"; - -export interface DockProps { - className?: string; -} - -interface Dependencies { - createResourceTab: () => void; - createTerminalTab: () => void; - dockStore: DockStore; -} - -enum Direction { - NEXT = 1, - PREV = -1, -} - -@observer -class NonInjectedDock extends React.Component { - private readonly element = React.createRef(); - - componentDidMount() { - document.addEventListener("keydown", this.onKeyDown); - } - - componentWillUnmount() { - document.removeEventListener("keydown", this.onKeyDown); - } - - onKeyDown = (evt: KeyboardEvent) => { - const { close, selectedTab, closeTab } = this.props.dockStore; - const { code, ctrlKey, metaKey, shiftKey } = evt; - - // Determine if user working inside or using any other areas in app - const dockIsFocused = this.element.current?.contains(document.activeElement); - - if (!selectedTab || !dockIsFocused) return; - - if (shiftKey && code === "Escape") { - close(); - } - - if ((ctrlKey && code === "KeyW") || (metaKey && code === "KeyW")) { - closeTab(selectedTab.id); - this.element.current?.focus(); // Avoid loosing focus when closing tab - } - - if(ctrlKey && code === "Period") { - this.switchToNextTab(selectedTab, Direction.NEXT); - } - - if(ctrlKey && code === "Comma") { - this.switchToNextTab(selectedTab, Direction.PREV); - } - }; - - onChangeTab = (tab: DockTab) => { - const { open, selectTab } = this.props.dockStore; - - open(); - selectTab(tab.id); - this.element.current?.focus(); - }; - - switchToNextTab = (selectedTab: DockTab, direction: Direction) => { - const { tabs } = this.props.dockStore; - const currentIndex = tabs.indexOf(selectedTab); - const nextIndex = currentIndex + direction; - - // check if moving to the next or previous tab is possible. - if (nextIndex >= tabs.length || nextIndex < 0) return; - - const nextElement = tabs[nextIndex]; - - this.onChangeTab(nextElement); - }; - - renderTab(tab: DockTab) { - switch (tab.kind) { - case TabKind.CREATE_RESOURCE: - return ; - case TabKind.EDIT_RESOURCE: - return ; - case TabKind.INSTALL_CHART: - return ; - case TabKind.UPGRADE_CHART: - return ; - case TabKind.POD_LOGS: - return ; - case TabKind.TERMINAL: - return ; - } - } - - renderTabContent() { - const { isOpen, height, selectedTab } = this.props.dockStore; - - if (!isOpen || !selectedTab) return null; - - return ( -
- {this.renderTab(selectedTab)} -
- ); - } - - render() { - const { className, dockStore } = this.props; - const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize } = this.props.dockStore; - - return ( -
- dockStore.height} - minExtent={dockStore.minHeight} - maxExtent={dockStore.maxHeight} - direction={ResizeDirection.VERTICAL} - onStart={dockStore.open} - onMinExtentSubceed={dockStore.close} - onMinExtentExceed={dockStore.open} - onDrag={extent => dockStore.height = extent} - /> -
- -
-
- - this.props.createTerminalTab()}> - - Terminal session - - this.props.createResourceTab()}> - - Create resource - - -
- {hasTabs() && ( - <> - - - - )} -
-
- - {this.renderTabContent()} - -
- ); - } -} - -export const Dock = withInjectables( - NonInjectedDock, - - { - getProps: (di, props) => ({ - createResourceTab: di.inject(createResourceTabInjectable), - dockStore: di.inject(dockStoreInjectable), - createTerminalTab: di.inject(createTerminalTabInjectable), - ...props, - }), - }, -); - diff --git a/src/renderer/components/dock/dock/close-dock-tab.injectable.ts b/src/renderer/components/dock/dock/close-dock-tab.injectable.ts deleted file mode 100644 index ab7fdca2cd..0000000000 --- a/src/renderer/components/dock/dock/close-dock-tab.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { TabId } from "./store"; -import dockStoreInjectable from "./store.injectable"; - -const closeDockTabInjectable = getInjectable({ - id: "close-dock-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - - return (tabId: TabId): void => { - dockStore.closeTab(tabId); - }; - }, -}); - -export default closeDockTabInjectable; diff --git a/src/renderer/components/dock/dock/create-dock-tab.injectable.ts b/src/renderer/components/dock/dock/create-dock-tab.injectable.ts deleted file mode 100644 index 84ee7c868e..0000000000 --- a/src/renderer/components/dock/dock/create-dock-tab.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 dockStoreInjectable from "./store.injectable"; -import type { DockTab, DockTabCreate } from "./store"; - -const createDockTabInjectable = getInjectable({ - id: "create-dock-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - - return (rawTabDesc: DockTabCreate, addNumber?: boolean): DockTab => - dockStore.createTab(rawTabDesc, addNumber); - }, -}); - -export default createDockTabInjectable; diff --git a/src/renderer/components/dock/dock/dock-storage.injectable.ts b/src/renderer/components/dock/dock/dock-storage.injectable.ts deleted file mode 100644 index ce2754dcab..0000000000 --- a/src/renderer/components/dock/dock/dock-storage.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; -import type { DockStorageState } from "./store"; -import { TabKind } from "./store"; - -const dockStorageInjectable = getInjectable({ - id: "dock-storage", - - instantiate: (di) => { - const createStorage = di.inject(createStorageInjectable); - - return createStorage("dock", { - height: 300, - tabs: [ - { - id: "terminal", - kind: TabKind.TERMINAL, - title: "Terminal", - pinned: false, - }, - ], - isOpen: false, - }); - }, -}); - -export default dockStorageInjectable; diff --git a/src/renderer/components/dock/dock/rename-tab.injectable.ts b/src/renderer/components/dock/dock/rename-tab.injectable.ts deleted file mode 100644 index b9f24f9cfc..0000000000 --- a/src/renderer/components/dock/dock/rename-tab.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 dockStoreInjectable from "./store.injectable"; -import type { TabId } from "./store"; - -const renameTabInjectable = getInjectable({ - id: "rename-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - - return (tabId: TabId, title: string): void => { - dockStore.renameTab(tabId, title); - }; - }, -}); - -export default renameTabInjectable; diff --git a/src/renderer/components/dock/dock/select-dock-tab.injectable.ts b/src/renderer/components/dock/dock/select-dock-tab.injectable.ts deleted file mode 100644 index 7951604deb..0000000000 --- a/src/renderer/components/dock/dock/select-dock-tab.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { TabId } from "./store"; -import dockStoreInjectable from "./store.injectable"; - -const selectDockTabInjectable = getInjectable({ - id: "select-dock-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - - return (tabId: TabId): void => { - dockStore.selectTab(tabId); - }; - }, -}); - -export default selectDockTabInjectable; diff --git a/src/renderer/components/dock/dock/store.injectable.ts b/src/renderer/components/dock/dock/store.injectable.ts deleted file mode 100644 index dac05009ac..0000000000 --- a/src/renderer/components/dock/dock/store.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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 { DockStore, TabKind } from "./store"; -import dockStorageInjectable from "./dock-storage.injectable"; -import clearLogTabDataInjectable from "../logs/clear-log-tab-data.injectable"; -import clearUpgradeChartTabDataInjectable from "../upgrade-chart/clear-upgrade-chart-tab-data.injectable"; -import clearCreateResourceTabDataInjectable from "../create-resource/clear-create-resource-tab-data.injectable"; -import clearEditResourceTabDataInjectable from "../edit-resource/clear-edit-resource-tab-data.injectable"; -import clearTerminalTabDataInjectable from "../terminal/clear-terminal-tab-data.injectable"; -import clearInstallChartTabDataInjectable from "../install-chart/clear-install-chart-tab-data.injectable"; -import isLogsTabDataValidInjectable from "../logs/is-logs-tab-data-valid.injectable"; - -const dockStoreInjectable = getInjectable({ - id: "dock-store", - - instantiate: (di) => new DockStore({ - storage: di.inject(dockStorageInjectable), - tabDataClearers: { - [TabKind.POD_LOGS]: di.inject(clearLogTabDataInjectable), - [TabKind.UPGRADE_CHART]: di.inject(clearUpgradeChartTabDataInjectable), - [TabKind.CREATE_RESOURCE]: di.inject(clearCreateResourceTabDataInjectable), - [TabKind.EDIT_RESOURCE]: di.inject(clearEditResourceTabDataInjectable), - [TabKind.INSTALL_CHART]: di.inject(clearInstallChartTabDataInjectable), - [TabKind.TERMINAL]: di.inject(clearTerminalTabDataInjectable), - }, - tabDataValidator: { - [TabKind.POD_LOGS]: di.inject(isLogsTabDataValidInjectable), - }, - }), -}); - -export default dockStoreInjectable; diff --git a/src/renderer/components/dock/dock/store.ts b/src/renderer/components/dock/dock/store.ts deleted file mode 100644 index 48e04c3a76..0000000000 --- a/src/renderer/components/dock/dock/store.ts +++ /dev/null @@ -1,383 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as uuid from "uuid"; -import { action, comparer, computed, makeObservable, observable, reaction, runInAction } from "mobx"; -import type { StorageLayer } from "../../../utils"; -import { autoBind } from "../../../utils"; -import throttle from "lodash/throttle"; - -export type TabId = string; - -export enum TabKind { - TERMINAL = "terminal", - CREATE_RESOURCE = "create-resource", - EDIT_RESOURCE = "edit-resource", - INSTALL_CHART = "install-chart", - UPGRADE_CHART = "upgrade-chart", - POD_LOGS = "pod-logs", -} - -/** - * This is the storage model for dock tabs. - * - * All fields are required. - */ -export type DockTab = Required; - -/** - * These are the arguments for creating a new Tab on the dock - */ -export interface DockTabCreate { - /** - * The ID of the tab for reference purposes. - */ - id?: TabId; - - /** - * What kind of dock tab it is - */ - kind: TabKind; - - /** - * The tab's title, defaults to `kind` - */ - title?: string; - - /** - * If true then the dock entry will take up the whole view and will not be - * closable. - */ - pinned?: boolean; - - /** - * Extra fields are supported. - */ - [key: string]: any; -} - -/** - * This type is for function which specifically create a single type of dock tab. - * - * That way users should get a type error if they try and specify a `kind` - * themselves. - */ -export type DockTabCreateSpecific = Omit; - -export interface DockStorageState { - height: number; - tabs: DockTab[]; - selectedTabId?: TabId; - isOpen: boolean; -} - -export interface DockTabChangeEvent { - tab: DockTab; - tabId: TabId; - prevTab?: DockTab; -} - -export interface DockTabChangeEventOptions { - /** - * apply a callback right after initialization - */ - fireImmediately?: boolean; - /** - * filter: by dockStore.selectedTab.kind == tabKind - */ - tabKind?: TabKind; - /** - * filter: dock and selected tab should be visible (default: true) - */ - dockIsVisible?: boolean; -} - -export interface DockTabCloseEvent { - tabId: TabId; // closed tab id -} - -interface Dependencies { - readonly storage: StorageLayer; - readonly tabDataClearers: Record void>; - readonly tabDataValidator: Partial boolean>>; -} - -export class DockStore implements DockStorageState { - constructor(private readonly dependencies: Dependencies) { - makeObservable(this); - autoBind(this); - - // adjust terminal height if window size changes - window.addEventListener("resize", throttle(this.adjustHeight, 250)); - - for (const tab of this.tabs) { - const tabDataIsValid = this.dependencies.tabDataValidator[tab.kind] ?? (() => true); - - if (!tabDataIsValid(tab.id)) { - this.closeTab(tab.id); - } - } - } - - readonly minHeight = 100; - @observable fullSize = false; - - @computed - get isOpen(): boolean { - return this.dependencies.storage.get().isOpen; - } - - set isOpen(isOpen: boolean) { - this.dependencies.storage.merge({ isOpen }); - } - - @computed - get height(): number { - return this.dependencies.storage.get().height; - } - - set height(height: number) { - this.dependencies.storage.merge({ - height: Math.max(this.minHeight, Math.min(height || this.minHeight, this.maxHeight)), - }); - } - - @computed - get tabs(): DockTab[] { - return this.dependencies.storage.get().tabs; - } - - set tabs(tabs: DockTab[]) { - this.dependencies.storage.merge({ tabs }); - } - - @computed - get selectedTabId(): TabId | undefined { - const storageData = this.dependencies.storage.get(); - - return ( - storageData.selectedTabId || - (this.tabs.length > 0 ? this.tabs[0]?.id : undefined) - ); - } - - set selectedTabId(tabId: TabId | undefined) { - if (tabId && !this.getTabById(tabId)) return; // skip invalid ids - - this.dependencies.storage.merge({ selectedTabId: tabId }); - } - - @computed get tabsNumber() : number { - return this.tabs.length; - } - - @computed get selectedTab() { - return this.tabs.find(tab => tab.id === this.selectedTabId); - } - - get maxHeight() { - const mainLayoutHeader = 40; - const mainLayoutTabs = 33; - const mainLayoutMargin = 16; - const dockTabs = 33; - const preferredMax = window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs; - - return Math.max(preferredMax, this.minHeight); // don't let max < min - } - - protected adjustHeight() { - if (this.height < this.minHeight) this.height = this.minHeight; - if (this.height > this.maxHeight) this.height = this.maxHeight; - } - - onResize(callback: () => void, opts: { fireImmediately?: boolean } = {}) { - return reaction(() => [this.height, this.fullSize], callback, { - fireImmediately: opts.fireImmediately, - }); - } - - onTabClose(callback: (evt: DockTabCloseEvent) => void, opts: { fireImmediately?: boolean } = {}) { - return reaction(() => this.tabs.map(tab => tab.id), (tabs: TabId[], prevTabs?: TabId[]) => { - if (!Array.isArray(prevTabs)) { - return; // tabs not yet modified - } - - const closedTabs: TabId[] = prevTabs.filter(id => !tabs.includes(id)); - - if (closedTabs.length > 0) { - runInAction(() => { - closedTabs.forEach(tabId => callback({ tabId })); - }); - } - }, { - equals: comparer.structural, - fireImmediately: opts.fireImmediately, - }); - } - - onTabChange(callback: (evt: DockTabChangeEvent) => void, options: DockTabChangeEventOptions = {}) { - const { tabKind, dockIsVisible = true, ...reactionOpts } = options; - - return reaction(() => this.selectedTab, ((tab, prevTab) => { - if (!tab) return; // skip when dock is empty - if (tabKind && tabKind !== tab.kind) return; // handle specific tab.kind only - if (dockIsVisible && !this.isOpen) return; - - callback({ - tab, prevTab, - tabId: tab.id, - }); - }), reactionOpts); - } - - hasTabs() { - return this.tabs.length > 0; - } - - @action - open(fullSize?: boolean) { - this.isOpen = true; - - if (typeof fullSize === "boolean") { - this.fullSize = fullSize; - } - } - - @action - close() { - this.isOpen = false; - } - - @action - toggle() { - if (this.isOpen) this.close(); - else this.open(); - } - - @action - toggleFillSize() { - if (!this.isOpen) this.open(); - this.fullSize = !this.fullSize; - } - - getTabById(tabId: TabId) { - return this.tabs.find(tab => tab.id === tabId); - } - - getTabIndex(tabId: TabId) { - return this.tabs.findIndex(tab => tab.id === tabId); - } - - protected getNewTabNumber(kind: TabKind) { - const tabNumbers = this.tabs - .filter(tab => tab.kind === kind) - .map(tab => { - const tabNumber = Number(tab.title.match(/\d+/)); - - return tabNumber === 0 ? 1 : tabNumber; // tab without a number is first - }); - - for (let i = 1; ; i++) { - if (!tabNumbers.includes(i)) return i; - } - } - - createTab = action((rawTabDesc: DockTabCreate, addNumber = true): DockTab => { - const { - id = uuid.v4(), - kind, - pinned = false, - ...restOfTabFields - } = rawTabDesc; - let { title = kind } = rawTabDesc; - - if (addNumber) { - const tabNumber = this.getNewTabNumber(kind); - - if (tabNumber > 1) { - title += ` (${tabNumber})`; - } - } - - const tab: DockTab = { - ...restOfTabFields, - id, - kind, - pinned, - title, - }; - - this.tabs.push(tab); - this.selectTab(tab.id); - this.open(); - - return tab; - }); - - @action - closeTab(tabId: TabId) { - const tab = this.getTabById(tabId); - const tabIndex = this.getTabIndex(tabId); - - if (!tab || tab.pinned) { - return; - } - - this.tabs = this.tabs.filter(tab => tab.id !== tabId); - this.dependencies.tabDataClearers[tab.kind](tab.id); - - if (this.selectedTabId === tab.id) { - if (this.tabs.length) { - const newTab = tabIndex < this.tabsNumber ? this.tabs[tabIndex] : this.tabs[tabIndex - 1]; - - this.selectTab(newTab.id); - } else { - this.selectedTabId = undefined; - this.close(); - } - } - } - - @action - closeTabs(tabs: DockTab[]) { - tabs.forEach(tab => this.closeTab(tab.id)); - } - - closeAllTabs() { - this.closeTabs([...this.tabs]); - } - - closeOtherTabs(tabId: TabId) { - const index = this.getTabIndex(tabId); - const tabs = [...this.tabs.slice(0, index), ...this.tabs.slice(index + 1)]; - - this.closeTabs(tabs); - } - - closeTabsToTheRight(tabId: TabId) { - const index = this.getTabIndex(tabId); - const tabs = this.tabs.slice(index + 1); - - this.closeTabs(tabs); - } - - renameTab(tabId: TabId, title: string) { - const tab = this.getTabById(tabId); - - if (tab) { - tab.title = title; - } - } - - @action - selectTab(tabId: TabId) { - this.selectedTabId = this.getTabById(tabId)?.id; - } - - @action - reset() { - this.dependencies.storage?.reset(); - } -} diff --git a/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts b/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts deleted file mode 100644 index 9f48fc2ee9..0000000000 --- a/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { TabId } from "../dock/store"; -import editResourceTabStoreInjectable from "./store.injectable"; - -const clearEditResourceTabDataInjectable = getInjectable({ - id: "clear-edit-resource-tab", - - instantiate: (di) => { - const editResourceTabStore = di.inject(editResourceTabStoreInjectable); - - return (tabId: TabId) => { - editResourceTabStore.clearData(tabId); - }; - }, -}); - -export default clearEditResourceTabDataInjectable; diff --git a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.global-override-for-injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.global-override-for-injectable.ts deleted file mode 100644 index ca999211f5..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.global-override-for-injectable.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getGlobalOverride } from "../../../../../../common/test-utils/get-global-override"; -import callForPatchResourceInjectable from "./call-for-patch-resource.injectable"; - -export default getGlobalOverride(callForPatchResourceInjectable, () => () => { - throw new Error( - "Tried to call patching of kube resource without explicit override.", - ); -}); diff --git a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable.ts deleted file mode 100644 index 097ef1ccf3..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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 { AsyncResult } from "../../../../../../common/utils/async-result"; -import apiManagerInjectable from "../../../../../../common/k8s-api/api-manager/manager.injectable"; -import type { JsonPatch } from "../../../../../../common/k8s-api/kube-object.store"; -import type { KubeObject } from "../../../../../../common/k8s-api/kube-object"; -import assert from "assert"; -import { getErrorMessage } from "../../../../../../common/utils/get-error-message"; - -export type CallForPatchResource = ( - item: KubeObject, - patch: JsonPatch -) => Promise>; - -const callForPatchResourceInjectable = getInjectable({ - id: "call-for-patch-resource", - instantiate: (di): CallForPatchResource => { - const apiManager = di.inject(apiManagerInjectable); - - return async (item, patch) => { - const store = apiManager.getStore(item.selfLink); - - assert(store); - - let kubeObject: KubeObject; - - try { - kubeObject = await store.patch(item, patch); - } catch (e: any) { - return { - callWasSuccessful: false, - error: getErrorMessage(e), - }; - } - - return { - callWasSuccessful: true, - response: { name: kubeObject.getName(), kind: kubeObject.kind }, - }; - }; - }, - - causesSideEffects: true, -}); - -export default callForPatchResourceInjectable; diff --git a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.global-override-for-injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.global-override-for-injectable.ts deleted file mode 100644 index a4e768da9a..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.global-override-for-injectable.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getGlobalOverride } from "../../../../../../common/test-utils/get-global-override"; -import callForResourceInjectable from "./call-for-resource.injectable"; - -export default getGlobalOverride(callForResourceInjectable, () => () => { - throw new Error( - "Tried to call for kube resource without explicit override.", - ); -}); diff --git a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts deleted file mode 100644 index a74da59c78..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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 { KubeObject } from "../../../../../../common/k8s-api/kube-object"; -import { parseKubeApi } from "../../../../../../common/k8s-api/kube-api-parse"; -import type { AsyncResult } from "../../../../../../common/utils/async-result"; -import { getErrorMessage } from "../../../../../../common/utils/get-error-message"; -import apiManagerInjectable from "../../../../../../common/k8s-api/api-manager/manager.injectable"; -import { waitUntilDefined } from "../../../../../../common/utils"; - -export type CallForResource = ( - selfLink: string -) => Promise>; - -const callForResourceInjectable = getInjectable({ - id: "call-for-resource", - - instantiate: (di): CallForResource => { - const apiManager = di.inject(apiManagerInjectable); - - return async (apiPath: string) => { - const api = await waitUntilDefined(() => apiManager.getApi(apiPath)); - - const parsed = parseKubeApi(apiPath); - - if (!api || !parsed.name) { - return { callWasSuccessful: false, error: "Invalid API path" }; - } - - let resource: KubeObject | null; - - try { - resource = await api.get({ - name: parsed.name, - namespace: parsed.namespace, - }); - } catch (e) { - return { callWasSuccessful: false, error: getErrorMessage(e) }; - } - - return { callWasSuccessful: true, response: resource || undefined }; - }; - }, - - causesSideEffects: true, -}); - -export default callForResourceInjectable; diff --git a/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx b/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx deleted file mode 100644 index f7efb026d8..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/** - * 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 type { CallForResource } from "./call-for-resource/call-for-resource.injectable"; -import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable"; -import { waitUntilDefined } from "../../../../../common/utils"; -import editResourceTabStoreInjectable from "../store.injectable"; -import type { EditResourceTabStore } from "../store"; -import { action, computed, makeObservable, observable, runInAction } from "mobx"; -import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; -import yaml from "js-yaml"; -import assert from "assert"; -import type { CallForPatchResource } from "./call-for-patch-resource/call-for-patch-resource.injectable"; -import callForPatchResourceInjectable from "./call-for-patch-resource/call-for-patch-resource.injectable"; -import { createPatch } from "rfc6902"; -import type { ShowNotification } from "../../../notifications"; -import showSuccessNotificationInjectable from "../../../notifications/show-success-notification.injectable"; -import React from "react"; -import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable"; - -const editResourceModelInjectable = getInjectable({ - id: "edit-resource-model", - - instantiate: async (di, tabId: string) => { - const model = new EditResourceModel({ - callForResource: di.inject(callForResourceInjectable), - callForPatchResource: di.inject(callForPatchResourceInjectable), - showSuccessNotification: di.inject(showSuccessNotificationInjectable), - showErrorNotification: di.inject(showErrorNotificationInjectable), - store: di.inject(editResourceTabStoreInjectable), - tabId, - }); - - await model.load(); - - return model; - }, - - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, tabId: string) => tabId, - }), -}); - -export default editResourceModelInjectable; - -interface Dependencies { - callForResource: CallForResource; - callForPatchResource: CallForPatchResource; - showSuccessNotification: ShowNotification; - showErrorNotification: ShowNotification; - readonly store: EditResourceTabStore; - readonly tabId: string; -} - -export class EditResourceModel { - constructor(private readonly dependencies: Dependencies) { - makeObservable(this); - } - - readonly configuration = { - value: computed(() => this.editingResource.draft || this.editingResource.firstDraft || ""), - - onChange: action((value: string) => { - this.editingResource.draft = value; - this.configuration.error.value.set(""); - }), - - error: { - value: observable.box(""), - - onChange: action((error: string) => { - this.configuration.error.value.set(error); - }), - }, - }; - - @observable private _resource: KubeObject | undefined; - - @computed get shouldShowErrorAboutNoResource() { - return !this._resource; - } - - @computed get resource() { - assert(this._resource, "Resource does not have data"); - - return this._resource; - } - - @computed get editingResource() { - const resource = this.dependencies.store.getData(this.dependencies.tabId); - - assert(resource, "Resource is not present in the store"); - - return resource; - } - - @computed private get selfLink() { - return this.editingResource.resource; - } - - load = async () => { - await waitUntilDefined(() => this.dependencies.store.getData(this.dependencies.tabId)); - - const result = await this.dependencies.callForResource(this.selfLink); - - if (!result.callWasSuccessful) { - this.dependencies.showErrorNotification( - `Loading resource failed: ${result.error}`, - ); - - return; - } - - runInAction(() => { - this._resource = result.response; - - if (this._resource) { - this.editingResource.firstDraft = yaml.dump( - this._resource.toPlainObject(), - ); - } - }); - }; - - get namespace() { - return this.resource.metadata.namespace || "default"; - } - - get name() { - return this.resource.metadata.name; - } - - get kind() { - return this.resource.kind; - } - - save = async () => { - const currentValue = this.configuration.value.get(); - const currentVersion = yaml.load(currentValue); - const firstVersion = yaml.load( - this.editingResource.firstDraft ?? currentValue, - ); - const patches = createPatch(firstVersion, currentVersion); - - const result = await this.dependencies.callForPatchResource( - this.resource, - patches, - ); - - if (!result.callWasSuccessful) { - this.dependencies.showErrorNotification(( -

- Failed to save resource: - {" "} - {result.error} -

- )); - - return; - } - - const { kind, name } = result.response; - - this.dependencies.showSuccessNotification(( -

- {`${kind} `} - {name} - {" updated."} -

- )); - - runInAction(() => { - this.editingResource.firstDraft = currentValue; - }); - }; -} diff --git a/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts deleted file mode 100644 index c3eb15f0f9..0000000000 --- a/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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 editResourceTabStoreInjectable from "./store.injectable"; -import dockStoreInjectable from "../dock/store.injectable"; -import type { KubeObject } from "../../../../common/k8s-api/kube-object"; -import type { DockTabCreateSpecific, TabId } from "../dock/store"; -import { TabKind } from "../dock/store"; -import { runInAction } from "mobx"; -import getRandomIdForEditResourceTabInjectable from "./get-random-id-for-edit-resource-tab.injectable"; - -const createEditResourceTabInjectable = getInjectable({ - id: "create-edit-resource-tab", - - instantiate: (di) => { - const dockStore = di.inject(dockStoreInjectable); - const editResourceStore = di.inject(editResourceTabStoreInjectable); - const getRandomId = di.inject(getRandomIdForEditResourceTabInjectable); - - return ( - object: KubeObject, - tabParams: DockTabCreateSpecific = {}, - ): TabId => { - // use existing tab if already opened - const tabId = editResourceStore.getTabIdByResource(object); - - if (tabId) { - dockStore.open(); - dockStore.selectTab(tabId); - - return tabId; - } - - return runInAction(() => { - const tab = dockStore.createTab( - { - id: getRandomId(), - title: `${object.kind}: ${object.getName()}`, - ...tabParams, - kind: TabKind.EDIT_RESOURCE, - }, - false, - ); - - editResourceStore.setData(tab.id, { - resource: object.selfLink, - }); - - return tab.id; - }); - }; - }, -}); - -export default createEditResourceTabInjectable; diff --git a/src/renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable.ts b/src/renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable.ts deleted file mode 100644 index e71330d261..0000000000 --- a/src/renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 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 getRandomIdInjectable from "../../../../common/utils/get-random-id.injectable"; - -const getRandomIdForEditResourceTabInjectable = getInjectable({ - id: "get-random-id-for-edit-resource-tab", - instantiate: (di) => di.inject(getRandomIdInjectable), -}); - -export default getRandomIdForEditResourceTabInjectable; diff --git a/src/renderer/components/dock/edit-resource/store.injectable.ts b/src/renderer/components/dock/edit-resource/store.injectable.ts deleted file mode 100644 index dbf4f44a6d..0000000000 --- a/src/renderer/components/dock/edit-resource/store.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 { EditResourceTabStore } from "./store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; - -const editResourceTabStoreInjectable = getInjectable({ - id: "edit-resource-tab-store", - - instantiate: (di) => new EditResourceTabStore({ - createStorage: di.inject(createStorageInjectable), - }), -}); - -export default editResourceTabStoreInjectable; diff --git a/src/renderer/components/dock/edit-resource/store.ts b/src/renderer/components/dock/edit-resource/store.ts deleted file mode 100644 index 13a2d30ad9..0000000000 --- a/src/renderer/components/dock/edit-resource/store.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { DockTabStoreDependencies } from "../dock-tab-store/dock-tab.store"; -import { DockTabStore } from "../dock-tab-store/dock-tab.store"; -import type { KubeObject } from "../../../../common/k8s-api/kube-object"; - -export interface EditingResource { - resource: string; // resource path, e.g. /api/v1/namespaces/default - draft?: string; // edited draft in yaml - firstDraft?: string; -} - -export class EditResourceTabStore extends DockTabStore { - constructor(protected readonly dependencies: DockTabStoreDependencies) { - super(dependencies, { - storageKey: "edit_resource_store", - }); - } - - getTabIdByResource(object: KubeObject): string | undefined { - return this.findTabIdFromData(({ resource }) => object.selfLink === resource); - } -} diff --git a/src/renderer/components/dock/edit-resource/view.tsx b/src/renderer/components/dock/edit-resource/view.tsx deleted file mode 100644 index 0465f80c93..0000000000 --- a/src/renderer/components/dock/edit-resource/view.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { observer } from "mobx-react"; -import { Spinner } from "../../spinner"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { EditResourceModel } from "./edit-resource-model/edit-resource-model.injectable"; -import editResourceModelInjectable from "./edit-resource-model/edit-resource-model.injectable"; -import { EditorPanel } from "../editor-panel"; -import { InfoPanel } from "../info-panel"; -import { Badge } from "../../badge"; -import { Notice } from "../../+extensions/notice"; - -export interface EditResourceProps { - tabId: string; -} - -interface Dependencies { - model: EditResourceModel; -} - -const NonInjectedEditResource = observer(({ - model, - tabId, -}: EditResourceProps & Dependencies) => ( -
- { - model.shouldShowErrorAboutNoResource - ? ( - - Resource not found - - ) - : ( - <> - - Kind: - - Name: - - Namespace: - -
- )} /> - - - ) - } - -), -); - -export const EditResource = withInjectables(NonInjectedEditResource, { - getPlaceholder: () => ( - - ), - getProps: async (di, props) => ({ - ...props, - model: await di.inject(editResourceModelInjectable, props.tabId), - }), -}); diff --git a/src/renderer/components/dock/editor-panel.module.scss b/src/renderer/components/dock/editor-panel.module.scss deleted file mode 100644 index 4af56c6f42..0000000000 --- a/src/renderer/components/dock/editor-panel.module.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.EditorPanel { - position: relative; - flex: 1; - height: 100%; -} - -.hidden { - display: none; -} diff --git a/src/renderer/components/dock/editor-panel.tsx b/src/renderer/components/dock/editor-panel.tsx deleted file mode 100644 index d9eca59159..0000000000 --- a/src/renderer/components/dock/editor-panel.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import styles from "./editor-panel.module.scss"; -import throttle from "lodash/throttle"; -import React, { createRef, useEffect } from "react"; -import { reaction } from "mobx"; -import { observer } from "mobx-react"; -import type { DockStore, TabId } from "./dock/store"; -import { cssNames, disposer } from "../../utils"; -import { MonacoEditor } from "../monaco-editor"; -import type { MonacoEditorProps, MonacoEditorRef } from "../monaco-editor"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import dockStoreInjectable from "./dock/store.injectable"; - -export interface EditorPanelProps { - tabId: TabId; - value: string; - className?: string; - autoFocus?: boolean; // default: true - onChange: MonacoEditorProps["onChange"]; - onError?: MonacoEditorProps["onError"]; - hidden?: boolean; -} - -interface Dependencies { - dockStore: DockStore; -} - -const NonInjectedEditorPanel = observer(({ - dockStore, - onChange, - tabId, - value, - autoFocus = true, - className, - onError, - hidden, -}: Dependencies & EditorPanelProps) => { - const editor = createRef(); - - useEffect(() => disposer( - reaction( - () => dockStore.isOpen, - isOpen => isOpen && editor.current?.focus(), - { - fireImmediately: true, - }, - ), - dockStore.onResize(throttle(() => editor.current?.focus(), 250)), - )); - - if (!tabId) { - return null; - } - - return ( -