From 4eb4edaed9b6e98005980cb8cb5d33577595fab6 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Wed, 27 Apr 2022 11:42:29 +0300 Subject: [PATCH] Remove multiple usages of shared global state Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- src/common/cluster/cluster.ts | 10 +++-- src/common/fs/ensure-dir.injectable.ts | 18 +++++++++ src/common/weblink-store.injectable.ts | 18 +++++++++ src/extensions/lens-main-extension.ts | 20 +++++++--- src/extensions/main-api/navigation.ts | 13 +++++- src/main/__test__/context-handler.test.ts | 37 ++++++++--------- .../electron-app/exit-app.injectable.ts | 18 +++++++++ .../__test__/kubeconfig-sync.test.ts | 6 +-- .../kubeconfig-sync-manager.injectable.ts | 2 + .../kubeconfig-sync-manager.ts | 5 ++- src/main/catalog-sources/weblinks.ts | 5 +-- .../base-cluster-detector.ts | 6 +-- .../create-version-detector.injectable.ts | 21 ++++++++++ .../detector-registry.injectable.ts | 16 ++++++++ .../cluster-detectors/detector-registry.ts | 12 ++++-- src/main/cluster-manager.injectable.ts | 23 +++++++++++ src/main/cluster-manager.ts | 40 ++++++++++--------- src/main/context-handler/context-handler.ts | 8 ++-- .../create-context-handler.injectable.ts | 2 + .../create-cluster.injectable.ts | 4 ++ src/main/developer-tools.ts | 20 ---------- src/main/exit-app.ts | 23 ----------- src/main/get-metrics.injectable.ts | 37 +++++++++++++++++ src/main/helm/helm-repo-manager.injectable.ts | 18 +++++++++ .../cluster-metadata-detectors.ts | 20 ---------- src/main/initializers/index.ts | 6 --- src/main/initializers/metrics-providers.ts | 19 --------- src/main/k8s-request.injectable.ts | 36 +++++++++++++++++ src/main/k8s-request.ts | 35 ---------------- .../create-kubeconfig-manager.injectable.ts | 2 + .../kubeconfig-manager/kubeconfig-manager.ts | 4 +- ...lens-proxy-port-number-state.injectable.ts | 36 +++++++++++++++++ src/main/lens-proxy-port-number.injectable.ts | 14 +++++++ src/main/lens-proxy.injectable.ts | 35 ++++++++++++++++ src/main/lens-proxy.ts | 32 +++++++-------- ...prometheus-provider-registry.injectable.ts | 13 ++++++ src/main/prometheus/provider-registry.ts | 3 +- .../lens-protocol-router-main.injectable.ts | 2 + .../lens-protocol-router-main.ts | 5 ++- src/main/proxy-env.ts | 8 +++- .../shell-api-request.injectable.ts | 5 ++- .../shell-api-request/shell-api-request.ts | 8 ++-- .../metrics/add-metrics-route.injectable.ts | 25 +++++++----- .../get-metric-providers-route.injectable.ts | 32 +++++++++------ .../stop-services-and-exit-app.injectable.ts | 32 +++++++++++++++ src/main/window-manager.injectable.ts | 10 ++--- src/main/window-manager.ts | 18 +++++---- .../create-cluster.injectable.ts | 4 ++ 48 files changed, 529 insertions(+), 257 deletions(-) create mode 100644 src/common/fs/ensure-dir.injectable.ts create mode 100644 src/common/weblink-store.injectable.ts create mode 100644 src/main/app-paths/get-electron-app-path/electron-app/exit-app.injectable.ts create mode 100644 src/main/cluster-detectors/create-version-detector.injectable.ts create mode 100644 src/main/cluster-detectors/detector-registry.injectable.ts create mode 100644 src/main/cluster-manager.injectable.ts delete mode 100644 src/main/developer-tools.ts delete mode 100644 src/main/exit-app.ts create mode 100644 src/main/get-metrics.injectable.ts create mode 100644 src/main/helm/helm-repo-manager.injectable.ts delete mode 100644 src/main/initializers/cluster-metadata-detectors.ts delete mode 100644 src/main/initializers/index.ts delete mode 100644 src/main/initializers/metrics-providers.ts create mode 100644 src/main/k8s-request.injectable.ts delete mode 100644 src/main/k8s-request.ts create mode 100644 src/main/lens-proxy-port-number-state.injectable.ts create mode 100644 src/main/lens-proxy-port-number.injectable.ts create mode 100644 src/main/lens-proxy.injectable.ts create mode 100644 src/main/prometheus/prometheus-provider-registry.injectable.ts create mode 100644 src/main/stop-services-and-exit-app.injectable.ts diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts index fbe7880f3c..d815a17287 100644 --- a/src/common/cluster/cluster.ts +++ b/src/common/cluster/cluster.ts @@ -15,8 +15,8 @@ import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from " import type { KubeApiResource, KubeResource } from "../rbac"; import { apiResourceRecord, apiResources } from "../rbac"; import logger from "../../main/logger"; -import { VersionDetector } from "../../main/cluster-detectors/version-detector"; -import { DetectorRegistry } from "../../main/cluster-detectors/detector-registry"; +import type { VersionDetector } from "../../main/cluster-detectors/version-detector"; +import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry"; import plimit from "p-limit"; import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate } from "../cluster-types"; import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types"; @@ -33,6 +33,8 @@ export interface ClusterDependencies { createKubectl: (clusterVersion: string) => Kubectl; createAuthorizationReview: (config: KubeConfig) => CanI; createListNamespaces: (config: KubeConfig) => ListNamespaces; + detectorRegistry: DetectorRegistry; + createVersionDetector: (cluster: Cluster) => VersionDetector; } /** @@ -425,7 +427,7 @@ export class Cluster implements ClusterModel, ClusterState { @action async refreshMetadata() { logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta()); - const metadata = await DetectorRegistry.getInstance().detectForCluster(this); + const metadata = await this.dependencies.detectorRegistry.detectForCluster(this); const existingMetadata = this.metadata; this.metadata = Object.assign(existingMetadata, metadata); @@ -488,7 +490,7 @@ export class Cluster implements ClusterModel, ClusterState { protected async getConnectionStatus(): Promise { try { - const versionDetector = new VersionDetector(this); + const versionDetector = this.dependencies.createVersionDetector(this); const versionData = await versionDetector.detect(); this.metadata.version = versionData.value; diff --git a/src/common/fs/ensure-dir.injectable.ts b/src/common/fs/ensure-dir.injectable.ts new file mode 100644 index 0000000000..88410ceee2 --- /dev/null +++ b/src/common/fs/ensure-dir.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 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 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) => di.inject(fsInjectable).ensureDir, + + causesSideEffects: true, +}); + +export default ensureDirInjectable; diff --git a/src/common/weblink-store.injectable.ts b/src/common/weblink-store.injectable.ts new file mode 100644 index 0000000000..4aca7dce2a --- /dev/null +++ b/src/common/weblink-store.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { WeblinkStore } from "./weblink-store"; + +const weblinkStoreInjectable = getInjectable({ + id: "weblink-store", + + instantiate: () => { + WeblinkStore.resetInstance(); + + return WeblinkStore.createInstance(); + }, +}); + +export default weblinkStoreInjectable; diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index bda6024522..9e5ddb7f4a 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -4,13 +4,17 @@ */ import { LensExtension } from "./lens-extension"; -import { WindowManager } from "../main/window-manager"; import { catalogEntityRegistry } from "../main/catalog"; import type { CatalogEntity } from "../common/catalog"; import type { IObservableArray } from "mobx"; import type { MenuRegistration } from "../main/menu/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 { + Environments, + getEnvironmentSpecificLegacyGlobalDiForExtensionApi, +} from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import windowManagerInjectable from "../main/window-manager.injectable"; export class LensMainExtension extends LensExtension { appMenus: MenuRegistration[] = []; @@ -18,21 +22,25 @@ export class LensMainExtension extends LensExtension { /** * 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) { - return WindowManager.getInstance().navigateExtension(this.id, pageId, params, frameId); + const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); + + const windowManager = di.inject(windowManagerInjectable); + + return windowManager.navigateExtension(this.id, pageId, params, frameId); } addCatalogSource(id: string, source: IObservableArray) { diff --git a/src/extensions/main-api/navigation.ts b/src/extensions/main-api/navigation.ts index 178f3d6d16..156641e7fb 100644 --- a/src/extensions/main-api/navigation.ts +++ b/src/extensions/main-api/navigation.ts @@ -3,8 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { WindowManager } from "../../main/window-manager"; +import { + Environments, + getEnvironmentSpecificLegacyGlobalDiForExtensionApi, +} from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; + +import windowManagerInjectable from "../../main/window-manager.injectable"; export function navigate(url: string) { - return WindowManager.getInstance().navigate(url); + const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); + + const windowManager = di.inject(windowManagerInjectable); + + return windowManager.navigate(url); } diff --git a/src/main/__test__/context-handler.test.ts b/src/main/__test__/context-handler.test.ts index c1adbde7b3..d314bbb5a5 100644 --- a/src/main/__test__/context-handler.test.ts +++ b/src/main/__test__/context-handler.test.ts @@ -12,6 +12,8 @@ import { getDiForUnitTesting } from "../getDiForUnitTesting"; import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable"; import type { Cluster } from "../../common/cluster/cluster"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; +import prometheusProviderRegistryInjectable + from "../prometheus/prometheus-provider-registry.injectable"; jest.mock("electron", () => ({ app: { @@ -74,6 +76,7 @@ const clusterStub = { describe("ContextHandler", () => { let createContextHandler: (cluster: Cluster) => ContextHandler; + let prometheusProviderRegistry: PrometheusProviderRegistry; beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); @@ -84,13 +87,12 @@ describe("ContextHandler", () => { di.override(createKubeAuthProxyInjectable, () => ({} as any)); - createContextHandler = di.inject(createContextHandlerInjectable); + prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable); - PrometheusProviderRegistry.createInstance(); + createContextHandler = di.inject(createContextHandlerInjectable); }); afterEach(() => { - PrometheusProviderRegistry.resetInstance(); UserStore.resetInstance(); mockFs.restore(); }); @@ -102,17 +104,16 @@ describe("ContextHandler", () => { [0, 2], [0, 3], ])("should throw from %d success(es) after %d failure(s)", async (successes, failures) => { - const reg = PrometheusProviderRegistry.getInstance(); let count = 0; for (let i = 0; i < failures; i += 1) { const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined; - reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); } for (let i = 0; i < successes; i += 1) { - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); } expect(() => { @@ -133,17 +134,16 @@ describe("ContextHandler", () => { [2, 2], [2, 3], ])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => { - const reg = PrometheusProviderRegistry.getInstance(); let count = 0; for (let i = 0; i < failures; i += 1) { const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined; - reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); } for (let i = 0; i < successes; i += 1) { - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); } // TODO: Unit test shouldn't access protected or private methods @@ -164,17 +164,16 @@ describe("ContextHandler", () => { [2, 2], [2, 3], ])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => { - const reg = PrometheusProviderRegistry.getInstance(); let count = 0; for (let i = 0; i < successes; i += 1) { - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); } for (let i = 0; i < failures; i += 1) { const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined; - reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); } // TODO: Unit test shouldn't access protected or private methods @@ -195,23 +194,22 @@ describe("ContextHandler", () => { [2, 2], [2, 3], ])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => { - const reg = PrometheusProviderRegistry.getInstance(); let count = 0; const beforeSuccesses = Math.floor(successes / 2); const afterSuccesses = successes - beforeSuccesses; for (let i = 0; i < beforeSuccesses; i += 1) { - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); } for (let i = 0; i < failures; i += 1) { const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined; - reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult)); } for (let i = 0; i < afterSuccesses; i += 1) { - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); } // TODO: Unit test shouldn't access protected or private methods @@ -223,12 +221,11 @@ describe("ContextHandler", () => { }); it("shouldn't pick the second provider of 2 success(es) after 1 failure(s)", async () => { - const reg = PrometheusProviderRegistry.getInstance(); let count = 0; - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure)); - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); - reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); + prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success)); // TODO: Unit test shouldn't access protected or private methods const contextHandler = createContextHandler(clusterStub) as any; diff --git a/src/main/app-paths/get-electron-app-path/electron-app/exit-app.injectable.ts b/src/main/app-paths/get-electron-app-path/electron-app/exit-app.injectable.ts new file mode 100644 index 0000000000..e7867b8cc5 --- /dev/null +++ b/src/main/app-paths/get-electron-app-path/electron-app/exit-app.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import electronAppInjectable from "./electron-app.injectable"; + +const exitAppInjectable = getInjectable({ + id: "exit-app", + + instantiate: (di) => () => { + const app = di.inject(electronAppInjectable); + + app.exit(0); + }, +}); + +export default exitAppInjectable; diff --git a/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts b/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts index eb6b5f1c97..0a736cff33 100644 --- a/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts +++ b/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts @@ -10,7 +10,6 @@ import type { Cluster } from "../../../common/cluster/cluster"; import { computeDiff as computeDiffFor, configToModels } from "../kubeconfig-sync-manager/kubeconfig-sync-manager"; import mockFs from "mock-fs"; import fs from "fs"; -import { ClusterManager } from "../../cluster-manager"; import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; @@ -19,6 +18,7 @@ import { ClusterStore } from "../../../common/cluster-store/cluster-store"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; import { runSetups } from "../../../common/setupable-injection-token/run-setups"; +import clusterManagerInjectable from "../../cluster-manager.injectable"; jest.mock("electron", () => ({ app: { @@ -53,19 +53,17 @@ describe("kubeconfig-sync.source tests", () => { computeDiff = computeDiffFor({ directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable), createCluster: di.inject(createClusterInjectionToken), + clusterManager: di.inject(clusterManagerInjectable), }); di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(appVersionInjectable); di.inject(clusterStoreInjectable); - - ClusterManager.createInstance(); }); afterEach(() => { mockFs.restore(); - ClusterManager.resetInstance(); ClusterStore.resetInstance(); }); diff --git a/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable.ts b/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable.ts index 7c258cabd7..0baf1c5fcd 100644 --- a/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable.ts +++ b/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import { KubeconfigSyncManager } from "./kubeconfig-sync-manager"; import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; +import clusterManagerInjectable from "../../cluster-manager.injectable"; const kubeconfigSyncManagerInjectable = getInjectable({ id: "kubeconfig-sync-manager", @@ -13,6 +14,7 @@ const kubeconfigSyncManagerInjectable = getInjectable({ instantiate: (di) => new KubeconfigSyncManager({ directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable), createCluster: di.inject(createClusterInjectionToken), + clusterManager: di.inject(clusterManagerInjectable), }), }); diff --git a/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.ts b/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.ts index a9e1038f53..1cbeb6663f 100644 --- a/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.ts +++ b/src/main/catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.ts @@ -53,6 +53,7 @@ const fileSyncMaxAllowedFileReadSize = 16 * folderSyncMaxAllowedFileReadSize; // interface Dependencies { directoryForKubeConfigs: string; createCluster: (model: ClusterModel) => Cluster; + clusterManager: ClusterManager; } const kubeConfigSyncName = "lens:kube-sync"; @@ -164,7 +165,7 @@ type RootSourceValue = [Cluster, CatalogEntity]; type RootSource = ObservableMap; // exported for testing -export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Dependencies) => (contents: string, source: RootSource, filePath: string): void => { +export const computeDiff = ({ directoryForKubeConfigs, createCluster, clusterManager }: Dependencies) => (contents: string, source: RootSource, filePath: string): void => { runInAction(() => { try { const { config, error } = loadConfigFromString(contents); @@ -184,7 +185,7 @@ export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Dependen // remove and disconnect clusters that were removed from the config if (!model) { // remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting - ClusterManager.getInstance().deleting.delete(value[0].id); + clusterManager.deleting.delete(value[0].id); value[0].disconnect(); source.delete(contextName); diff --git a/src/main/catalog-sources/weblinks.ts b/src/main/catalog-sources/weblinks.ts index 71979332db..3fd0b75187 100644 --- a/src/main/catalog-sources/weblinks.ts +++ b/src/main/catalog-sources/weblinks.ts @@ -4,7 +4,7 @@ */ import { computed, observable, reaction } from "mobx"; -import { WeblinkStore } from "../../common/weblink-store"; +import type { WeblinkStore } from "../../common/weblink-store"; import { WebLink } from "../../common/catalog-entities"; import { catalogEntityRegistry } from "../catalog"; import got from "got"; @@ -29,8 +29,7 @@ async function validateLink(link: WebLink) { } -export function syncWeblinks() { - const weblinkStore = WeblinkStore.getInstance(); +export function syncWeblinks(weblinkStore: WeblinkStore) { const webLinkEntities = observable.map(); function periodicallyCheckLink(link: WebLink): Disposer { diff --git a/src/main/cluster-detectors/base-cluster-detector.ts b/src/main/cluster-detectors/base-cluster-detector.ts index a5a2564ab4..8f264f358f 100644 --- a/src/main/cluster-detectors/base-cluster-detector.ts +++ b/src/main/cluster-detectors/base-cluster-detector.ts @@ -5,7 +5,7 @@ import type { RequestPromiseOptions } from "request-promise-native"; import type { Cluster } from "../../common/cluster/cluster"; -import { k8sRequest } from "../k8s-request"; +import type { K8sRequest } from "../k8s-request.injectable"; export interface ClusterDetectionResult { value: string | number | boolean; @@ -15,7 +15,7 @@ export interface ClusterDetectionResult { export class BaseClusterDetector { key: string; - constructor(public cluster: Cluster) { + constructor(public cluster: Cluster, private _k8sRequest: K8sRequest) { } detect(): Promise { @@ -23,6 +23,6 @@ export class BaseClusterDetector { } protected async k8sRequest(path: string, options: RequestPromiseOptions = {}): Promise { - return k8sRequest(this.cluster, path, options); + return this._k8sRequest(this.cluster, path, options); } } diff --git a/src/main/cluster-detectors/create-version-detector.injectable.ts b/src/main/cluster-detectors/create-version-detector.injectable.ts new file mode 100644 index 0000000000..efd59dcfc3 --- /dev/null +++ b/src/main/cluster-detectors/create-version-detector.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { VersionDetector } from "./version-detector"; +import k8sRequestInjectable from "../k8s-request.injectable"; +import type { Cluster } from "../../common/cluster/cluster"; + +const createVersionDetectorInjectable = getInjectable({ + id: "create-version-detector", + + instantiate: (di) => { + const k8sRequest = di.inject(k8sRequestInjectable); + + return (cluster: Cluster) => + new VersionDetector(cluster, k8sRequest); + }, +}); + +export default createVersionDetectorInjectable; diff --git a/src/main/cluster-detectors/detector-registry.injectable.ts b/src/main/cluster-detectors/detector-registry.injectable.ts new file mode 100644 index 0000000000..344e82003f --- /dev/null +++ b/src/main/cluster-detectors/detector-registry.injectable.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { DetectorRegistry } from "./detector-registry"; +import k8sRequestInjectable from "../k8s-request.injectable"; + +const detectorRegistryInjectable = getInjectable({ + id: "detector-registry", + + instantiate: (di) => + new DetectorRegistry({ k8sRequest: di.inject(k8sRequestInjectable) }), +}); + +export default detectorRegistryInjectable; diff --git a/src/main/cluster-detectors/detector-registry.ts b/src/main/cluster-detectors/detector-registry.ts index e2741c1353..d0f4708faf 100644 --- a/src/main/cluster-detectors/detector-registry.ts +++ b/src/main/cluster-detectors/detector-registry.ts @@ -5,11 +5,17 @@ import { observable } from "mobx"; import type { ClusterMetadata } from "../../common/cluster-types"; -import { Singleton } from "../../common/utils"; import type { Cluster } from "../../common/cluster/cluster"; import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector"; +import type { K8sRequest } from "../k8s-request.injectable"; + +interface Dependencies { + k8sRequest: K8sRequest; +} + +export class DetectorRegistry { + constructor(private dependencies: Dependencies) {} -export class DetectorRegistry extends Singleton { registry = observable.array([], { deep: false }); add(detectorClass: typeof BaseClusterDetector): this { @@ -22,7 +28,7 @@ export class DetectorRegistry extends Singleton { const results: { [key: string]: ClusterDetectionResult } = {}; for (const detectorClass of this.registry) { - const detector = new detectorClass(cluster); + const detector = new detectorClass(cluster, this.dependencies.k8sRequest); try { const data = await detector.detect(); diff --git a/src/main/cluster-manager.injectable.ts b/src/main/cluster-manager.injectable.ts new file mode 100644 index 0000000000..6b27af864e --- /dev/null +++ b/src/main/cluster-manager.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { ClusterManager } from "./cluster-manager"; +import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable"; + +const clusterManagerInjectable = getInjectable({ + id: "cluster-manager", + + instantiate: (di) => { + const clusterManager = new ClusterManager({ + store: di.inject(clusterStoreInjectable), + }); + + clusterManager.init(); + + return clusterManager; + }, +}); + +export default clusterManagerInjectable; diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 498884785d..df7c24769a 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -9,42 +9,44 @@ import { action, makeObservable, observable, observe, reaction, toJS } from "mob import type { Cluster } from "../common/cluster/cluster"; import logger from "./logger"; import { apiKubePrefix } from "../common/vars"; -import { getClusterIdFromHost, Singleton } from "../common/utils"; +import { getClusterIdFromHost } from "../common/utils"; import { catalogEntityRegistry } from "./catalog"; import type { KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster"; import { KubernetesCluster, LensKubernetesClusterStatus } from "../common/catalog-entities/kubernetes-cluster"; import { ipcMainOn } from "../common/ipc"; import { once } from "lodash"; -import { ClusterStore } from "../common/cluster-store/cluster-store"; +import type { ClusterStore } from "../common/cluster-store/cluster-store"; import type { ClusterId } from "../common/cluster-types"; const logPrefix = "[CLUSTER-MANAGER]:"; const lensSpecificClusterStatuses: Set = new Set(Object.values(LensKubernetesClusterStatus)); -export class ClusterManager extends Singleton { - private store = ClusterStore.getInstance(); +interface Dependencies { + store: ClusterStore; +} + +export class ClusterManager { deleting = observable.set(); @observable visibleCluster: ClusterId | undefined = undefined; - constructor() { - super(); + constructor(private dependencies: Dependencies) { makeObservable(this); } init = once(() => { // reacting to every cluster's state change and total amount of items reaction( - () => this.store.clustersList.map(c => c.getState()), - () => this.updateCatalog(this.store.clustersList), + () => this.dependencies.store.clustersList.map(c => c.getState()), + () => this.updateCatalog(this.dependencies.store.clustersList), { fireImmediately: false }, ); // reacting to every cluster's preferences change and total amount of items reaction( - () => this.store.clustersList.map(c => toJS(c.preferences)), - () => this.updateCatalog(this.store.clustersList), + () => this.dependencies.store.clustersList.map(c => toJS(c.preferences)), + () => this.updateCatalog(this.dependencies.store.clustersList), { fireImmediately: false }, ); @@ -180,7 +182,7 @@ export class ClusterManager extends Singleton { @action protected syncClustersFromCatalog(entities: KubernetesCluster[]) { for (const entity of entities) { - const cluster = this.store.getById(entity.getId()); + const cluster = this.dependencies.store.getById(entity.getId()); if (!cluster) { const model = { @@ -195,7 +197,7 @@ export class ClusterManager extends Singleton { * Add the bare minimum of data to ClusterStore. And especially no * preferences, as those might be configured by the entity's source */ - this.store.addCluster(model); + this.dependencies.store.addCluster(model); } catch (error) { if (error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) { logger.warn(`${logPrefix} kubeconfig file disappeared`, model); @@ -234,7 +236,7 @@ export class ClusterManager extends Singleton { protected onNetworkOffline = () => { logger.info(`${logPrefix} network is offline`); - this.store.clustersList.forEach((cluster) => { + this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.online = false; cluster.accessible = false; @@ -245,7 +247,7 @@ export class ClusterManager extends Singleton { protected onNetworkOnline = () => { logger.info(`${logPrefix} network is online`); - this.store.clustersList.forEach((cluster) => { + this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.refreshConnectionStatus().catch((e) => e); } @@ -253,16 +255,16 @@ export class ClusterManager extends Singleton { }; stop() { - this.store.clusters.forEach((cluster: Cluster) => { + this.dependencies.store.clusters.forEach((cluster: Cluster) => { cluster.disconnect(); }); } - getClusterForRequest(req: http.IncomingMessage): Cluster { + getClusterForRequest = (req: http.IncomingMessage): Cluster => { // lens-server is connecting to 127.0.0.1:/ if (req.headers.host.startsWith("127.0.0.1")) { const clusterId = req.url.split("/")[1]; - const cluster = this.store.getById(clusterId); + const cluster = this.dependencies.store.getById(clusterId); if (cluster) { // we need to swap path prefix so that request is proxied to kube api @@ -272,8 +274,8 @@ export class ClusterManager extends Singleton { return cluster; } - return this.store.getById(getClusterIdFromHost(req.headers.host)); - } + return this.dependencies.store.getById(getClusterIdFromHost(req.headers.host)); + }; } export function catalogEntityFromCluster(cluster: Cluster) { diff --git a/src/main/context-handler/context-handler.ts b/src/main/context-handler/context-handler.ts index 46e100c07e..815a318905 100644 --- a/src/main/context-handler/context-handler.ts +++ b/src/main/context-handler/context-handler.ts @@ -3,8 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { PrometheusProvider, PrometheusService } from "../prometheus/provider-registry"; -import { PrometheusProviderRegistry } from "../prometheus/provider-registry"; +import type { PrometheusProvider, PrometheusService, PrometheusProviderRegistry } from "../prometheus/provider-registry"; import type { ClusterPrometheusPreferences } from "../../common/cluster-types"; import type { Cluster } from "../../common/cluster/cluster"; import type httpProxy from "http-proxy"; @@ -30,6 +29,7 @@ interface PrometheusServicePreferences { interface Dependencies { createKubeAuthProxy: CreateKubeAuthProxy; authProxyCa: string; + prometheusProviderRegistry: PrometheusProviderRegistry; } export class ContextHandler { @@ -67,11 +67,11 @@ export class ContextHandler { this.prometheusProvider = service.id; } - return PrometheusProviderRegistry.getInstance().getByKind(this.prometheusProvider); + return this.dependencies.prometheusProviderRegistry.getByKind(this.prometheusProvider); } protected listPotentialProviders(): PrometheusProvider[] { - const registry = PrometheusProviderRegistry.getInstance(); + const registry = this.dependencies.prometheusProviderRegistry; const provider = this.prometheusProvider && registry.getByKind(this.prometheusProvider); if (provider) { diff --git a/src/main/context-handler/create-context-handler.injectable.ts b/src/main/context-handler/create-context-handler.injectable.ts index 86867ccdfb..d2242426cb 100644 --- a/src/main/context-handler/create-context-handler.injectable.ts +++ b/src/main/context-handler/create-context-handler.injectable.ts @@ -9,6 +9,7 @@ import { ContextHandler } from "./context-handler"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate"; import URLParse from "url-parse"; +import prometheusProviderRegistryInjectable from "../prometheus/prometheus-provider-registry.injectable"; const createContextHandlerInjectable = getInjectable({ id: "create-context-handler", @@ -20,6 +21,7 @@ const createContextHandlerInjectable = getInjectable({ const dependencies = { createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable), authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert, + prometheusProviderRegistry: di.inject(prometheusProviderRegistryInjectable), }; return new ContextHandler(dependencies, cluster); diff --git a/src/main/create-cluster/create-cluster.injectable.ts b/src/main/create-cluster/create-cluster.injectable.ts index d62c37e9c7..63af064c31 100644 --- a/src/main/create-cluster/create-cluster.injectable.ts +++ b/src/main/create-cluster/create-cluster.injectable.ts @@ -12,6 +12,8 @@ import createContextHandlerInjectable from "../context-handler/create-context-ha import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token"; import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable"; import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable"; +import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable"; +import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable"; const createClusterInjectable = getInjectable({ id: "create-cluster", @@ -24,6 +26,8 @@ const createClusterInjectable = getInjectable({ createContextHandler: di.inject(createContextHandlerInjectable), createAuthorizationReview: di.inject(authorizationReviewInjectable), createListNamespaces: di.inject(listNamespacesInjectable), + detectorRegistry: di.inject(detectorRegistryInjectable), + createVersionDetector: di.inject(createVersionDetectorInjectable), }; return (model) => new Cluster(dependencies, model); diff --git a/src/main/developer-tools.ts b/src/main/developer-tools.ts deleted file mode 100644 index f3e40263f0..0000000000 --- a/src/main/developer-tools.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 logger from "./logger"; - -/** - * Installs Electron developer tools in the development build. - * The dependency is not bundled to the production build. - */ -export const installDeveloperTools = () => { - if (process.env.NODE_ENV === "development") { - logger.info("🤓 Installing developer tools"); - import("electron-devtools-installer") - .then(({ default: devToolsInstaller, REACT_DEVELOPER_TOOLS }) => devToolsInstaller([REACT_DEVELOPER_TOOLS])) - .then((name) => logger.info(`[DEVTOOLS-INSTALLER]: installed ${name}`)) - .catch(error => logger.error(`[DEVTOOLS-INSTALLER]: failed`, { error })); - } -}; diff --git a/src/main/exit-app.ts b/src/main/exit-app.ts deleted file mode 100644 index c9a5bc5bf1..0000000000 --- a/src/main/exit-app.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 { app } from "electron"; -import { WindowManager } from "./window-manager"; -import { appEventBus } from "../common/app-event-bus/event-bus"; -import { ClusterManager } from "./cluster-manager"; -import logger from "./logger"; - -export function exitApp() { - const windowManager = WindowManager.getInstance(false); - const clusterManager = ClusterManager.getInstance(false); - - appEventBus.emit({ name: "service", action: "close" }); - windowManager?.hide(); - clusterManager?.stop(); - logger.info("SERVICE:QUIT"); - setTimeout(() => { - app.exit(); - }, 1000); -} diff --git a/src/main/get-metrics.injectable.ts b/src/main/get-metrics.injectable.ts new file mode 100644 index 0000000000..4d6634909c --- /dev/null +++ b/src/main/get-metrics.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Cluster } from "../common/cluster/cluster"; +import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api"; +import k8sRequestInjectable from "./k8s-request.injectable"; + +export type GetMetrics = (cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) => Promise; + +const getMetricsInjectable = getInjectable({ + id: "get-metrics", + + instantiate: (di): GetMetrics => { + const k8sRequest = di.inject(k8sRequestInjectable); + + return async ( + cluster, + prometheusPath, + queryParams, + ) => { + const prometheusPrefix = cluster.preferences.prometheus?.prefix || ""; + const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; + + return k8sRequest(cluster, metricsPath, { + timeout: 0, + resolveWithFullResponse: false, + json: true, + method: "POST", + form: queryParams, + }); + }; + }, +}); + +export default getMetricsInjectable; diff --git a/src/main/helm/helm-repo-manager.injectable.ts b/src/main/helm/helm-repo-manager.injectable.ts new file mode 100644 index 0000000000..5b5c5738a3 --- /dev/null +++ b/src/main/helm/helm-repo-manager.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { HelmRepoManager } from "./helm-repo-manager"; + +const helmRepoManagerInjectable = getInjectable({ + id: "helm-repo-manager", + + instantiate: () => { + HelmRepoManager.resetInstance(); + + return HelmRepoManager.createInstance(); + }, +}); + +export default helmRepoManagerInjectable; diff --git a/src/main/initializers/cluster-metadata-detectors.ts b/src/main/initializers/cluster-metadata-detectors.ts deleted file mode 100644 index 95d724bf3f..0000000000 --- a/src/main/initializers/cluster-metadata-detectors.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 { ClusterIdDetector } from "../cluster-detectors/cluster-id-detector"; -import { DetectorRegistry } from "../cluster-detectors/detector-registry"; -import { DistributionDetector } from "../cluster-detectors/distribution-detector"; -import { LastSeenDetector } from "../cluster-detectors/last-seen-detector"; -import { NodesCountDetector } from "../cluster-detectors/nodes-count-detector"; -import { VersionDetector } from "../cluster-detectors/version-detector"; - -export function initClusterMetadataDetectors() { - DetectorRegistry.createInstance() - .add(ClusterIdDetector) - .add(LastSeenDetector) - .add(VersionDetector) - .add(DistributionDetector) - .add(NodesCountDetector); -} diff --git a/src/main/initializers/index.ts b/src/main/initializers/index.ts deleted file mode 100644 index 233376a91f..0000000000 --- a/src/main/initializers/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 "./metrics-providers"; -export * from "./cluster-metadata-detectors"; diff --git a/src/main/initializers/metrics-providers.ts b/src/main/initializers/metrics-providers.ts deleted file mode 100644 index b8e8208a29..0000000000 --- a/src/main/initializers/metrics-providers.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 { PrometheusProviderRegistry } from "../prometheus"; -import { PrometheusHelm } from "../prometheus/helm"; -import { PrometheusLens } from "../prometheus/lens"; -import { PrometheusOperator } from "../prometheus/operator"; -import { PrometheusStacklight } from "../prometheus/stacklight"; - -export function initPrometheusProviderRegistry() { - PrometheusProviderRegistry - .getInstance() - .registerProvider(new PrometheusLens()) - .registerProvider(new PrometheusHelm()) - .registerProvider(new PrometheusOperator()) - .registerProvider(new PrometheusStacklight()); -} diff --git a/src/main/k8s-request.injectable.ts b/src/main/k8s-request.injectable.ts new file mode 100644 index 0000000000..82705cc8ce --- /dev/null +++ b/src/main/k8s-request.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import request, { RequestPromiseOptions } from "request-promise-native"; +import { apiKubePrefix } from "../common/vars"; +import type { Cluster } from "../common/cluster/cluster"; +import { getInjectable } from "@ogre-tools/injectable"; +import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable"; + +export type K8sRequest = (cluster: Cluster, path: string, options?: RequestPromiseOptions) => Promise; + +const k8SRequestInjectable = getInjectable({ + id: "k8s-request", + + instantiate: (di) => { + const lensProxyPortNumberState = di.inject(lensProxyPortNumberStateInjectable); + + return async ( + cluster: Cluster, + path: string, + options: RequestPromiseOptions = {}, + ) => { + const kubeProxyUrl = `http://localhost:${lensProxyPortNumberState.get()}${apiKubePrefix}`; + + options.headers ??= {}; + options.json ??= true; + options.timeout ??= 30000; + options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest() + + return request(kubeProxyUrl + path, options); + }; + }, +}); + +export default k8SRequestInjectable; diff --git a/src/main/k8s-request.ts b/src/main/k8s-request.ts deleted file mode 100644 index 8531e9abb7..0000000000 --- a/src/main/k8s-request.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 type { RequestPromiseOptions } from "request-promise-native"; -import request from "request-promise-native"; -import { apiKubePrefix } from "../common/vars"; -import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api"; -import { LensProxy } from "./lens-proxy"; -import type { Cluster } from "../common/cluster/cluster"; - -export async function k8sRequest(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise { - const kubeProxyUrl = `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`; - - options.headers ??= {}; - options.json ??= true; - options.timeout ??= 30000; - options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest() - - return request(kubeProxyUrl + path, options); -} - -export async function getMetrics(cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }): Promise { - const prometheusPrefix = cluster.preferences.prometheus?.prefix || ""; - const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; - - return k8sRequest(cluster, metricsPath, { - timeout: 0, - resolveWithFullResponse: false, - json: true, - method: "POST", - form: queryParams, - }); -} diff --git a/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts b/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts index fd75737555..f1d5fc73ba 100644 --- a/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts +++ b/src/main/kubeconfig-manager/create-kubeconfig-manager.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../common/cluster/cluster"; import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable"; import { KubeconfigManager } from "./kubeconfig-manager"; +import lensProxyPortNumberStateInjectable from "../lens-proxy-port-number-state.injectable"; export interface KubeConfigManagerInstantiationParameter { cluster: Cluster; @@ -17,6 +18,7 @@ const createKubeconfigManagerInjectable = getInjectable({ instantiate: (di) => { const dependencies = { directoryForTemp: di.inject(directoryForTempInjectable), + lensProxyPortNumberState: di.inject(lensProxyPortNumberStateInjectable), }; return (cluster: Cluster) => new KubeconfigManager(dependencies, cluster); diff --git a/src/main/kubeconfig-manager/kubeconfig-manager.ts b/src/main/kubeconfig-manager/kubeconfig-manager.ts index fb72b5ebac..b945a2bf72 100644 --- a/src/main/kubeconfig-manager/kubeconfig-manager.ts +++ b/src/main/kubeconfig-manager/kubeconfig-manager.ts @@ -10,10 +10,10 @@ import path from "path"; import fs from "fs-extra"; import { dumpConfigYaml } from "../../common/kube-helpers"; import logger from "../logger"; -import { LensProxy } from "../lens-proxy"; interface Dependencies { directoryForTemp: string; + lensProxyPortNumberState: { get: () => number }; } export class KubeconfigManager { @@ -79,7 +79,7 @@ export class KubeconfigManager { } get resolveProxyUrl() { - return `http://127.0.0.1:${LensProxy.getInstance().port}/${this.cluster.id}`; + return `http://127.0.0.1:${this.dependencies.lensProxyPortNumberState.get()}/${this.cluster.id}`; } /** diff --git a/src/main/lens-proxy-port-number-state.injectable.ts b/src/main/lens-proxy-port-number-state.injectable.ts new file mode 100644 index 0000000000..e09c4509e4 --- /dev/null +++ b/src/main/lens-proxy-port-number-state.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (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 lensProxyPortNumberStateInjectable = getInjectable({ + id: "lens-proxy-port-number-state", + + instantiate: () => { + let _portNumber: number; + + return { + get: () => { + if (!_portNumber) { + throw new Error( + "Tried to access port number of LensProxy while it has not been set yet.", + ); + } + + return _portNumber; + }, + set: (portNumber: number) => { + if (_portNumber) { + throw new Error( + "Tried to set port number for LensProxy when it has already been set.", + ); + } + + _portNumber = portNumber; + }, + }; + }, +}); + +export default lensProxyPortNumberStateInjectable; diff --git a/src/main/lens-proxy-port-number.injectable.ts b/src/main/lens-proxy-port-number.injectable.ts new file mode 100644 index 0000000000..45bcbdf8bd --- /dev/null +++ b/src/main/lens-proxy-port-number.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable"; + +const lensProxyPortNumberInjectable = getInjectable({ + id: "lens-proxy-port-number", + + instantiate: (di) => di.inject(lensProxyPortNumberStateInjectable).get(), +}); + +export default lensProxyPortNumberInjectable; diff --git a/src/main/lens-proxy.injectable.ts b/src/main/lens-proxy.injectable.ts new file mode 100644 index 0000000000..dd5749a3da --- /dev/null +++ b/src/main/lens-proxy.injectable.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { LensProxy } from "./lens-proxy"; +import { kubeApiUpgradeRequest } from "./proxy-functions"; +import routerInjectable from "./router/router.injectable"; +import httpProxy from "http-proxy"; +import clusterManagerInjectable from "./cluster-manager.injectable"; +import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable"; +import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable"; + +const lensProxyInjectable = getInjectable({ + id: "lens-proxy", + + instantiate: (di) => { + const clusterManager = di.inject(clusterManagerInjectable); + const router = di.inject(routerInjectable); + const shellApiRequest = di.inject(shellApiRequestInjectable); + const proxy = httpProxy.createProxy(); + const lensProxyPortNumberState = di.inject(lensProxyPortNumberStateInjectable); + + return new LensProxy({ + router, + proxy, + kubeApiUpgradeRequest, + shellApiRequest, + getClusterForRequest: clusterManager.getClusterForRequest, + lensProxyPortNumberState, + }); + }, +}); + +export default lensProxyInjectable; diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts index 945c7269e8..b2efc1f9f2 100644 --- a/src/main/lens-proxy.ts +++ b/src/main/lens-proxy.ts @@ -11,7 +11,6 @@ import { apiPrefix, apiKubePrefix } from "../common/vars"; import type { Router } from "./router/router"; import type { ContextHandler } from "./context-handler/context-handler"; import logger from "./logger"; -import { Singleton } from "../common/utils"; import type { Cluster } from "../common/cluster/cluster"; import type { ProxyApiRequestArgs } from "./proxy-functions"; import { appEventBus } from "../common/app-event-bus/event-bus"; @@ -19,10 +18,13 @@ import { getBoolean } from "./utils/parse-query"; type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | null; -export interface LensProxyFunctions { +interface Dependencies { getClusterForRequest: GetClusterForRequest; shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise; kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise; + router: Router; + proxy: httpProxy; + lensProxyPortNumberState: { set: (portNumber: number) => void }; } const watchParam = "watch"; @@ -52,21 +54,14 @@ const disallowedPorts = new Set([ 10080, ]); -export class LensProxy extends Singleton { +export class LensProxy { protected origin: string; protected proxyServer: http.Server; protected closed = false; protected retryCounters = new Map(); - protected getClusterForRequest: GetClusterForRequest; - public port: number; - - constructor(protected router: Router, protected proxy: httpProxy, { shellApiRequest, kubeApiUpgradeRequest, getClusterForRequest }: LensProxyFunctions) { - super(); - - this.configureProxy(proxy); - - this.getClusterForRequest = getClusterForRequest; + constructor(private dependencies: Dependencies) { + this.configureProxy(dependencies.proxy); this.proxyServer = spdy.createServer({ spdy: { @@ -79,14 +74,14 @@ export class LensProxy extends Singleton { this.proxyServer .on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => { - const cluster = getClusterForRequest(req); + const cluster = dependencies.getClusterForRequest(req); if (!cluster) { logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`); socket.destroy(); } else { const isInternal = req.url.startsWith(`${apiPrefix}?`); - const reqHandler = isInternal ? shellApiRequest : kubeApiUpgradeRequest; + const reqHandler = isInternal ? dependencies.shellApiRequest : dependencies.kubeApiUpgradeRequest; (async () => reqHandler({ req, socket, head, cluster }))() .catch(error => logger.error("[LENS-PROXY]: failed to handle proxy upgrade", error)); @@ -110,13 +105,14 @@ export class LensProxy extends Singleton { const { address, port } = this.proxyServer.address() as net.AddressInfo; + this.dependencies.lensProxyPortNumberState.set(port); + logger.info(`[LENS-PROXY]: Proxy server has started at ${address}:${port}`); this.proxyServer.on("error", (error) => { logger.info(`[LENS-PROXY]: Subsequent error: ${error}`); }); - this.port = port; appEventBus.emit({ name: "lens-proxy", action: "listen", params: { port }}); resolve(port); }) @@ -228,15 +224,15 @@ export class LensProxy extends Singleton { } protected async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) { - const cluster = this.getClusterForRequest(req); + const cluster = this.dependencies.getClusterForRequest(req); if (cluster) { const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler); if (proxyTarget) { - return this.proxy.web(req, res, proxyTarget); + return this.dependencies.proxy.web(req, res, proxyTarget); } } - this.router.route(cluster, req, res); + this.dependencies.router.route(cluster, req, res); } } diff --git a/src/main/prometheus/prometheus-provider-registry.injectable.ts b/src/main/prometheus/prometheus-provider-registry.injectable.ts new file mode 100644 index 0000000000..bac9e1a728 --- /dev/null +++ b/src/main/prometheus/prometheus-provider-registry.injectable.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { PrometheusProviderRegistry } from "./provider-registry"; + +const prometheusProviderRegistryInjectable = getInjectable({ + id: "prometheus-provider-registry", + instantiate: () => new PrometheusProviderRegistry(), +}); + +export default prometheusProviderRegistryInjectable; diff --git a/src/main/prometheus/provider-registry.ts b/src/main/prometheus/provider-registry.ts index 21c64d7a67..a776ec4455 100644 --- a/src/main/prometheus/provider-registry.ts +++ b/src/main/prometheus/provider-registry.ts @@ -5,7 +5,6 @@ import type { CoreV1Api } from "@kubernetes/client-node"; import { inspect } from "util"; -import { Singleton } from "../../common/utils"; export interface PrometheusService { id: string; @@ -65,7 +64,7 @@ export abstract class PrometheusProvider { } } -export class PrometheusProviderRegistry extends Singleton { +export class PrometheusProviderRegistry { public providers = new Map(); getByKind(kind: string): PrometheusProvider { diff --git a/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts b/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts index f09a7c2b8a..4a72ed6cd9 100644 --- a/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts +++ b/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import { LensProtocolRouterMain } from "./lens-protocol-router-main"; import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; +import windowManagerInjectable from "../../window-manager.injectable"; const lensProtocolRouterMainInjectable = getInjectable({ id: "lens-protocol-router-main", @@ -14,6 +15,7 @@ const lensProtocolRouterMainInjectable = getInjectable({ new LensProtocolRouterMain({ extensionLoader: di.inject(extensionLoaderInjectable), extensionsStore: di.inject(extensionsStoreInjectable), + windowManager: di.inject(windowManagerInjectable), }), }); diff --git a/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts b/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts index e40ba5fa11..c6bd1b92df 100644 --- a/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts +++ b/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts @@ -12,7 +12,7 @@ import { observable, when, makeObservable } from "mobx"; import type { RouteAttempt } from "../../../common/protocol-handler"; import { ProtocolHandlerInvalid } from "../../../common/protocol-handler"; import { disposer, noop } from "../../../common/utils"; -import { WindowManager } from "../../window-manager"; +import type { WindowManager } from "../../window-manager"; import type { ExtensionLoader } from "../../../extensions/extension-loader"; import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store"; @@ -40,6 +40,7 @@ function checkHost(url: URLParse): boolean { interface Dependencies { extensionLoader: ExtensionLoader; extensionsStore: ExtensionsStore; + windowManager: WindowManager; } export class LensProtocolRouterMain extends proto.LensProtocolRouter { @@ -73,7 +74,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter { throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url); } - WindowManager.getInstance(false)?.ensureMainWindow().catch(noop); + this.dependencies.windowManager.ensureMainWindow().catch(noop); const routeInternally = checkHost(url); logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`); diff --git a/src/main/proxy-env.ts b/src/main/proxy-env.ts index e6f98554af..b02044a30c 100644 --- a/src/main/proxy-env.ts +++ b/src/main/proxy-env.ts @@ -5,9 +5,9 @@ import { app } from "electron"; -const switchValue = app.commandLine.getSwitchValue("proxy-server"); - export function mangleProxyEnv() { + const switchValue = app.commandLine.getSwitchValue("proxy-server"); + let httpsProxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || ""; delete process.env.HTTPS_PROXY; @@ -20,4 +20,8 @@ export function mangleProxyEnv() { if (httpsProxy !== "") { process.env.APP_HTTPS_PROXY = httpsProxy; } + + if (app.commandLine.getSwitchValue("proxy-server") !== "") { + process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server"); + } } diff --git a/src/main/proxy-functions/shell-api-request/shell-api-request.injectable.ts b/src/main/proxy-functions/shell-api-request/shell-api-request.injectable.ts index 73477d1889..d4703a16c7 100644 --- a/src/main/proxy-functions/shell-api-request/shell-api-request.injectable.ts +++ b/src/main/proxy-functions/shell-api-request/shell-api-request.injectable.ts @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { shellApiRequest } from "./shell-api-request"; import createShellSessionInjectable from "../../shell-session/create-shell-session.injectable"; -import shellRequestAuthenticatorInjectable - from "./shell-request-authenticator/shell-request-authenticator.injectable"; +import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; +import clusterManagerInjectable from "../../cluster-manager.injectable"; const shellApiRequestInjectable = getInjectable({ id: "shell-api-request", @@ -14,6 +14,7 @@ const shellApiRequestInjectable = getInjectable({ instantiate: (di) => shellApiRequest({ createShellSession: di.inject(createShellSessionInjectable), authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate, + clusterManager: di.inject(clusterManagerInjectable), }), }); diff --git a/src/main/proxy-functions/shell-api-request/shell-api-request.ts b/src/main/proxy-functions/shell-api-request/shell-api-request.ts index 8a797f5f68..467955a14f 100644 --- a/src/main/proxy-functions/shell-api-request/shell-api-request.ts +++ b/src/main/proxy-functions/shell-api-request/shell-api-request.ts @@ -7,7 +7,7 @@ import logger from "../../logger"; import type WebSocket from "ws"; import { Server as WebSocketServer } from "ws"; import type { ProxyApiRequestArgs } from "../types"; -import { ClusterManager } from "../../cluster-manager"; +import type { ClusterManager } from "../../cluster-manager"; import URLParse from "url-parse"; import type { Cluster } from "../../../common/cluster/cluster"; import type { ClusterId } from "../../../common/cluster-types"; @@ -21,10 +21,12 @@ interface Dependencies { tabId: string; nodeName?: string; }) => { open: () => Promise }; + + clusterManager: ClusterManager; } -export const shellApiRequest = ({ createShellSession, authenticateRequest }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => { - const cluster = ClusterManager.getInstance().getClusterForRequest(req); +export const shellApiRequest = ({ createShellSession, authenticateRequest, clusterManager }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => { + const cluster = clusterManager.getClusterForRequest(req); const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true); if (!cluster || !authenticateRequest(cluster.id, tabId, shellToken)) { diff --git a/src/main/routes/metrics/add-metrics-route.injectable.ts b/src/main/routes/metrics/add-metrics-route.injectable.ts index 470fecd56c..a00b26ea76 100644 --- a/src/main/routes/metrics/add-metrics-route.injectable.ts +++ b/src/main/routes/metrics/add-metrics-route.injectable.ts @@ -11,13 +11,14 @@ import type { ClusterPrometheusMetadata } from "../../../common/cluster-types"; import { ClusterMetadataKey } from "../../../common/cluster-types"; import logger from "../../logger"; import type { Cluster } from "../../../common/cluster/cluster"; -import { getMetrics } from "../../k8s-request"; import type { IMetricsQuery } from "./metrics-query"; +import type { GetMetrics } from "../../get-metrics.injectable"; +import getMetricsInjectable from "../../get-metrics.injectable"; // This is used for backoff retry tracking. const ATTEMPTS = [false, false, false, false, true]; -async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record): Promise { +const loadMetricsFor = (getMetrics: GetMetrics) => async (promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record): Promise => { const queries = promQueries.map(p => p.trim()); const loaders = new Map>(); @@ -41,9 +42,11 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa } return Promise.all(queries.map(loadMetric)); -} +}; + +const addMetricsRoute = (getMetrics: GetMetrics) => async ({ cluster, payload, query }: LensApiRequest) => { + const loadMetrics = loadMetricsFor(getMetrics); -const addMetricsRoute = async ({ cluster, payload, query }: LensApiRequest) => { const queryParams: IMetricsQuery = Object.fromEntries(query.entries()); const prometheusMetadata: ClusterPrometheusMetadata = {}; @@ -97,11 +100,15 @@ const addMetricsRoute = async ({ cluster, payload, query }: LensApiRequest) => { const addMetricsRouteInjectable = getInjectable({ id: "add-metrics-route", - instantiate: (): Route => ({ - method: "post", - path: `${apiPrefix}/metrics`, - handler: addMetricsRoute, - }), + instantiate: (di): Route => { + const getMetrics = di.inject(getMetricsInjectable); + + return { + method: "post", + path: `${apiPrefix}/metrics`, + handler: addMetricsRoute(getMetrics), + }; + }, injectionToken: routeInjectionToken, }); diff --git a/src/main/routes/metrics/get-metric-providers-route.injectable.ts b/src/main/routes/metrics/get-metric-providers-route.injectable.ts index 8fcba9d984..4716406e76 100644 --- a/src/main/routes/metrics/get-metric-providers-route.injectable.ts +++ b/src/main/routes/metrics/get-metric-providers-route.injectable.ts @@ -7,26 +7,34 @@ import { getInjectable } from "@ogre-tools/injectable"; import { apiPrefix } from "../../../common/vars"; import type { Route } from "../../router/router"; import { routeInjectionToken } from "../../router/router.injectable"; -import { PrometheusProviderRegistry } from "../../prometheus"; import type { MetricProviderInfo } from "../../../common/k8s-api/endpoints/metrics.api"; +import prometheusProviderRegistryInjectable from "../../prometheus/prometheus-provider-registry.injectable"; const getMetricProvidersRouteInjectable = getInjectable({ id: "get-metric-providers-route", - instantiate: (): Route => ({ - method: "get", - path: `${apiPrefix}/metrics/providers`, + instantiate: (di): Route => { + const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable); - handler: () => { - const providers: MetricProviderInfo[] = []; + return { + method: "get", + path: `${apiPrefix}/metrics/providers`, - for (const { name, id, isConfigurable } of PrometheusProviderRegistry.getInstance().providers.values()) { - providers.push({ name, id, isConfigurable }); - } + handler: () => { + const providers: MetricProviderInfo[] = []; - return { response: providers }; - }, - }), + for (const { + name, + id, + isConfigurable, + } of prometheusProviderRegistry.providers.values()) { + providers.push({ name, id, isConfigurable }); + } + + return { response: providers }; + }, + }; + }, injectionToken: routeInjectionToken, }); diff --git a/src/main/stop-services-and-exit-app.injectable.ts b/src/main/stop-services-and-exit-app.injectable.ts new file mode 100644 index 0000000000..4d34ba4641 --- /dev/null +++ b/src/main/stop-services-and-exit-app.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import exitAppInjectable from "./app-paths/get-electron-app-path/electron-app/exit-app.injectable"; +import clusterManagerInjectable from "./cluster-manager.injectable"; +import windowManagerInjectable from "./window-manager.injectable"; +import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable"; +import loggerInjectable from "../common/logger.injectable"; + +const stopServicesAndExitAppInjectable = getInjectable({ + id: "stop-services-and-exit-app", + + instantiate: (di) => { + const exitApp = di.inject(exitAppInjectable); + const windowManager = di.inject(windowManagerInjectable); + const clusterManager = di.inject(clusterManagerInjectable); + const appEventBus = di.inject(appEventBusInjectable); + const logger = di.inject(loggerInjectable); + + return () => { + appEventBus.emit({ name: "service", action: "close" }); + windowManager.hide(); + clusterManager.stop(); + logger.info("SERVICE:QUIT"); + setTimeout(exitApp, 1000); + }; + }, +}); + +export default stopServicesAndExitAppInjectable; diff --git a/src/main/window-manager.injectable.ts b/src/main/window-manager.injectable.ts index 6a1f54a660..28f8c13c10 100644 --- a/src/main/window-manager.injectable.ts +++ b/src/main/window-manager.injectable.ts @@ -4,15 +4,15 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { WindowManager } from "./window-manager"; +import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable"; const windowManagerInjectable = getInjectable({ id: "window-manager", - instantiate: () => { - WindowManager.resetInstance(); - - return WindowManager.createInstance(); - }, + instantiate: (di) => + new WindowManager({ + lensProxyPortNumberState: di.inject(lensProxyPortNumberStateInjectable), + }), causesSideEffects: true, }); diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 260a82ccc0..13c8727c88 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -9,13 +9,12 @@ import { app, BrowserWindow, dialog, ipcMain, webContents } from "electron"; import windowStateKeeper from "electron-window-state"; import { appEventBus } from "../common/app-event-bus/event-bus"; import { ipcMainOn } from "../common/ipc"; -import { delay, iter, Singleton, openBrowser } from "../common/utils"; +import { delay, iter, openBrowser } from "../common/utils"; import type { ClusterFrameInfo } from "../common/cluster-frames"; -import { clusterFrameMap } from "../common/cluster-frames"; +import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import logger from "./logger"; import { isMac, productName } from "../common/vars"; -import { LensProxy } from "./lens-proxy"; import { bundledExtensionsLoaded } from "../common/ipc/extension-handling"; function isHideable(window: BrowserWindow | null): boolean { @@ -28,9 +27,11 @@ export interface SendToViewArgs { data?: any[]; } -export class WindowManager extends Singleton { - public mainContentUrl = `http://localhost:${LensProxy.getInstance().port}`; +interface Dependencies { + lensProxyPortNumberState: { get: () => number }; +} +export class WindowManager { protected mainWindow: BrowserWindow; protected splashWindow: BrowserWindow; protected windowState: windowStateKeeper.State; @@ -38,12 +39,15 @@ export class WindowManager extends Singleton { @observable activeClusterId: ClusterId; - constructor() { - super(); + constructor(private dependencies: Dependencies) { makeObservable(this); this.bindEvents(); } + get mainContentUrl() { + return `http://localhost:${this.dependencies.lensProxyPortNumberState.get()}`; + } + private async initMainWindow(showSplash: boolean) { // Manage main window size and position with state persistence if (!this.windowState) { diff --git a/src/renderer/create-cluster/create-cluster.injectable.ts b/src/renderer/create-cluster/create-cluster.injectable.ts index a1c244568c..a78853a94f 100644 --- a/src/renderer/create-cluster/create-cluster.injectable.ts +++ b/src/renderer/create-cluster/create-cluster.injectable.ts @@ -14,11 +14,15 @@ const createClusterInjectable = getInjectable({ instantiate: (di) => { const dependencies: ClusterDependencies = { directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable), + + // TODO: Dismantle wrong abstraction createKubeconfigManager: () => { throw new Error("Tried to access back-end feature in front-end."); }, createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");}, createContextHandler: () => { throw new Error("Tried to access back-end feature in front-end."); }, createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); }, createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); }, + detectorRegistry: null, + createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); }, }; return (model) => new Cluster(dependencies, model);