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 new file mode 100644 index 0000000000..db4734994d --- /dev/null +++ b/src/common/app-paths/path-to-npm-cli.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * 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 new file mode 100644 index 0000000000..1c32b9ed6a --- /dev/null +++ b/src/common/app-paths/path-to-npm-cli.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"; + +const pathToNpmCliInjectable = getInjectable({ + id: "path-to-npm-cli", + instantiate: () => __non_webpack_require__.resolve("npm"), + causesSideEffects: true, +}); + +export default pathToNpmCliInjectable; diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index a714af73a4..e7826acff7 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -9,13 +9,14 @@ import { ipcMain, ipcRenderer, webContents } from "electron"; import { toJS } from "../utils/toJS"; -import logger from "../../main/logger"; import type { ClusterFrameInfo } from "../cluster-frames"; import { clusterFrameMap } from "../cluster-frames"; import type { Disposer } from "../utils"; import ipcMainInjectable from "../../main/utils/channel/ipc-main/ipc-main.injectable"; 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 { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; +import loggerInjectable from "../logger.injectable"; export const broadcastMainChannel = "ipc:broadcast-main"; @@ -42,6 +43,8 @@ export async function broadcastMessage(channel: string, ...args: any[]): Promise return; } + const logger = asLegacyGlobalForExtensionApi(loggerInjectable); + ipcMain.listeners(channel).forEach((func) => func({ processId: undefined, frameId: undefined, sender: undefined, senderFrame: undefined, }, ...args)); diff --git a/src/common/k8s-api/__tests__/api-manager.test.ts b/src/common/k8s-api/__tests__/api-manager.test.ts index e99ac62018..92f9334363 100644 --- a/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/src/common/k8s-api/__tests__/api-manager.test.ts @@ -11,6 +11,7 @@ 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"; @@ -64,6 +65,7 @@ describe("ApiManager", () => { }); const kubeStore = new TestStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, kubeApi); apiManager.registerApi(apiBase, kubeApi); diff --git a/src/common/k8s-api/__tests__/kube-object.store.test.ts b/src/common/k8s-api/__tests__/kube-object.store.test.ts index afe755a6ba..424cffba23 100644 --- a/src/common/k8s-api/__tests__/kube-object.store.test.ts +++ b/src/common/k8s-api/__tests__/kube-object.store.test.ts @@ -3,6 +3,7 @@ * 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"; @@ -18,6 +19,13 @@ class FakeKubeObjectStore extends KubeObjectStore { isGlobalWatchEnabled: () => true, isLoadingAll: () => true, }, + logger: { + debug: noop, + error: noop, + info: noop, + silly: noop, + warn: noop, + }, }, api as KubeApi); } diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 9e7c541b58..ee6a32265e 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -14,7 +14,7 @@ import type { KubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-a import { parseKubeApi } from "./kube-api-parse"; import type { RequestInit } from "node-fetch"; import type { Patch } from "rfc6902"; -import logger from "../logger"; +import type { Logger } from "../logger"; import assert from "assert"; import type { PartialDeep } from "type-fest"; import { entries } from "../utils/objects"; @@ -86,6 +86,7 @@ export type JsonPatch = Patch; export interface KubeObjectStoreDependencies { readonly context: ClusterContext; + readonly logger: Logger; } export abstract class KubeObjectStore< @@ -498,7 +499,7 @@ export abstract class KubeObjectStore< const { type, object } = event; if (!object.metadata?.uid) { - logger.warn("[KUBE-STORE]: watch event did not have defined .metadata.uid, skipping", { event }); + 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; } @@ -528,7 +529,7 @@ export abstract class KubeObjectStore< break; } } catch (error) { - logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); + this.dependencies.logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); } } diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index dc388efbc8..c439c29d16 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -5,7 +5,6 @@ import { KubeConfig } from "@kubernetes/client-node"; import yaml from "js-yaml"; -import logger from "../main/logger"; 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"; @@ -220,8 +219,6 @@ export function dumpConfigYaml(kubeConfig: PartialDeep): string { users, }; - logger.debug("Dumping KubeConfig:", config); - // skipInvalid: true makes dump ignore undefined values return yaml.dump(config, { skipInvalid: true }); } diff --git a/src/common/logger.ts b/src/common/logger.ts index 0b460a48ff..ad81271bfa 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { asLegacyGlobalForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import loggerInjectable from "./logger.injectable"; export interface Logger { info: (message: string, ...args: any) => void; @@ -13,10 +11,3 @@ export interface Logger { warn: (message: string, ...args: any) => void; silly: (message: string, ...args: any) => void; } - -/** - * @deprecated use `di.inject(loggerInjectable)` instead - */ -const logger = asLegacyGlobalForExtensionApi(loggerInjectable); - -export default logger; diff --git a/src/extensions/common-api/index.ts b/src/extensions/common-api/index.ts index 6e4b39b1b7..01e41ca11c 100644 --- a/src/extensions/common-api/index.ts +++ b/src/extensions/common-api/index.ts @@ -11,8 +11,10 @@ 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"; -import logger from "../../common/logger"; +const logger = asLegacyGlobalForExtensionApi(loggerInjectable); export { App, diff --git a/src/extensions/common-api/k8s-api.ts b/src/extensions/common-api/k8s-api.ts index 9b62af7551..5ff09b3d7f 100644 --- a/src/extensions/common-api/k8s-api.ts +++ b/src/extensions/common-api/k8s-api.ts @@ -21,6 +21,8 @@ import type { KubeJsonApiDataFor, KubeObject } from "../../common/k8s-api/kube-o import type { KubeApi } 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"; export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable); export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable); @@ -104,9 +106,12 @@ export abstract class KubeObjectStore< */ constructor(); constructor(api?: A, opts?: KubeObjectStoreOptions) { + const di = getLegacyGlobalDiForExtensionApi(); + super( { - context: asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable), + context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion api!, 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 index 9d5891a01a..e7ad2285bc 100644 --- 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 @@ -3,11 +3,14 @@ * 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: () => new ExtensionInstallationStateStore(), + 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 index 1942740690..093c80934b 100644 --- a/src/extensions/extension-installation-state-store/extension-installation-state-store.ts +++ b/src/extensions/extension-installation-state-store/extension-installation-state-store.ts @@ -4,12 +4,12 @@ */ import { action, computed, observable } from "mobx"; -import logger from "../../main/logger"; 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", @@ -17,26 +17,29 @@ export enum ExtensionInstallationState { 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 InstallingFromMainChannel = - "extension-installation-state-store:install"; + private readonly preInstallIds = observable.set(); + private readonly uninstallingExtensions = observable.set(); + private readonly installingExtensions = observable.set(); - private ClearInstallingFromMainChannel = - "extension-installation-state-store:clear-install"; - - private PreInstallIds = observable.set(); - private UninstallingExtensions = observable.set(); - private InstallingExtensions = observable.set(); + constructor(private readonly dependencies: Dependencies) {} bindIpcListeners = () => { ipcRenderer - .on(this.InstallingFromMainChannel, (event, extId) => { + .on(installingFromMainChannel, (event, extId) => { this.setInstalling(extId); }) - .on(this.ClearInstallingFromMainChannel, (event, extId) => { + .on(clearInstallingFromMainChannel, (event, extId) => { this.clearInstalling(extId); }); }; @@ -47,7 +50,7 @@ export class ExtensionInstallationStateStore { * @throws if state is not IDLE */ @action setInstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to set ${extId} as installing`); + this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as installing`); const curState = this.getInstallationState(extId); @@ -57,7 +60,7 @@ export class ExtensionInstallationStateStore { ); } - this.InstallingExtensions.add(extId); + this.installingExtensions.add(extId); }; /** @@ -65,7 +68,7 @@ export class ExtensionInstallationStateStore { * @param extId the ID of the extension */ setInstallingFromMain = (extId: string): void => { - broadcastMessage(this.InstallingFromMainChannel, extId); + broadcastMessage(installingFromMainChannel, extId); }; /** @@ -73,7 +76,7 @@ export class ExtensionInstallationStateStore { * @param extId the ID of the extension */ clearInstallingFromMain = (extId: string): void => { - broadcastMessage(this.ClearInstallingFromMainChannel, extId); + broadcastMessage(clearInstallingFromMainChannel, extId); }; /** @@ -85,14 +88,14 @@ export class ExtensionInstallationStateStore { @action startPreInstall = (): ExtendableDisposer => { const preInstallStepId = uuid.v4(); - logger.debug( + this.dependencies.logger.debug( `${Prefix}: starting a new preinstall phase: ${preInstallStepId}`, ); - this.PreInstallIds.add(preInstallStepId); + this.preInstallIds.add(preInstallStepId); return disposer(() => { - this.PreInstallIds.delete(preInstallStepId); - logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); + this.preInstallIds.delete(preInstallStepId); + this.dependencies.logger.debug(`${Prefix}: ending a preinstall phase: ${preInstallStepId}`); }); }; @@ -102,7 +105,7 @@ export class ExtensionInstallationStateStore { * @throws if state is not IDLE */ @action setUninstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to set ${extId} as uninstalling`); + this.dependencies.logger.debug(`${Prefix}: trying to set ${extId} as uninstalling`); const curState = this.getInstallationState(extId); @@ -112,7 +115,7 @@ export class ExtensionInstallationStateStore { ); } - this.UninstallingExtensions.add(extId); + this.uninstallingExtensions.add(extId); }; /** @@ -121,13 +124,13 @@ export class ExtensionInstallationStateStore { * @throws if state is not INSTALLING */ @action clearInstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to clear ${extId} as installing`); + 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); + return void this.installingExtensions.delete(extId); default: throw new Error( `${Prefix}: cannot clear INSTALLING state for ${extId}, it is currently ${curState}`, @@ -141,13 +144,13 @@ export class ExtensionInstallationStateStore { * @throws if state is not UNINSTALLING */ @action clearUninstalling = (extId: string): void => { - logger.debug(`${Prefix}: trying to clear ${extId} as uninstalling`); + 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); + return void this.uninstallingExtensions.delete(extId); default: throw new Error( `${Prefix}: cannot clear UNINSTALLING state for ${extId}, it is currently ${curState}`, @@ -160,11 +163,11 @@ export class ExtensionInstallationStateStore { * @param extId The ID of the extension */ getInstallationState = (extId: string): ExtensionInstallationState => { - if (this.InstallingExtensions.has(extId)) { + if (this.installingExtensions.has(extId)) { return ExtensionInstallationState.INSTALLING; } - if (this.UninstallingExtensions.has(extId)) { + if (this.uninstallingExtensions.has(extId)) { return ExtensionInstallationState.UNINSTALLING; } @@ -197,14 +200,14 @@ export class ExtensionInstallationStateStore { * The current number of extensions installing */ @computed get installing(): number { - return this.InstallingExtensions.size; + return this.installingExtensions.size; } /** * The current number of extensions uninstalling */ get uninstalling(): number { - return this.UninstallingExtensions.size; + return this.uninstallingExtensions.size; } /** @@ -225,7 +228,7 @@ export class ExtensionInstallationStateStore { * The current number of extensions preinstalling */ get preinstalling(): number { - return this.PreInstallIds.size; + return this.preInstallIds.size; } /** diff --git a/src/extensions/extension-installer/extension-installer.injectable.ts b/src/extensions/extension-installer/extension-installer.injectable.ts index 528d504958..92b4436701 100644 --- a/src/extensions/extension-installer/extension-installer.injectable.ts +++ b/src/extensions/extension-installer/extension-installer.injectable.ts @@ -3,6 +3,8 @@ * 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"; @@ -11,6 +13,8 @@ const extensionInstallerInjectable = getInjectable({ instantiate: (di) => new ExtensionInstaller({ extensionPackageRootDirectory: di.inject(extensionPackageRootDirectoryInjectable), + logger: di.inject(loggerInjectable), + pathToNpmCli: di.inject(pathToNpmCliInjectable), }), }); diff --git a/src/extensions/extension-installer/extension-installer.ts b/src/extensions/extension-installer/extension-installer.ts index 941f8700d1..223477d0c4 100644 --- a/src/extensions/extension-installer/extension-installer.ts +++ b/src/extensions/extension-installer/extension-installer.ts @@ -5,12 +5,14 @@ import AwaitLock from "await-lock"; import child_process from "child_process"; -import logger from "../../main/logger"; +import type { Logger } from "../../common/logger"; const logModule = "[EXTENSION-INSTALLER]"; interface Dependencies { - extensionPackageRootDirectory: string; + readonly extensionPackageRootDirectory: string; + readonly logger: Logger; + readonly pathToNpmCli: string; } const baseNpmInstallArgs = [ @@ -27,13 +29,9 @@ const baseNpmInstallArgs = [ * Installs dependencies for extensions */ export class ExtensionInstaller { - private installLock = new AwaitLock(); + private readonly installLock = new AwaitLock(); - constructor(private dependencies: Dependencies) {} - - get npmPath() { - return __non_webpack_require__.resolve("npm"); - } + constructor(private readonly dependencies: Dependencies) {} /** * Install single package using npm @@ -43,9 +41,9 @@ export class ExtensionInstaller { await this.installLock.acquireAsync(); try { - logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); + this.dependencies.logger.info(`${logModule} installing package from ${name} to ${this.dependencies.extensionPackageRootDirectory}`); await this.npm(...baseNpmInstallArgs, name); - logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); + this.dependencies.logger.info(`${logModule} package ${name} installed to ${this.dependencies.extensionPackageRootDirectory}`); } finally { this.installLock.release(); } @@ -53,7 +51,7 @@ export class ExtensionInstaller { private npm(...args: string[]): Promise { return new Promise((resolve, reject) => { - const child = child_process.fork(this.npmPath, args, { + const child = child_process.fork(this.dependencies.pathToNpmCli, args, { cwd: this.dependencies.extensionPackageRootDirectory, silent: true, env: {}, diff --git a/src/extensions/ipc/ipc-main.ts b/src/extensions/ipc/ipc-main.ts index 05a2c00645..cd18bcc56c 100644 --- a/src/extensions/ipc/ipc-main.ts +++ b/src/extensions/ipc/ipc-main.ts @@ -4,12 +4,11 @@ */ import { ipcMain } from "electron"; import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; -import { Disposers } from "../lens-extension"; +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"; -import logger from "../../main/logger"; export abstract class IpcMain extends IpcRegistrar { constructor(extension: LensMainExtension) { @@ -28,12 +27,12 @@ export abstract class IpcMain extends IpcRegistrar { listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; const cleanup = once(() => { - logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + 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); }); - logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + 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); @@ -48,10 +47,10 @@ export abstract class IpcMain extends IpcRegistrar { handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + 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(() => { - logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); + 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 index 7f4e4ef94d..04222bd64b 100644 --- a/src/extensions/ipc/ipc-registrar.ts +++ b/src/extensions/ipc/ipc-registrar.ts @@ -12,7 +12,7 @@ export const IpcPrefix = Symbol(); export abstract class IpcRegistrar extends Singleton { readonly [IpcPrefix]: string; - constructor(protected extension: LensExtension) { + constructor(protected readonly extension: LensExtension) { super(); this[IpcPrefix] = createHash("sha256").update(extension.id).digest("hex"); } diff --git a/src/main/cluster/manager.injectable.ts b/src/main/cluster/manager.injectable.ts index e4c22f0e38..69c1bea47c 100644 --- a/src/main/cluster/manager.injectable.ts +++ b/src/main/cluster/manager.injectable.ts @@ -4,6 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable"; +import loggerInjectable from "../../common/logger.injectable"; import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable"; import { ClusterManager } from "./manager"; @@ -17,6 +18,7 @@ const clusterManagerInjectable = getInjectable({ catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable), visibleCluster: di.inject(visibleClusterInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/main/cluster/manager.ts b/src/main/cluster/manager.ts index 83270941d4..6888a44f5f 100644 --- a/src/main/cluster/manager.ts +++ b/src/main/cluster/manager.ts @@ -4,13 +4,10 @@ */ import "../../common/ipc/cluster"; -import type http from "http"; import type { IObservableValue, ObservableSet } from "mobx"; import { action, makeObservable, observe, reaction, toJS } from "mobx"; import type { Cluster } from "../../common/cluster/cluster"; -import logger from "../logger"; -import { apiKubePrefix } from "../../common/vars"; -import { getClusterIdFromHost, isErrnoException } from "../../common/utils"; +import { isErrnoException } from "../../common/utils"; import type { KubernetesClusterPrometheusMetrics } from "../../common/catalog-entities/kubernetes-cluster"; import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../../common/catalog-entities/kubernetes-cluster"; import { ipcMainOn } from "../../common/ipc"; @@ -18,6 +15,7 @@ import { once } from "lodash"; import type { ClusterStore } from "../../common/cluster-store/cluster-store"; import type { ClusterId } from "../../common/cluster-types"; import type { CatalogEntityRegistry } from "../catalog"; +import type { Logger } from "../../common/logger"; const logPrefix = "[CLUSTER-MANAGER]:"; @@ -28,6 +26,7 @@ interface Dependencies { readonly catalogEntityRegistry: CatalogEntityRegistry; readonly clustersThatAreBeingDeleted: ObservableSet; readonly visibleCluster: IObservableValue; + readonly logger: Logger; } export class ClusterManager { @@ -80,7 +79,7 @@ export class ClusterManager { @action protected updateCatalog(clusters: Cluster[]) { - logger.debug("[CLUSTER-MANAGER]: updating catalog from cluster store"); + this.dependencies.logger.debug("[CLUSTER-MANAGER]: updating catalog from cluster store"); for (const cluster of clusters) { this.updateEntityFromCluster(cluster); @@ -146,31 +145,31 @@ export class ClusterManager { } else { entity.status.phase = (() => { if (!cluster) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`); return LensKubernetesClusterStatus.DISCONNECTED; } if (cluster.accessible) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`); return LensKubernetesClusterStatus.CONNECTED; } if (!cluster.disconnected) { - logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`); return LensKubernetesClusterStatus.CONNECTING; } // Extensions are not allowed to use the Lens specific status phases if (!lensSpecificClusterStatuses.has(entity?.status?.phase)) { - logger.debug(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`); + this.dependencies.logger.debug(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`); return entity.status.phase; } - logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`); + this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`); return LensKubernetesClusterStatus.DISCONNECTED; })(); @@ -200,9 +199,9 @@ export class ClusterManager { this.dependencies.store.addCluster(model); } catch (error) { if (isErrnoException(error) && error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) { - logger.warn(`${logPrefix} kubeconfig file disappeared`, model); + this.dependencies.logger.warn(`${logPrefix} kubeconfig file disappeared`, model); } else { - logger.error(`${logPrefix} failed to add cluster: ${error}`, model); + this.dependencies.logger.error(`${logPrefix} failed to add cluster: ${error}`, model); } } } else { @@ -235,7 +234,7 @@ export class ClusterManager { } protected onNetworkOffline = () => { - logger.info(`${logPrefix} network is offline`); + this.dependencies.logger.info(`${logPrefix} network is offline`); this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.online = false; @@ -246,7 +245,7 @@ export class ClusterManager { }; protected onNetworkOnline = () => { - logger.info(`${logPrefix} network is online`); + this.dependencies.logger.info(`${logPrefix} network is online`); this.dependencies.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.refreshConnectionStatus().catch((e) => e); @@ -259,27 +258,6 @@ export class ClusterManager { cluster.disconnect(); }); } - - getClusterForRequest = (req: http.IncomingMessage): Cluster | undefined => { - if (!req.headers.host) { - return undefined; - } - - // lens-server is connecting to 127.0.0.1:/ - if (req.url && req.headers.host.startsWith("127.0.0.1")) { - const clusterId = req.url.split("/")[1]; - const cluster = this.dependencies.store.getById(clusterId); - - if (cluster) { - // we need to swap path prefix so that request is proxied to kube api - req.url = req.url.replace(`/${clusterId}`, apiKubePrefix); - } - - return cluster; - } - - return this.dependencies.store.getById(getClusterIdFromHost(req.headers.host)); - }; } export function catalogEntityFromCluster(cluster: Cluster) { diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts index ba64b69c69..4d711160bc 100644 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts +++ b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts @@ -11,6 +11,7 @@ import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.in import loggerInjectable from "../../common/logger.injectable"; import waitUntilPortIsUsedInjectable from "./wait-until-port-is-used/wait-until-port-is-used.injectable"; import lensK8sProxyPathInjectable from "./lens-k8s-proxy-path.injectable"; +import getPortFromStreamInjectable from "../utils/get-port-from-stream.injectable"; export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy; @@ -23,6 +24,7 @@ const createKubeAuthProxyInjectable = getInjectable({ spawn: di.inject(spawnInjectable), logger: di.inject(loggerInjectable), waitUntilPortIsUsed: di.inject(waitUntilPortIsUsedInjectable), + getPortFromStream: di.inject(getPortFromStreamInjectable), }; return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => { diff --git a/src/main/kube-auth-proxy/kube-auth-proxy.ts b/src/main/kube-auth-proxy/kube-auth-proxy.ts index a6067e548b..1d669f28c9 100644 --- a/src/main/kube-auth-proxy/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy/kube-auth-proxy.ts @@ -6,7 +6,7 @@ import type { ChildProcess } from "child_process"; import { randomBytes } from "crypto"; import type { Cluster } from "../../common/cluster/cluster"; -import { getPortFrom } from "../utils/get-port"; +import type { GetPortFromStream } from "../utils/get-port-from-stream.injectable"; import { makeObservable, observable, when } from "mobx"; import type { SelfSignedCert } from "selfsigned"; import assert from "assert"; @@ -23,9 +23,10 @@ const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), export interface KubeAuthProxyDependencies { readonly proxyBinPath: string; readonly proxyCert: SelfSignedCert; - readonly spawn: Spawn; readonly logger: Logger; - readonly waitUntilPortIsUsed: WaitUntilPortIsUsed; + spawn: Spawn; + waitUntilPortIsUsed: WaitUntilPortIsUsed; + getPortFromStream: GetPortFromStream; } export class KubeAuthProxy { @@ -101,7 +102,7 @@ export class KubeAuthProxy { } }); - this._port = await getPortFrom(this.proxyProcess.stdout, { + this._port = await this.dependencies.getPortFromStream(this.proxyProcess.stdout, { lineRegex: startingServeRegex, onFind: () => this.cluster.broadcastConnectUpdate("Authentication proxy started"), }); diff --git a/src/main/kubectl/create-kubectl.injectable.ts b/src/main/kubectl/create-kubectl.injectable.ts index f5a278ab7b..adfce0922f 100644 --- a/src/main/kubectl/create-kubectl.injectable.ts +++ b/src/main/kubectl/create-kubectl.injectable.ts @@ -17,6 +17,7 @@ import kubectlVersionMapInjectable from "./version-map.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable"; import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; +import loggerInjectable from "../../common/logger.injectable"; const createKubectlInjectable = getInjectable({ id: "create-kubectl", @@ -32,6 +33,7 @@ const createKubectlInjectable = getInjectable({ baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable), bundledKubectlVersion: di.inject(bundledKubectlVersionInjectable), kubectlVersionMap: di.inject(kubectlVersionMapInjectable), + logger: di.inject(loggerInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable), joinPaths: di.inject(joinPathsInjectable), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), diff --git a/src/main/kubectl/kubectl.ts b/src/main/kubectl/kubectl.ts index f0b3c45da0..15773a2ef7 100644 --- a/src/main/kubectl/kubectl.ts +++ b/src/main/kubectl/kubectl.ts @@ -5,7 +5,6 @@ import fs from "fs"; import { promiseExecFile } from "../../common/utils/promise-exec"; -import logger from "../logger"; import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; import { SemVer, coerce } from "semver"; @@ -18,6 +17,7 @@ import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; import type { NormalizedPlatform } from "../../common/vars/normalized-platform.injectable"; +import type { Logger } from "../../common/logger"; const initScriptVersionString = "# lens-initscript v3"; @@ -36,6 +36,7 @@ export interface KubectlDependencies { }; readonly bundledKubectlVersion: string; readonly kubectlVersionMap: Map; + readonly logger: Logger; joinPaths: JoinPaths; getDirnameOfPath: GetDirnameOfPath; getBasenameOfPath: GetBasenameOfPath; @@ -67,13 +68,13 @@ export class Kubectl { */ if (fromMajorMinor) { this.kubectlVersion = fromMajorMinor; - logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using version map`); + this.dependencies.logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using version map`); } else { /* this is the version (without possible prelease tag) to get from the download mirror */ const ver = coerce(version.format()) ?? bundledVersion; this.kubectlVersion = ver.format(); - logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); + this.dependencies.logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); } this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${this.dependencies.normalizedDownloadPlatform}/${this.dependencies.normalizedDownloadArch}/${this.dependencies.kubectlBinaryName}`; @@ -115,14 +116,14 @@ export class Kubectl { try { if (!await this.ensureKubectl()) { - logger.error("Failed to ensure kubectl, fallback to the bundled version"); + this.dependencies.logger.error("Failed to ensure kubectl, fallback to the bundled version"); return this.getBundledPath(); } return this.path; } catch (err) { - logger.error("Failed to ensure kubectl, fallback to the bundled version", err); + this.dependencies.logger.error("Failed to ensure kubectl, fallback to the bundled version", err); return this.getBundledPath(); } @@ -135,7 +136,7 @@ export class Kubectl { return this.dirname; } catch (err) { - logger.error("Failed to get biniary directory", err); + this.dependencies.logger.error("Failed to get biniary directory", err); return ""; } @@ -164,13 +165,13 @@ export class Kubectl { } if (version === this.kubectlVersion) { - logger.debug(`Local kubectl is version ${this.kubectlVersion}`); + this.dependencies.logger.debug(`Local kubectl is version ${this.kubectlVersion}`); return true; } - logger.error(`Local kubectl is version ${version}, expected ${this.kubectlVersion}, unlinking`); + this.dependencies.logger.error(`Local kubectl is version ${version}, expected ${this.kubectlVersion}, unlinking`); } catch (error) { - logger.error(`Local kubectl failed to run properly (${error}), unlinking`); + this.dependencies.logger.error(`Local kubectl failed to run properly (${error}), unlinking`); } await fs.promises.unlink(this.path); } @@ -190,7 +191,7 @@ export class Kubectl { return true; } catch (err) { - logger.error(`Could not copy the bundled kubectl to app-data: ${err}`); + this.dependencies.logger.error(`Could not copy the bundled kubectl to app-data: ${err}`); return false; } @@ -205,7 +206,7 @@ export class Kubectl { } if (Kubectl.invalidBundle) { - logger.error(`Detected invalid bundle binary, returning ...`); + this.dependencies.logger.error(`Detected invalid bundle binary, returning ...`); return false; } @@ -215,7 +216,7 @@ export class Kubectl { try { const release = await lockFile.lock(this.dirname); - logger.debug(`Acquired a lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`Acquired a lock for ${this.kubectlVersion}`); const bundled = await this.checkBundled(); let isValid = await this.checkBinary(this.path, !bundled); @@ -223,8 +224,8 @@ export class Kubectl { try { await this.downloadKubectl(); } catch (error) { - logger.error(`[KUBECTL]: failed to download kubectl`, error); - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.error(`[KUBECTL]: failed to download kubectl`, error); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return false; @@ -234,18 +235,18 @@ export class Kubectl { } if (!isValid) { - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return false; } - logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); + this.dependencies.logger.debug(`[KUBECTL]: Releasing lock for ${this.kubectlVersion}`); await release(); return true; } catch (error) { - logger.error(`[KUBECTL]: Failed to get a lock for ${this.kubectlVersion}`, error); + this.dependencies.logger.error(`[KUBECTL]: Failed to get a lock for ${this.kubectlVersion}`, error); return false; } @@ -254,7 +255,7 @@ export class Kubectl { public async downloadKubectl() { await ensureDir(this.dependencies.getDirnameOfPath(this.path), 0o755); - logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); + this.dependencies.logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); const downloadStream = got.stream({ url: this.url, decompress: true }); const fileWriteStream = fs.createWriteStream(this.path, { mode: 0o755 }); @@ -263,7 +264,7 @@ export class Kubectl { try { await pipeline(downloadStream, fileWriteStream); await fs.promises.chmod(this.path, 0o755); - logger.debug("kubectl binary download finished"); + this.dependencies.logger.debug("kubectl binary download finished"); } catch (error) { await fs.promises.unlink(this.path).catch(noop); throw error; diff --git a/src/main/lens-proxy/get-cluster-for-request.injectable.ts b/src/main/lens-proxy/get-cluster-for-request.injectable.ts new file mode 100644 index 0000000000..7d529540d1 --- /dev/null +++ b/src/main/lens-proxy/get-cluster-for-request.injectable.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 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 { getClusterIdFromHost } from "../../common/utils"; +import { apiKubePrefix } from "../../common/vars"; +import type { GetClusterForRequest } from "./lens-proxy"; + +const getClusterForRequestInjectable = getInjectable({ + id: "get-cluster-for-request", + instantiate: (di): GetClusterForRequest => { + const getClusterById = di.inject(getClusterByIdInjectable); + + return (req) => { + if (!req.headers.host) { + return undefined; + } + + // lens-server is connecting to 127.0.0.1:/ + if (req.url && req.headers.host.startsWith("127.0.0.1")) { + const clusterId = req.url.split("/")[1]; + const cluster = getClusterById(clusterId); + + if (cluster) { + // we need to swap path prefix so that request is proxied to kube api + req.url = req.url.replace(`/${clusterId}`, apiKubePrefix); + } + + return cluster; + } + + const clusterId = getClusterIdFromHost(req.headers.host); + + if (!clusterId) { + return undefined; + } + + return getClusterById(clusterId); + }; + }, +}); + +export default getClusterForRequestInjectable; diff --git a/src/main/lens-proxy/lens-proxy.injectable.ts b/src/main/lens-proxy/lens-proxy.injectable.ts index 998312b535..541ef18cc3 100644 --- a/src/main/lens-proxy/lens-proxy.injectable.ts +++ b/src/main/lens-proxy/lens-proxy.injectable.ts @@ -7,13 +7,13 @@ 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 shellApiRequestInjectable from "./proxy-functions/shell-api-request.injectable"; import lensProxyPortInjectable from "./lens-proxy-port.injectable"; import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable"; import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import loggerInjectable from "../../common/logger.injectable"; import lensProxyCertificateInjectable from "../../common/certificate/lens-proxy-certificate.injectable"; +import getClusterForRequestInjectable from "./get-cluster-for-request.injectable"; const lensProxyInjectable = getInjectable({ id: "lens-proxy", @@ -23,7 +23,7 @@ const lensProxyInjectable = getInjectable({ proxy: httpProxy.createProxy(), kubeApiUpgradeRequest, shellApiRequest: di.inject(shellApiRequestInjectable), - getClusterForRequest: di.inject(clusterManagerInjectable).getClusterForRequest, + getClusterForRequest: di.inject(getClusterForRequestInjectable), lensProxyPort: di.inject(lensProxyPortInjectable), contentSecurityPolicy: di.inject(contentSecurityPolicyInjectable), emitAppEvent: di.inject(emitAppEventInjectable), diff --git a/src/main/lens-proxy/lens-proxy.ts b/src/main/lens-proxy/lens-proxy.ts index 9b1d80270a..1a5acdd9f2 100644 --- a/src/main/lens-proxy/lens-proxy.ts +++ b/src/main/lens-proxy/lens-proxy.ts @@ -19,14 +19,14 @@ import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injecta import type { Logger } from "../../common/logger"; import type { SelfSignedCert } from "selfsigned"; -type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; - +export type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; export type ServerIncomingMessage = SetRequired; +export type LensProxyApiRequest = (args: ProxyApiRequestArgs) => void | Promise; interface Dependencies { getClusterForRequest: GetClusterForRequest; - shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise; - kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise; + shellApiRequest: LensProxyApiRequest; + kubeApiUpgradeRequest: LensProxyApiRequest; emitAppEvent: EmitAppEvent; readonly router: Router; readonly proxy: httpProxy; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts new file mode 100644 index 0000000000..afaf2a870d --- /dev/null +++ b/src/main/lens-proxy/proxy-functions/shell-api-request.injectable.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; +import openShellSessionInjectable from "../../shell-session/create-shell-session.injectable"; +import type { LensProxyApiRequest } from "../lens-proxy"; +import URLParse from "url-parse"; +import { Server as WebSocketServer } from "ws"; +import loggerInjectable from "../../../common/logger.injectable"; +import getClusterForRequestInjectable from "../get-cluster-for-request.injectable"; + +const shellApiRequestInjectable = getInjectable({ + id: "shell-api-request", + + instantiate: (di): LensProxyApiRequest => { + const openShellSession = di.inject(openShellSessionInjectable); + const authenticateRequest = di.inject(shellRequestAuthenticatorInjectable).authenticate; + const getClusterForRequest = di.inject(getClusterForRequestInjectable); + const logger = di.inject(loggerInjectable); + + return ({ req, socket, head }) => { + const cluster = getClusterForRequest(req); + const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true); + + if (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) { + socket.write("Invalid shell request"); + socket.end(); + } else { + const ws = new WebSocketServer({ noServer: true }); + + ws.handleUpgrade(req, socket, head, (websocket) => { + openShellSession({ websocket, cluster, tabId, nodeName }) + .catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error)); + }); + } + }; + }, +}); + +export default shellApiRequestInjectable; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.injectable.ts deleted file mode 100644 index 550a8d2560..0000000000 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.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 { shellApiRequest } from "./shell-api-request"; -import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; -import clusterManagerInjectable from "../../../cluster/manager.injectable"; -import openShellSessionInjectable from "../../../shell-session/create-shell-session.injectable"; - -const shellApiRequestInjectable = getInjectable({ - id: "shell-api-request", - - instantiate: (di) => shellApiRequest({ - openShellSession: di.inject(openShellSessionInjectable), - authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate, - clusterManager: di.inject(clusterManagerInjectable), - }), -}); - -export default shellApiRequestInjectable; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts b/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.ts deleted file mode 100644 index f4492e96f6..0000000000 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-api-request.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 logger from "../../../logger"; -import { Server as WebSocketServer } from "ws"; -import type { ProxyApiRequestArgs } from "../types"; -import type { ClusterManager } from "../../../cluster/manager"; -import URLParse from "url-parse"; -import type { ClusterId } from "../../../../common/cluster-types"; -import type { OpenShellSession } from "../../../shell-session/create-shell-session.injectable"; - -interface Dependencies { - authenticateRequest: (clusterId: ClusterId, tabId: string, shellToken: string | undefined) => boolean; - openShellSession: OpenShellSession; - clusterManager: ClusterManager; -} - -export const shellApiRequest = ({ openShellSession, 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 (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) { - socket.write("Invalid shell request"); - - return void socket.end(); - } - - const ws = new WebSocketServer({ noServer: true }); - - ws.handleUpgrade(req, socket, head, (websocket) => { - openShellSession({ websocket, cluster, tabId, nodeName }) - .catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error)); - }); -}; diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.injectable.ts b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.injectable.ts similarity index 100% rename from src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.injectable.ts rename to src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.injectable.ts diff --git a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts similarity index 89% rename from src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts rename to src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts index fe0a8f643d..ab5e46eb77 100644 --- a/src/main/lens-proxy/proxy-functions/shell-api-request/shell-request-authenticator/shell-request-authenticator.ts +++ b/src/main/lens-proxy/proxy-functions/shell-request-authenticator/shell-request-authenticator.ts @@ -2,9 +2,9 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getOrInsertMap } from "../../../../../common/utils"; -import type { ClusterId } from "../../../../../common/cluster-types"; -import { ipcMainHandle } from "../../../../../common/ipc"; +import { getOrInsertMap } from "../../../../common/utils"; +import type { ClusterId } from "../../../../common/cluster-types"; +import { ipcMainHandle } from "../../../../common/ipc"; import crypto from "crypto"; import { promisify } from "util"; diff --git a/src/main/logger.ts b/src/main/logger.ts deleted file mode 100644 index 8434c6e4d8..0000000000 --- a/src/main/logger.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 logger from "../common/logger"; - -export default logger; diff --git a/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts b/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts index ab2a5e8c42..e2bfb8a517 100644 --- a/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts +++ b/src/main/routes/port-forward/functionality/create-port-forward.injectable.ts @@ -3,22 +3,25 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import type { PortForwardArgs } from "./port-forward"; +import type { PortForwardArgs, PortForwardDependencies } from "./port-forward"; import { PortForward } from "./port-forward"; import bundledKubectlInjectable from "../../../kubectl/bundled-kubectl.injectable"; +import getPortFromStreamInjectable from "../../../utils/get-port-from-stream.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; + +export type CreatePortForward = (pathToKubeConfig: string, args: PortForwardArgs) => PortForward; const createPortForwardInjectable = getInjectable({ id: "create-port-forward", - instantiate: (di) => { - const bundledKubectl = di.inject(bundledKubectlInjectable); - - const dependencies = { - getKubectlBinPath: bundledKubectl.getPath, + instantiate: (di): CreatePortForward => { + const dependencies: PortForwardDependencies = { + getKubectlBinPath: di.inject(bundledKubectlInjectable).getPath, + getPortFromStream: di.inject(getPortFromStreamInjectable), + logger: di.inject(loggerInjectable), }; - return (pathToKubeConfig: string, args: PortForwardArgs) => - new PortForward(dependencies, pathToKubeConfig, args); + return (pathToKubeConfig, args) => new PortForward(dependencies, pathToKubeConfig, args); }, }); diff --git a/src/main/routes/port-forward/functionality/port-forward.ts b/src/main/routes/port-forward/functionality/port-forward.ts index 43d8f9d425..0419024f2f 100644 --- a/src/main/routes/port-forward/functionality/port-forward.ts +++ b/src/main/routes/port-forward/functionality/port-forward.ts @@ -2,12 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import logger from "../../../logger"; -import { getPortFrom } from "../../../utils/get-port"; +import type { GetPortFromStream } from "../../../utils/get-port-from-stream.injectable"; import type { ChildProcessWithoutNullStreams } from "child_process"; import { spawn } from "child_process"; import * as tcpPortUsed from "tcp-port-used"; import { TypedRegEx } from "typed-regex"; +import type { Logger } from "../../../../common/logger"; const internalPortMatcher = "^forwarding from (?
.+) ->"; const internalPortRegex = Object.assign(TypedRegEx(internalPortMatcher, "i"), { @@ -23,8 +23,10 @@ export interface PortForwardArgs { forwardPort: number; } -interface Dependencies { +export interface PortForwardDependencies { + readonly logger: Logger; getKubectlBinPath: (bundled: boolean) => Promise; + getPortFromStream: GetPortFromStream; } export class PortForward { @@ -48,7 +50,7 @@ export class PortForward { public port: number; public forwardPort: number; - constructor(private dependencies: Dependencies, public pathToKubeConfig: string, args: PortForwardArgs) { + constructor(private dependencies: PortForwardDependencies, public pathToKubeConfig: string, args: PortForwardArgs) { this.clusterId = args.clusterId; this.kind = args.kind; this.namespace = args.namespace; @@ -80,10 +82,10 @@ export class PortForward { }); this.process.stderr.on("data", (data) => { - logger.debug(`[PORT-FORWARD-ROUTE]: kubectl port-forward process stderr: ${data}`); + this.dependencies.logger.debug(`[PORT-FORWARD-ROUTE]: kubectl port-forward process stderr: ${data}`); }); - const internalPort = await getPortFrom(this.process.stdout, { + const internalPort = await this.dependencies.getPortFromStream(this.process.stdout, { lineRegex: internalPortRegex, }); diff --git a/src/main/utils/get-port-from-stream.injectable.ts b/src/main/utils/get-port-from-stream.injectable.ts new file mode 100644 index 0000000000..983ed66313 --- /dev/null +++ b/src/main/utils/get-port-from-stream.injectable.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { Readable } from "stream"; +import URLParse from "url-parse"; +import { getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../../common/logger.injectable"; + +export interface GetPortFromStreamArgs { + /** + * Should be case insensitive + * Must have a named matching group called `address` + */ + lineRegex: { + match: (line: string) => { + matched: boolean; + groups?: { + address?: string; + }; + raw?: RegExpExecArray; + }; + rawMatcher: string; + }; + /** + * Called when the port is found + */ + onFind?: () => void; + /** + * Timeout for how long to wait for the port. + * Default: 15s + */ + timeout?: number; +} + +export type GetPortFromStream = (stream: Readable, args: GetPortFromStreamArgs) => Promise; + +const getPortFromStreamInjectable = getInjectable({ + id: "get-port-from-stream", + instantiate: (di): GetPortFromStream => { + const logger = di.inject(loggerInjectable); + + return (stream, args) => { + const logLines: string[] = []; + + return new Promise((resolve, reject) => { + const handler = (data: unknown) => { + const logItem = String(data); + const match = args.lineRegex.match(logItem); + + logLines.push(logItem); + + if (match.matched) { + // use unknown protocol so that there is no default port + const addr = new URLParse(`s://${match.groups?.address?.trim()}`); + + args.onFind?.(); + stream.off("data", handler); + clearTimeout(timeoutID); + resolve(+addr.port); + } + }; + const timeoutID = setTimeout(() => { + stream.off("data", handler); + logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.rawMatcher}`, logLines); + reject(new Error("failed to retrieve port from stream")); + }, args.timeout ?? 15000); + + stream.on("data", handler); + }); + }; + }, +}); + +export default getPortFromStreamInjectable; + diff --git a/src/main/utils/get-port.ts b/src/main/utils/get-port.ts deleted file mode 100644 index b0d7f5615f..0000000000 --- a/src/main/utils/get-port.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 { Readable } from "stream"; -import URLParse from "url-parse"; -import logger from "../logger"; - -interface GetPortArgs { - /** - * Should be case insensitive - * Must have a named matching group called `address` - */ - lineRegex: { - match: (line: string) => { - matched: boolean; - groups?: { - address?: string; - }; - raw?: RegExpExecArray; - }; - rawMatcher: string; - }; - /** - * Called when the port is found - */ - onFind?: () => void; - /** - * Timeout for how long to wait for the port. - * Default: 15s - */ - timeout?: number; -} - -/** - * Parse lines from `stream` (assumes data comes in lines) to find the port - * which the source of the stream is watching on. - * @param stream A readable stream to match lines against - * @param args The args concerning the stream - * @returns A Promise for port number - */ -export function getPortFrom(stream: Readable, args: GetPortArgs): Promise { - const logLines: string[] = []; - - return new Promise((resolve, reject) => { - const handler = (data: unknown) => { - const logItem = String(data); - const match = args.lineRegex.match(logItem); - - logLines.push(logItem); - - if (match.matched) { - // use unknown protocol so that there is no default port - const addr = new URLParse(`s://${match.groups?.address?.trim()}`); - - args.onFind?.(); - stream.off("data", handler); - clearTimeout(timeoutID); - resolve(+addr.port); - } - }; - const timeoutID = setTimeout(() => { - stream.off("data", handler); - logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.rawMatcher}`, logLines); - reject(new Error("failed to retrieve port from stream")); - }, args.timeout ?? 15000); - - stream.on("data", handler); - }); -} diff --git a/src/renderer/api/create-terminal-api.injectable.ts b/src/renderer/api/create-terminal-api.injectable.ts index 255fa396f3..97592f0dab 100644 --- a/src/renderer/api/create-terminal-api.injectable.ts +++ b/src/renderer/api/create-terminal-api.injectable.ts @@ -4,8 +4,9 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; +import loggerInjectable from "../../common/logger.injectable"; import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-id.injectable"; -import type { TerminalApiQuery } from "./terminal-api"; +import type { TerminalApiDependencies, TerminalApiQuery } from "./terminal-api"; import { TerminalApi } from "./terminal-api"; export type CreateTerminalApi = (query: TerminalApiQuery) => TerminalApi; @@ -14,12 +15,16 @@ const createTerminalApiInjectable = getInjectable({ id: "create-terminal-api", instantiate: (di): CreateTerminalApi => { const hostedClusterId = di.inject(hostedClusterIdInjectable); + const deps: Omit = { + logger: di.inject(loggerInjectable), + }; return (query) => { assert(hostedClusterId, "Can only create terminal APIs within a cluster frame"); return new TerminalApi({ hostedClusterId, + ...deps, }, query); }; }, diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 75d907616c..74b718ec81 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -9,7 +9,7 @@ import isEqual from "lodash/isEqual"; import url from "url"; import { makeObservable, observable } from "mobx"; import { ipcRenderer } from "electron"; -import logger from "../../common/logger"; +import type { Logger } from "../../common/logger"; import { once } from "lodash"; import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels"; @@ -38,6 +38,7 @@ export interface TerminalEvents extends WebSocketEvents { export interface TerminalApiDependencies { readonly hostedClusterId: string; + readonly logger: Logger; } export class TerminalApi extends WebSocketApi { @@ -145,11 +146,11 @@ export class TerminalApi extends WebSocketApi { this.emit("connected"); break; default: - logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message); + this.dependencies.logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message); break; } } catch (error) { - logger.error(`[TERMINAL-API]: failed to handle message`, error); + this.dependencies.logger.error(`[TERMINAL-API]: failed to handle message`, error); } } diff --git a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts index ecfac3136a..78ba68c173 100644 --- a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts +++ b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts @@ -12,6 +12,7 @@ import { KubeObject } from "../../../common/k8s-api/kube-object"; import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens"; import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const setupAutoRegistrationInjectable = getInjectable({ id: "setup-auto-registration", @@ -23,6 +24,7 @@ const setupAutoRegistrationInjectable = getInjectable({ const beforeApiManagerInitializationApis: KubeApi[] = []; const deps: KubeObjectStoreDependencies = { context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }; let initialized = false; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index d37f561c05..f509847c74 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -13,7 +13,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable"; import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable"; import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; -import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable"; +import initRootFrameInjectable from "./frames/root-frame/init-root-frame.injectable"; import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable"; import { Router } from "react-router"; import historyInjectable from "./navigation/history.injectable"; 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 index a4666adcef..2d6ece0dfd 100644 --- 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 @@ -14,6 +14,7 @@ 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", @@ -34,6 +35,7 @@ const clusterOverviewStoreInjectable = getInjectable({ nodeStore: di.inject(nodeStoreInjectable), requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, clusterApi); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index ab7ea2c726..4bd5f12a66 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -16,17 +16,19 @@ import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTa import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import logger from "../../../common/logger"; +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"; export interface HpaDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { apiManager: ApiManager; + logger: Logger; getDetailsUrl: GetDetailsUrl; } @@ -104,7 +106,7 @@ class NonInjectedHpaDetails extends React.Component(NonInje ...props, apiManager: di.inject(apiManagerInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+config-autoscalers/store.injectable.ts b/src/renderer/components/+config-autoscalers/store.injectable.ts index 4d4a3adc21..47a0f25680 100644 --- a/src/renderer/components/+config-autoscalers/store.injectable.ts +++ b/src/renderer/components/+config-autoscalers/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const horizontalPodAutoscalerStoreInjectable = getInjectable({ return new HorizontalPodAutoscalerStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-leases/store.injectable.ts b/src/renderer/components/+config-leases/store.injectable.ts index eb9676ce90..df91a1df54 100644 --- a/src/renderer/components/+config-leases/store.injectable.ts +++ b/src/renderer/components/+config-leases/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const leaseStoreInjectable = getInjectable({ return new LeaseStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx index 2da12a3d31..79c3038a5d 100644 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx @@ -12,7 +12,9 @@ import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-ran import { LimitPart, LimitRange, Resource } from "../../../common/k8s-api/endpoints/limit-range.api"; import { DrawerItem } from "../drawer/drawer-item"; import { Badge } from "../badge"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface LimitRangeDetailsProps extends KubeObjectDetailsProps { } @@ -51,10 +53,14 @@ function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) { )); } +interface Dependencies { + logger: Logger; +} + @observer -export class LimitRangeDetails extends React.Component { +class NonInjectedLimitRangeDetails extends React.Component { render() { - const { object: limitRange } = this.props; + const { object: limitRange, logger } = this.props; if (!limitRange) { return null; @@ -97,3 +103,10 @@ export class LimitRangeDetails extends React.Component { ); } } + +export const LimitRangeDetails = withInjectables(NonInjectedLimitRangeDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-limit-ranges/store.injectable.ts b/src/renderer/components/+config-limit-ranges/store.injectable.ts index 494968b60c..f22aadaec1 100644 --- a/src/renderer/components/+config-limit-ranges/store.injectable.ts +++ b/src/renderer/components/+config-limit-ranges/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const limitRangeStoreInjectable = getInjectable({ return new LimitRangeStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-maps/store.injectable.ts b/src/renderer/components/+config-maps/store.injectable.ts index 7cbd59e4a5..44945120f0 100644 --- a/src/renderer/components/+config-maps/store.injectable.ts +++ b/src/renderer/components/+config-maps/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const configMapStoreInjectable = getInjectable({ return new ConfigMapStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx index b58a42b0d9..a6b6e48342 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx @@ -11,13 +11,19 @@ import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints"; -import logger from "../../../common/logger"; +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 -export class PodDisruptionBudgetDetails extends React.Component { +class NonInjectedPodDisruptionBudgetDetails extends React.Component { render() { const { object: pdb } = this.props; @@ -27,7 +33,7 @@ export class PodDisruptionBudgetDetails extends React.Component(NonInjectedPodDisruptionBudgetDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts index 697fd5c444..f6bb813455 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts +++ b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const podDisruptionBudgetStoreInjectable = getInjectable({ return new PodDisruptionBudgetStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-priority-classes/store.injectable.ts b/src/renderer/components/+config-priority-classes/store.injectable.ts index b01e69602c..acd97aee5b 100644 --- a/src/renderer/components/+config-priority-classes/store.injectable.ts +++ b/src/renderer/components/+config-priority-classes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const priorityClassStoreInjectable = getInjectable({ return new PriorityClassStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx index c02f9b221b..55ae188cbf 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx @@ -13,7 +13,9 @@ 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 logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps { } @@ -75,8 +77,12 @@ function renderQuotas(quota: ResourceQuota): JSX.Element[] { }); } +interface Dependencies { + logger: Logger; +} + @observer -export class ResourceQuotaDetails extends React.Component { +class NonInjectedResourceQuotaDetails extends React.Component { render() { const { object: quota } = this.props; @@ -85,7 +91,7 @@ export class ResourceQuotaDetails extends React.Component(NonInjectedResourceQuotaDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+config-resource-quotas/store.injectable.ts b/src/renderer/components/+config-resource-quotas/store.injectable.ts index 4999ca9517..77708a1bf4 100644 --- a/src/renderer/components/+config-resource-quotas/store.injectable.ts +++ b/src/renderer/components/+config-resource-quotas/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const resourceQuotaStoreInjectable = getInjectable({ return new ResourceQuotaStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-runtime-classes/store.injectable.ts b/src/renderer/components/+config-runtime-classes/store.injectable.ts index 63e8d82526..03d4e50a1a 100644 --- a/src/renderer/components/+config-runtime-classes/store.injectable.ts +++ b/src/renderer/components/+config-runtime-classes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -19,6 +20,7 @@ const runtimeClassStoreInjectable = getInjectable({ return new RuntimeClassStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+config-secrets/store.injectable.ts b/src/renderer/components/+config-secrets/store.injectable.ts index 2af7bf13f6..d98f3677c1 100644 --- a/src/renderer/components/+config-secrets/store.injectable.ts +++ b/src/renderer/components/+config-secrets/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.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 { SecretStore } from "./store"; @@ -19,6 +20,7 @@ const secretStoreInjectable = getInjectable({ return new SecretStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index d3023a020e..23194dbc73 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -15,13 +15,19 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Table, TableCell, TableHead, TableRow } from "../table"; import { Input } from "../input"; import { MonacoEditor } from "../monaco-editor"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface CRDDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class CRDDetails extends React.Component { +class NonInjectedCRDDetails extends React.Component { render() { const { object: crd } = this.props; @@ -30,7 +36,7 @@ export class CRDDetails extends React.Component { } if (!(crd instanceof CustomResourceDefinition)) { - logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd); + this.props.logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd); return null; } @@ -152,3 +158,10 @@ export class CRDDetails extends React.Component { ); } } + +export const CRDDetails = withInjectables(NonInjectedCRDDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index 01f8b3a175..149812f663 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -17,7 +17,9 @@ import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/cust import { safeJSONPathValue } from "../../utils/jsonPath"; import type { KubeObjectMetadata, KubeObjectStatus } from "../../../common/k8s-api/kube-object"; import { KubeObject } from "../../../common/k8s-api/kube-object"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface CustomResourceDetailsProps extends KubeObjectDetailsProps { crd?: CustomResourceDefinition; @@ -59,8 +61,12 @@ function convertSpecValue(value: unknown): React.ReactNode { return null; } +interface Dependencies { + logger: Logger; +} + @observer -export class CustomResourceDetails extends React.Component { +class NonInjectedCustomResourceDetails extends React.Component { renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) { return columns.map(({ name, jsonPath }) => ( @@ -106,7 +112,7 @@ export class CustomResourceDetails extends React.Component(NonInjectedCustomResourceDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+custom-resources/definition.store.injectable.ts b/src/renderer/components/+custom-resources/definition.store.injectable.ts index b93f656df9..47c7652f8b 100644 --- a/src/renderer/components/+custom-resources/definition.store.injectable.ts +++ b/src/renderer/components/+custom-resources/definition.store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.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 { CustomResourceDefinitionStore } from "./definition.store"; @@ -21,6 +22,7 @@ const customResourceDefinitionStoreInjectable = getInjectable({ return new CustomResourceDefinitionStore({ autoRegistration: di.inject(autoRegistrationEmitterInjectable), context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index 5334744054..07842b6f78 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -14,13 +14,14 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { DurationAbsoluteTimestamp } from "./duration-absolute"; 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 { cssNames } from "../../utils"; +import loggerInjectable from "../../../common/logger.injectable"; export interface EventDetailsProps extends KubeObjectDetailsProps { } @@ -28,6 +29,7 @@ export interface EventDetailsProps extends KubeObjectDetailsProps { interface Dependencies { getDetailsUrl: GetDetailsUrl; apiManager: ApiManager; + logger: Logger; } const NonInjectedEventDetails = observer(({ @@ -35,6 +37,7 @@ const NonInjectedEventDetails = observer(({ getDetailsUrl, object: event, className, + logger, }: Dependencies & EventDetailsProps) => { if (!event) { return null; @@ -101,5 +104,6 @@ export const EventDetails = withInjectables(Non ...props, apiManager: di.inject(apiManagerInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+events/kube-event-details.tsx b/src/renderer/components/+events/kube-event-details.tsx index 29cb84b856..aef3bf7b10 100644 --- a/src/renderer/components/+events/kube-event-details.tsx +++ b/src/renderer/components/+events/kube-event-details.tsx @@ -12,12 +12,13 @@ import { DrawerItem, DrawerTitle } from "../drawer"; import { cssNames } from "../../utils"; import { LocaleDate } from "../locale-date"; import type { EventStore } from "./store"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import eventStoreInjectable from "./store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface KubeEventDetailsProps { object: KubeObject; @@ -26,6 +27,7 @@ export interface KubeEventDetailsProps { interface Dependencies { subscribeStores: SubscribeStores; eventStore: EventStore; + logger: Logger; } @observer @@ -46,7 +48,7 @@ class NonInjectedKubeEventDetails extends React.Component Promise; diff --git a/src/renderer/components/+extensions/uninstall-extension.injectable.tsx b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx new file mode 100644 index 0000000000..8da5e3090a --- /dev/null +++ b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx @@ -0,0 +1,81 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; +import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; +import extensionDiscoveryInjectable from "../../../extensions/extension-discovery/extension-discovery.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { LensExtensionId } from "../../../extensions/lens-extension"; +import { extensionDisplayName } from "../../../extensions/lens-extension"; +import { Notifications } from "../notifications"; +import React from "react"; +import { when } from "mobx"; +import { getMessageFromError } from "./get-message-from-error/get-message-from-error"; + +const uninstallExtensionInjectable = getInjectable({ + id: "uninstall-extension", + + instantiate: (di) => { + const extensionLoader = di.inject(extensionLoaderInjectable); + const extensionDiscovery = di.inject(extensionDiscoveryInjectable); + const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable); + const logger = di.inject(loggerInjectable); + + return async (extensionId: LensExtensionId): Promise => { + const ext = extensionLoader.getExtension(extensionId); + + if (!ext) { + logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`); + + return true; + } + + const { manifest } = ext; + const displayName = extensionDisplayName(manifest.name, manifest.version); + + try { + logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`); + extensionInstallationStateStore.setUninstalling(extensionId); + + await extensionDiscovery.uninstallExtension(extensionId); + + // wait for the ExtensionLoader to actually uninstall the extension + await when(() => !extensionLoader.userExtensions.has(extensionId)); + + Notifications.ok( +

+ {"Extension "} + {displayName} + {" successfully uninstalled!"} +

, + ); + + return true; + } catch (error) { + const message = getMessageFromError(error); + + logger.info( + `[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`, + { error }, + ); + Notifications.error( +

+ {"Uninstalling extension "} + {displayName} + {" has failed: "} + {message} +

, + ); + + return false; + } finally { + // Remove uninstall state on uninstall failure + extensionInstallationStateStore.clearUninstalling(extensionId); + } + }; + }, +}); + +export default uninstallExtensionInjectable; diff --git a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts b/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.injectable.ts deleted file mode 100644 index a060ed7e88..0000000000 --- a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.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 extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; -import { uninstallExtension } from "./uninstall-extension"; -import extensionInstallationStateStoreInjectable - from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; -import extensionDiscoveryInjectable - from "../../../../extensions/extension-discovery/extension-discovery.injectable"; - -const uninstallExtensionInjectable = getInjectable({ - id: "uninstall-extension", - - instantiate: (di) => - uninstallExtension({ - extensionLoader: di.inject(extensionLoaderInjectable), - extensionDiscovery: di.inject(extensionDiscoveryInjectable), - extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), - }), -}); - -export default uninstallExtensionInjectable; diff --git a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx b/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.tsx deleted file mode 100644 index 3db43813a2..0000000000 --- a/src/renderer/components/+extensions/uninstall-extension/uninstall-extension.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 type { ExtensionLoader } from "../../../../extensions/extension-loader"; -import type { LensExtensionId } from "../../../../extensions/lens-extension"; -import { extensionDisplayName } from "../../../../extensions/lens-extension"; -import logger from "../../../../main/logger"; -import type { ExtensionDiscovery } from "../../../../extensions/extension-discovery/extension-discovery"; -import { Notifications } from "../../notifications"; -import React from "react"; -import { when } from "mobx"; -import { getMessageFromError } from "../get-message-from-error/get-message-from-error"; -import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store"; - -interface Dependencies { - extensionLoader: ExtensionLoader; - extensionDiscovery: ExtensionDiscovery; - extensionInstallationStateStore: ExtensionInstallationStateStore; -} - -export const uninstallExtension = ({ - extensionLoader, - extensionDiscovery, - extensionInstallationStateStore, -}: Dependencies) => ( - async (extensionId: LensExtensionId): Promise => { - const ext = extensionLoader.getExtension(extensionId); - - if (!ext) { - logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`); - - return true; - } - - const { manifest } = ext; - const displayName = extensionDisplayName(manifest.name, manifest.version); - - try { - logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`); - extensionInstallationStateStore.setUninstalling(extensionId); - - await extensionDiscovery.uninstallExtension(extensionId); - - // wait for the ExtensionLoader to actually uninstall the extension - await when(() => !extensionLoader.userExtensions.has(extensionId)); - - Notifications.ok( -

- {"Extension "} - {displayName} - {" successfully uninstalled!"} -

, - ); - - return true; - } catch (error) { - const message = getMessageFromError(error); - - logger.info( - `[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`, - { error }, - ); - Notifications.error( -

- {"Uninstalling extension "} - {displayName} - {" has failed: "} - {message} -

, - ); - - return false; - } finally { - // Remove uninstall state on uninstall failure - extensionInstallationStateStore.clearUninstalling(extensionId); - } - } -); diff --git a/src/renderer/components/+namespaces/store.injectable.ts b/src/renderer/components/+namespaces/store.injectable.ts index eb2a7e4a35..50164e6626 100644 --- a/src/renderer/components/+namespaces/store.injectable.ts +++ b/src/renderer/components/+namespaces/store.injectable.ts @@ -11,6 +11,7 @@ import assert from "assert"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable"; import clusterConfiguredAccessibleNamespacesInjectable from "../../cluster/accessible-namespaces.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const namespaceStoreInjectable = getInjectable({ id: "namespace-store", @@ -25,6 +26,7 @@ const namespaceStoreInjectable = getInjectable({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), storage: createStorage("selected_namespaces", undefined), clusterConfiguredAccessibleNamespaces: di.inject(clusterConfiguredAccessibleNamespacesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-endpoints/endpoint-details.tsx b/src/renderer/components/+network-endpoints/endpoint-details.tsx index 4e014595b7..6e806683e8 100644 --- a/src/renderer/components/+network-endpoints/endpoint-details.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-details.tsx @@ -11,13 +11,19 @@ import { DrawerTitle } from "../drawer"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { Endpoints } from "../../../common/k8s-api/endpoints"; import { EndpointSubsetList } from "./endpoint-subset-list"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface EndpointsDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class EndpointsDetails extends React.Component { +class NonInjectedEndpointsDetails extends React.Component { render() { const { object: endpoint } = this.props; @@ -26,7 +32,7 @@ export class EndpointsDetails extends React.Component { } if (!(endpoint instanceof Endpoints)) { - logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint); + this.props.logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint); return null; } @@ -47,3 +53,10 @@ export class EndpointsDetails extends React.Component { ); } } + +export const EndpointsDetails = withInjectables(NonInjectedEndpointsDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+network-endpoints/store.injectable.ts b/src/renderer/components/+network-endpoints/store.injectable.ts index 7531b933b3..d376b039a0 100644 --- a/src/renderer/components/+network-endpoints/store.injectable.ts +++ b/src/renderer/components/+network-endpoints/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import endpointsApiInjectable from "../../../common/k8s-api/endpoints/endpoint.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 { EndpointsStore } from "./store"; @@ -19,6 +20,7 @@ const endpointsStoreInjectable = getInjectable({ return new EndpointsStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-ingresses/ingress-store.injectable.ts b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts index 909c784b99..8f5f1e6e39 100644 --- a/src/renderer/components/+network-ingresses/ingress-store.injectable.ts +++ b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.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 { IngressStore } from "./ingress-store"; @@ -19,6 +20,7 @@ const ingressStoreInjectable = getInjectable({ return new IngressStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-policies/network-policy-details.tsx b/src/renderer/components/+network-policies/network-policy-details.tsx index 60eec92e91..08b03e7186 100644 --- a/src/renderer/components/+network-policies/network-policy-details.tsx +++ b/src/renderer/components/+network-policies/network-policy-details.tsx @@ -13,15 +13,21 @@ import { Badge } from "../badge"; import { SubTitle } from "../layout/sub-title"; import { observer } from "mobx-react"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object"; import { isEmpty } from "lodash"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface NetworkPolicyDetailsProps extends KubeObjectDetailsProps { } +interface Dependencies { + logger: Logger; +} + @observer -export class NetworkPolicyDetails extends React.Component { +class NonInjectedNetworkPolicyDetails extends React.Component { renderIPolicyIpBlock(ipBlock: IPolicyIpBlock | undefined) { if (!ipBlock) { return null; @@ -159,7 +165,7 @@ export class NetworkPolicyDetails extends React.Component(NonInjectedNetworkPolicyDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+network-policies/store.injectable.ts b/src/renderer/components/+network-policies/store.injectable.ts index ad79a89b54..b17c0918d9 100644 --- a/src/renderer/components/+network-policies/store.injectable.ts +++ b/src/renderer/components/+network-policies/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import networkPolicyApiInjectable from "../../../common/k8s-api/endpoints/network-policy.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 { NetworkPolicyStore } from "./store"; @@ -19,6 +20,7 @@ const networkPolicyStoreInjectable = getInjectable({ return new NetworkPolicyStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 998a8c6576..a9d5e8a93c 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -15,12 +15,13 @@ import { ServicePortComponent } from "./service-port-component"; import type { EndpointsStore } from "../+network-endpoints/store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import type { PortForwardStore } from "../../port-forward"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import endpointsStoreInjectable from "../+network-endpoints/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface ServiceDetailsProps extends KubeObjectDetailsProps { } @@ -29,6 +30,7 @@ interface Dependencies { subscribeStores: SubscribeStores; portForwardStore: PortForwardStore; endpointsStore: EndpointsStore; + logger: Logger; } @observer @@ -59,7 +61,7 @@ class NonInjectedServiceDetails extends React.Component subscribeStores: di.inject(subscribeStoresInjectable), portForwardStore: di.inject(portForwardStoreInjectable), endpointsStore: di.inject(endpointsStoreInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index 79aeff7ec5..e30112ce4f 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -18,11 +18,12 @@ 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 logger from "../../../common/logger"; +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"; export interface ServicePortComponentProps { service: Service; @@ -31,6 +32,7 @@ export interface ServicePortComponentProps { interface Dependencies { portForwardStore: PortForwardStore; + logger: Logger; openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void; aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; @@ -127,7 +129,7 @@ class NonInjectedServicePortComponent extends React.Component( - NonInjectedServicePortComponent, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const ServicePortComponent = withInjectables(NonInjectedServicePortComponent, { + 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), + }), +}); diff --git a/src/renderer/components/+network-services/store.injectable.ts b/src/renderer/components/+network-services/store.injectable.ts index e54f994162..d705ca0d46 100644 --- a/src/renderer/components/+network-services/store.injectable.ts +++ b/src/renderer/components/+network-services/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.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 { ServiceStore } from "./store"; @@ -19,6 +20,7 @@ const serviceStoreInjectable = getInjectable({ return new ServiceStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+nodes/store.injectable.ts b/src/renderer/components/+nodes/store.injectable.ts index ac149210bd..a397db07e1 100644 --- a/src/renderer/components/+nodes/store.injectable.ts +++ b/src/renderer/components/+nodes/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.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 { NodeStore } from "./store"; @@ -19,6 +20,7 @@ const nodeStoreInjectable = getInjectable({ return new NodeStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx index 03cce618a0..d30ae17898 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx @@ -12,7 +12,9 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { PodSecurityPolicy } from "../../../common/k8s-api/endpoints"; import { Badge } from "../badge"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps { } @@ -25,8 +27,12 @@ interface RuleGroup { }[]; } +interface Dependencies { + logger: Logger; +} + @observer -export class PodSecurityPolicyDetails extends React.Component { +class NonInjectedPodSecurityPolicyDetails extends React.Component { renderRuleGroup(title: React.ReactNode, group: RuleGroup | undefined) { if (!group) return null; const { rule, ranges } = group; @@ -59,7 +65,7 @@ export class PodSecurityPolicyDetails extends React.Component(NonInjectedPodSecurityPolicyDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+pod-security-policies/store.injectable.ts b/src/renderer/components/+pod-security-policies/store.injectable.ts index 44ab372d70..2e27ad466a 100644 --- a/src/renderer/components/+pod-security-policies/store.injectable.ts +++ b/src/renderer/components/+pod-security-policies/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; import podSecurityPolicyApiInjectable from "../../../common/k8s-api/endpoints/pod-security-policy.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 { PodSecurityPolicyStore } from "./store"; @@ -19,6 +20,7 @@ const podSecurityPolicyStoreInjectable = getInjectable({ return new PodSecurityPolicyStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+storage-classes/storage-class-details.tsx b/src/renderer/components/+storage-classes/storage-class-details.tsx index 0df28e13f8..14f76cda32 100644 --- a/src/renderer/components/+storage-classes/storage-class-details.tsx +++ b/src/renderer/components/+storage-classes/storage-class-details.tsx @@ -15,12 +15,13 @@ import { StorageClass } from "../../../common/k8s-api/endpoints"; import type { StorageClassStore } from "./store"; import { VolumeDetailsList } from "../+storage-volumes/volume-details-list"; import type { PersistentVolumeStore } from "../+storage-volumes/store"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import storageClassStoreInjectable from "./store.injectable"; import persistentVolumeStoreInjectable from "../+storage-volumes/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface StorageClassDetailsProps extends KubeObjectDetailsProps { } @@ -29,6 +30,7 @@ interface Dependencies { subscribeStores: SubscribeStores; storageClassStore: StorageClassStore; persistentVolumeStore: PersistentVolumeStore; + logger: Logger; } @observer @@ -49,7 +51,7 @@ class NonInjectedStorageClassDetails extends React.Component { } +interface Dependencies { + logger: Logger; +} + @observer -export class PersistentVolumeDetails extends React.Component { +class NonInjectedPersistentVolumeDetails extends React.Component { render() { const { object: volume } = this.props; @@ -30,7 +36,7 @@ export class PersistentVolumeDetails extends React.Component(NonInjectedPersistentVolumeDetails, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts index a2e3223fa7..ac3eb8a07d 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts +++ b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts @@ -9,6 +9,7 @@ import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoint import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import { ClusterRoleBindingStore } from "./store"; import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; const clusterRoleBindingStoreInjectable = getInjectable({ id: "cluster-role-binding-store", @@ -19,6 +20,7 @@ const clusterRoleBindingStoreInjectable = getInjectable({ return new ClusterRoleBindingStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts index 9ae877f635..c88b457b1d 100644 --- a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts +++ b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts @@ -9,6 +9,7 @@ import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/clust import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; 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", @@ -19,6 +20,7 @@ const clusterRoleStoreInjectable = getInjectable({ return new ClusterRoleStore({ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts index a3f33e76bc..416223b071 100644 --- a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts +++ b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; import roleBindingApiInjectable from "../../../../common/k8s-api/endpoints/role-binding.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 { RoleBindingStore } from "./store"; @@ -19,6 +20,7 @@ const roleBindingStoreInjectable = getInjectable({ return new RoleBindingStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+roles/store.injectable.ts b/src/renderer/components/+user-management/+roles/store.injectable.ts index 8b86ddb0ab..f0df652438 100644 --- a/src/renderer/components/+user-management/+roles/store.injectable.ts +++ b/src/renderer/components/+user-management/+roles/store.injectable.ts @@ -9,6 +9,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; 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", @@ -19,6 +20,7 @@ const roleStoreInjectable = getInjectable({ return new RoleStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts index 98570c9017..fd96db2d65 100644 --- a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts +++ b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts @@ -9,6 +9,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable"; 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", @@ -19,6 +20,7 @@ const serviceAccountStoreInjectable = getInjectable({ return new ServiceAccountStore({ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx index 4701869ab6..68f05a54fe 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx @@ -17,12 +17,13 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { getDetailsUrl } from "../kube-detail-params"; import type { Job } from "../../../common/k8s-api/endpoints"; import { CronJob } from "../../../common/k8s-api/endpoints"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import cronJobStoreInjectable from "./store.injectable"; import jobStoreInjectable from "../+workloads-jobs/store.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface CronJobDetailsProps extends KubeObjectDetailsProps { } @@ -31,6 +32,7 @@ interface Dependencies { subscribeStores: SubscribeStores; jobStore: JobStore; cronJobStore: CronJobStore; + logger: Logger; } @observer @@ -51,7 +53,7 @@ class NonInjectedCronJobDetails extends React.Component subscribeStores: di.inject(subscribeStoresInjectable), cronJobStore: di.inject(cronJobStoreInjectable), jobStore: di.inject(jobStoreInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/components/+workloads-cronjobs/store.injectable.ts b/src/renderer/components/+workloads-cronjobs/store.injectable.ts index 6b0fab2c9c..2569e77440 100644 --- a/src/renderer/components/+workloads-cronjobs/store.injectable.ts +++ b/src/renderer/components/+workloads-cronjobs/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getJobsByOwnerInjectable from "../+workloads-jobs/get-jobs-by-owner.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -21,6 +22,7 @@ const cronJobStoreInjectable = getInjectable({ return new CronJobStore({ getJobsByOwner: di.inject(getJobsByOwnerInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-daemonsets/store.injectable.ts b/src/renderer/components/+workloads-daemonsets/store.injectable.ts index 5711ad625f..59252688bf 100644 --- a/src/renderer/components/+workloads-daemonsets/store.injectable.ts +++ b/src/renderer/components/+workloads-daemonsets/store.injectable.ts @@ -10,6 +10,7 @@ import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-create import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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", @@ -21,6 +22,7 @@ const daemonSetStoreInjectable = getInjectable({ return new DaemonSetStore({ getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-deployments/store.injectable.ts b/src/renderer/components/+workloads-deployments/store.injectable.ts index b77d9aa496..1a6a9f7df1 100644 --- a/src/renderer/components/+workloads-deployments/store.injectable.ts +++ b/src/renderer/components/+workloads-deployments/store.injectable.ts @@ -10,6 +10,7 @@ import { storesAndApisCanBeCreatedInjectionToken } from "../../../common/k8s-api 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", @@ -21,6 +22,7 @@ const deploymentStoreInjectable = getInjectable({ return new DeploymentStore({ podStore: di.inject(podStoreInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-jobs/store.injectable.ts b/src/renderer/components/+workloads-jobs/store.injectable.ts index 8f394a1bf5..28d3806788 100644 --- a/src/renderer/components/+workloads-jobs/store.injectable.ts +++ b/src/renderer/components/+workloads-jobs/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -21,6 +22,7 @@ const jobStoreInjectable = getInjectable({ return new JobStore({ getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx index 21d73df6e9..da613177e6 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -18,11 +18,12 @@ 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 logger from "../../../common/logger"; +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"; export interface PodContainerPortProps { pod: Pod; @@ -31,6 +32,7 @@ export interface PodContainerPortProps { interface Dependencies { portForwardStore: PortForwardStore; + logger: Logger; openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void; aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; @@ -123,7 +125,7 @@ class NonInjectedPodContainerPort extends React.Component( - NonInjectedPodContainerPort, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +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), + }), +}); diff --git a/src/renderer/components/+workloads-pods/store.injectable.ts b/src/renderer/components/+workloads-pods/store.injectable.ts index a1bf527bef..473cd80c5c 100644 --- a/src/renderer/components/+workloads-pods/store.injectable.ts +++ b/src/renderer/components/+workloads-pods/store.injectable.ts @@ -10,6 +10,7 @@ import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manag 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", @@ -21,6 +22,7 @@ const podStoreInjectable = getInjectable({ return new PodStore({ podMetricsApi: di.inject(podMetricsApiInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-replicasets/store.injectable.ts b/src/renderer/components/+workloads-replicasets/store.injectable.ts index c961e504ad..b54fd0295d 100644 --- a/src/renderer/components/+workloads-replicasets/store.injectable.ts +++ b/src/renderer/components/+workloads-replicasets/store.injectable.ts @@ -10,6 +10,7 @@ import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-create import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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", @@ -21,6 +22,7 @@ const replicaSetStoreInjectable = getInjectable({ return new ReplicaSetStore({ getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/+workloads-statefulsets/store.injectable.ts b/src/renderer/components/+workloads-statefulsets/store.injectable.ts index 5931b5dfaf..ce8649b109 100644 --- a/src/renderer/components/+workloads-statefulsets/store.injectable.ts +++ b/src/renderer/components/+workloads-statefulsets/store.injectable.ts @@ -7,6 +7,7 @@ import assert from "assert"; import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; 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"; @@ -21,6 +22,7 @@ const statefulSetStoreInjectable = getInjectable({ return new StatefulSetStore({ getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable), context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }, api); }, injectionToken: kubeObjectStoreInjectionToken, diff --git a/src/renderer/components/dock/terminal/create-terminal.injectable.ts b/src/renderer/components/dock/terminal/create-terminal.injectable.ts index cdf2e95f94..27c7c0664a 100644 --- a/src/renderer/components/dock/terminal/create-terminal.injectable.ts +++ b/src/renderer/components/dock/terminal/create-terminal.injectable.ts @@ -13,6 +13,7 @@ import terminalCopyOnSelectInjectable from "../../../../common/user-store/termin import isMacInjectable from "../../../../common/vars/is-mac.injectable"; import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable"; import xtermColorThemeInjectable from "../../../themes/terminal-colors.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal; @@ -26,6 +27,7 @@ const createTerminalInjectable = getInjectable({ isMac: di.inject(isMacInjectable), openLinkInBrowser: di.inject(openLinkInBrowserInjectable), xtermColorTheme: di.inject(xtermColorThemeInjectable), + logger: di.inject(loggerInjectable), }; return (tabId, api) => new Terminal(dependencies, { tabId, api }); diff --git a/src/renderer/components/dock/terminal/terminal.ts b/src/renderer/components/dock/terminal/terminal.ts index 541e2b2947..5f49882f1c 100644 --- a/src/renderer/components/dock/terminal/terminal.ts +++ b/src/renderer/components/dock/terminal/terminal.ts @@ -13,7 +13,7 @@ import type { TerminalApi } from "../../../api/terminal-api"; import { disposer } from "../../../utils"; import { once } from "lodash"; import { clipboard } from "electron"; -import logger from "../../../../common/logger"; +import type { Logger } from "../../../../common/logger"; import type { TerminalConfig } from "../../../../common/user-store/preferences-helpers"; import assert from "assert"; import { TerminalChannels } from "../../../../common/terminal/channels"; @@ -26,6 +26,7 @@ export interface TerminalDependencies { readonly terminalCopyOnSelect: IComputedValue; readonly isMac: boolean; readonly xtermColorTheme: IComputedValue>; + readonly logger: Logger; openLinkInBrowser: OpenLinkInBrowser; } @@ -199,14 +200,14 @@ export class Terminal { }; setFontSize = (fontSize: number) => { - logger.info(`[TERMINAL]: set fontSize to ${fontSize}`); + this.dependencies.logger.info(`[TERMINAL]: set fontSize to ${fontSize}`); this.xterm.options.fontSize = fontSize; this.fit(); }; setFontFamily = (fontFamily: string) => { - logger.info(`[TERMINAL]: set fontFamily to ${fontFamily}`); + this.dependencies.logger.info(`[TERMINAL]: set fontFamily to ${fontFamily}`); this.xterm.options.fontFamily = fontFamily; this.fit(); diff --git a/src/renderer/components/input/drop-file-input.tsx b/src/renderer/components/input/drop-file-input.tsx index 70a847ad45..41461a27c9 100644 --- a/src/renderer/components/input/drop-file-input.tsx +++ b/src/renderer/components/input/drop-file-input.tsx @@ -9,7 +9,9 @@ import type { IClassName } from "../../utils"; import { autoBind, cssNames } from "../../utils"; import { observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; -import logger from "../../../main/logger"; +import type { Logger } from "../../../common/logger"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import loggerInjectable from "../../../common/logger.injectable"; export interface DropFileInputProps extends React.DOMAttributes { className?: IClassName; @@ -21,12 +23,16 @@ export interface DropFileMeta { evt: React.DragEvent; } +interface Dependencies { + logger: Logger; +} + @observer -export class DropFileInput extends React.Component> { +class NonInjectedDropFileInput extends React.Component & Dependencies> { @observable dropAreaActive = false; dragCounter = 0; // Counter preventing firing onDragLeave() too early (https://stackoverflow.com/questions/7110353/html5-dragleave-fired-when-hovering-a-child-element) - constructor(props: DropFileInputProps) { + constructor(props: DropFileInputProps & Dependencies) { super(props); makeObservable(this); autoBind(this); @@ -92,9 +98,18 @@ export class DropFileInput extends React.Component must contain only single child element`); + this.props.logger.error(`Error: must contain only single child element`); return this.props.children; } } } + +const InjectedDropFileInput = withInjectables>(NonInjectedDropFileInput, { + getProps: (di, props) => ({ + ...props, + logger: di.inject(loggerInjectable), + }), +}); + +export const DropFileInput = (props: DropFileInputProps) => ; diff --git a/src/renderer/components/kube-object-meta/kube-object-meta.tsx b/src/renderer/components/kube-object-meta/kube-object-meta.tsx index f339877eb2..593f2cb2ae 100644 --- a/src/renderer/components/kube-object-meta/kube-object-meta.tsx +++ b/src/renderer/components/kube-object-meta/kube-object-meta.tsx @@ -11,13 +11,14 @@ import type { ApiManager } from "../../../common/k8s-api/api-manager"; import { Link } from "react-router-dom"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { LocaleDate } from "../locale-date"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import { KubeObjectAge } from "../kube-object/age"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import { observer } from "mobx-react"; import { withInjectables } from "@ogre-tools/injectable-react"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; export interface KubeObjectMetaProps { object: KubeObject; @@ -27,6 +28,7 @@ export interface KubeObjectMetaProps { interface Dependencies { getDetailsUrl: GetDetailsUrl; apiManager: ApiManager; + logger: Logger; } const NonInjectedKubeObjectMeta = observer(({ @@ -38,6 +40,7 @@ const NonInjectedKubeObjectMeta = observer(({ "resourceVersion", "selfLink", ], + logger, }: Dependencies & KubeObjectMetaProps) => { if (!object) { return null; @@ -118,5 +121,6 @@ export const KubeObjectMeta = withInjectables ...props, getDetailsUrl: di.inject(getDetailsUrlInjectable), apiManager: di.inject(apiManagerInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/frames/root-frame/init-root-frame.injectable.ts b/src/renderer/frames/root-frame/init-root-frame.injectable.ts new file mode 100644 index 0000000000..8d2c3a43be --- /dev/null +++ b/src/renderer/frames/root-frame/init-root-frame.injectable.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import ipcRendererInjectable from "../../utils/channel/ipc-renderer.injectable"; +import bindProtocolAddRouteHandlersInjectable from "../../protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable"; +import lensProtocolRouterRendererInjectable from "../../protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable"; +import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; +import registerIpcListenersInjectable from "../../ipc/register-ipc-listeners.injectable"; +import loadExtensionsInjectable from "../load-extensions.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import { delay } from "../../../common/utils"; +import { broadcastMessage } from "../../../common/ipc"; +import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling"; + +const initRootFrameInjectable = getInjectable({ + id: "init-root-frame", + instantiate: (di) => { + const loadExtensions = di.inject(loadExtensionsInjectable); + const registerIpcListeners = di.inject(registerIpcListenersInjectable); + const ipcRenderer = di.inject(ipcRendererInjectable); + const bindProtocolAddRouteHandlers = di.inject(bindProtocolAddRouteHandlersInjectable); + const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable); + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const logger = di.inject(loggerInjectable); + + return async (unmountRoot: () => void) => { + catalogEntityRegistry.init(); + + try { + // maximum time to let bundled extensions finish loading + const timeout = delay(10000); + + const loadingExtensions = await loadExtensions(); + + const loadingBundledExtensions = loadingExtensions + .filter((e) => e.isBundled) + .map((e) => e.loaded); + + const bundledExtensionsFinished = Promise.all(loadingBundledExtensions); + + await Promise.race([bundledExtensionsFinished, timeout]); + } finally { + ipcRenderer.send(bundledExtensionsLoaded); + } + + lensProtocolRouterRenderer.init(); + + bindProtocolAddRouteHandlers(); + + window.addEventListener("offline", () => + broadcastMessage("network:offline"), + ); + + window.addEventListener("online", () => broadcastMessage("network:online")); + + registerIpcListeners(); + + window.addEventListener("beforeunload", () => { + logger.info("[ROOT-FRAME]: Unload app"); + + unmountRoot(); + }); + }; + }, +}); + +export default initRootFrameInjectable; diff --git a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.injectable.ts b/src/renderer/frames/root-frame/init-root-frame/init-root-frame.injectable.ts deleted file mode 100644 index fb82bc238b..0000000000 --- a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.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 { initRootFrame } from "./init-root-frame"; -import ipcRendererInjectable from "../../../utils/channel/ipc-renderer.injectable"; -import bindProtocolAddRouteHandlersInjectable from "../../../protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable"; -import lensProtocolRouterRendererInjectable from "../../../protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable"; -import catalogEntityRegistryInjectable from "../../../api/catalog/entity/registry.injectable"; -import registerIpcListenersInjectable from "../../../ipc/register-ipc-listeners.injectable"; -import loadExtensionsInjectable from "../../load-extensions.injectable"; - -const initRootFrameInjectable = getInjectable({ - id: "init-root-frame", - instantiate: (di) => initRootFrame({ - loadExtensions: di.inject(loadExtensionsInjectable), - registerIpcListeners: di.inject(registerIpcListenersInjectable), - ipcRenderer: di.inject(ipcRendererInjectable), - bindProtocolAddRouteHandlers: di.inject(bindProtocolAddRouteHandlersInjectable), - lensProtocolRouterRenderer: di.inject(lensProtocolRouterRendererInjectable), - catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), - }), -}); - -export default initRootFrameInjectable; diff --git a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts b/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts deleted file mode 100644 index 1e67ee98da..0000000000 --- a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.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 { delay } from "../../../../common/utils"; -import { broadcastMessage } from "../../../../common/ipc"; -import logger from "../../../../common/logger"; -import type { ExtensionLoading } from "../../../../extensions/extension-loader"; -import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; -import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling"; - - -interface Dependencies { - loadExtensions: () => Promise; - - registerIpcListeners: () => void; - - // TODO: Move usages of third party library behind abstraction - ipcRenderer: { send: (name: string) => void }; - - // TODO: Remove dependencies being here only for correct timing of initialization - bindProtocolAddRouteHandlers: () => void; - lensProtocolRouterRenderer: { init: () => void }; - catalogEntityRegistry: CatalogEntityRegistry; -} - -const logPrefix = "[ROOT-FRAME]:"; - -export const initRootFrame = - ({ - loadExtensions, - bindProtocolAddRouteHandlers, - lensProtocolRouterRenderer, - ipcRenderer, - registerIpcListeners, - catalogEntityRegistry, - }: Dependencies) => - async (unmountRoot: () => void) => { - catalogEntityRegistry.init(); - - try { - // maximum time to let bundled extensions finish loading - const timeout = delay(10000); - - const loadingExtensions = await loadExtensions(); - - const loadingBundledExtensions = loadingExtensions - .filter((e) => e.isBundled) - .map((e) => e.loaded); - - const bundledExtensionsFinished = Promise.all(loadingBundledExtensions); - - await Promise.race([bundledExtensionsFinished, timeout]); - } finally { - ipcRenderer.send(bundledExtensionsLoaded); - } - - lensProtocolRouterRenderer.init(); - - bindProtocolAddRouteHandlers(); - - window.addEventListener("offline", () => - broadcastMessage("network:offline"), - ); - - window.addEventListener("online", () => broadcastMessage("network:online")); - - registerIpcListeners(); - - window.addEventListener("beforeunload", () => { - logger.info(`${logPrefix} Unload app`); - - unmountRoot(); - }); - }; diff --git a/src/renderer/kube-watch-api/kube-watch-api.injectable.ts b/src/renderer/kube-watch-api/kube-watch-api.injectable.ts index 23e53bf7cc..1a376819a5 100644 --- a/src/renderer/kube-watch-api/kube-watch-api.injectable.ts +++ b/src/renderer/kube-watch-api/kube-watch-api.injectable.ts @@ -3,6 +3,7 @@ * 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 clusterFrameContextForNamespacedResourcesInjectable from "../cluster-frame-context/for-namespaced-resources.injectable"; import { KubeWatchApi } from "./kube-watch-api"; @@ -11,6 +12,7 @@ const kubeWatchApiInjectable = getInjectable({ instantiate: (di) => new KubeWatchApi({ clusterContext: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), }), }); diff --git a/src/renderer/kube-watch-api/kube-watch-api.ts b/src/renderer/kube-watch-api/kube-watch-api.ts index 4fc31bd1d5..04d6ccb32b 100644 --- a/src/renderer/kube-watch-api/kube-watch-api.ts +++ b/src/renderer/kube-watch-api/kube-watch-api.ts @@ -6,7 +6,7 @@ import { comparer, reaction } from "mobx"; import type { Disposer } from "../../common/utils"; import { disposer, getOrInsert, noop, WrappedAbortController } from "../../common/utils"; import { once } from "lodash"; -import logger from "../../common/logger"; +import type { Logger } from "../../common/logger"; import type { KubeObjectStoreLoadAllParams, KubeObjectStoreSubscribeParams } from "../../common/k8s-api/kube-object.store"; import AbortController from "abort-controller"; import type { ClusterContext } from "../cluster-frame-context/cluster-frame-context"; @@ -21,13 +21,19 @@ interface SubscribeStoreParams { onLoadFailure?: (err: any) => void; } +interface WatchCountDependencies { + readonly logger: Logger; +} + class WatchCount { readonly #data = new Map(); + constructor(private readonly dependencies: WatchCountDependencies) {} + public inc(store: SubscribableStore): number { const newCount = getOrInsert(this.#data, store, 0) + 1; - logger.info(`[KUBE-WATCH-API]: inc() count for ${store.api.apiBase} is now ${newCount}`); + this.dependencies.logger.info(`[KUBE-WATCH-API]: inc() count for ${store.api.apiBase} is now ${newCount}`); this.#data.set(store, newCount); return newCount; @@ -46,7 +52,7 @@ class WatchCount { throw new Error(`Cannot dec count more times than it has been inc: ${store.api.kind}`); } - logger.info(`[KUBE-WATCH-API]: dec() count for ${store.api.apiBase} is now ${newCount}`); + this.dependencies.logger.info(`[KUBE-WATCH-API]: dec() count for ${store.api.apiBase} is now ${newCount}`); this.#data.set(store, newCount); return newCount; @@ -69,6 +75,7 @@ export interface KubeWatchSubscribeStoreOptions { interface Dependencies { readonly clusterContext: ClusterContext; + readonly logger: Logger; } export interface SubscribableStore { @@ -84,7 +91,7 @@ export interface SubscribableStore { export type SubscribeStores = (stores: SubscribableStore[], opts?: KubeWatchSubscribeStoreOptions) => Disposer; export class KubeWatchApi { - readonly #watch = new WatchCount(); + readonly #watch = new WatchCount(this.dependencies); constructor(private readonly dependencies: Dependencies) {} @@ -173,8 +180,8 @@ export class KubeWatchApi { protected log(message: any, meta: any) { const log = message instanceof Error - ? console.error - : console.debug; + ? this.dependencies.logger.error + : this.dependencies.logger.debug; log("[KUBE-WATCH-API]:", message, { time: new Date().toLocaleString(), diff --git a/src/renderer/port-forward/port-forward-dialog.tsx b/src/renderer/port-forward/port-forward-dialog.tsx index 692ec488ca..831edd2e70 100644 --- a/src/renderer/port-forward/port-forward-dialog.tsx +++ b/src/renderer/port-forward/port-forward-dialog.tsx @@ -18,18 +18,20 @@ import { Checkbox } from "../components/checkbox"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { PortForwardDialogData, PortForwardDialogModel } from "./port-forward-dialog-model/port-forward-dialog-model"; import portForwardDialogModelInjectable from "./port-forward-dialog-model/port-forward-dialog-model.injectable"; -import logger from "../../common/logger"; +import type { Logger } from "../../common/logger"; import portForwardStoreInjectable from "./port-forward-store/port-forward-store.injectable"; import aboutPortForwardingInjectable from "./about-port-forwarding.injectable"; import notifyErrorPortForwardingInjectable from "./notify-error-port-forwarding.injectable"; import type { OpenPortForward } from "./open-port-forward.injectable"; import openPortForwardInjectable from "./open-port-forward.injectable"; +import loggerInjectable from "../../common/logger.injectable"; export interface PortForwardDialogProps extends Partial {} interface Dependencies { portForwardStore: PortForwardStore; model: PortForwardDialogModel; + logger: Logger; aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; openPortForward: OpenPortForward; @@ -94,7 +96,7 @@ class NonInjectedPortForwardDialog extends Component( - NonInjectedPortForwardDialog, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - model: di.inject(portForwardDialogModelInjectable), - aboutPortForwarding: di.inject(aboutPortForwardingInjectable), - notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const PortForwardDialog = withInjectables(NonInjectedPortForwardDialog, { + getProps: (di, props) => ({ + ...props, + portForwardStore: di.inject(portForwardStoreInjectable), + model: di.inject(portForwardDialogModelInjectable), + aboutPortForwarding: di.inject(aboutPortForwardingInjectable), + notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), + openPortForward: di.inject(openPortForwardInjectable), + logger: di.inject(loggerInjectable), + }), +}); diff --git a/src/renderer/port-forward/port-forward-store/port-forward-store.injectable.ts b/src/renderer/port-forward/port-forward-store/port-forward-store.injectable.ts index 2d309c315d..3b698f4e73 100644 --- a/src/renderer/port-forward/port-forward-store/port-forward-store.injectable.ts +++ b/src/renderer/port-forward/port-forward-store/port-forward-store.injectable.ts @@ -9,6 +9,7 @@ import createStorageInjectable from "../../utils/create-storage/create-storage.i import notifyErrorPortForwardingInjectable from "../notify-error-port-forwarding.injectable"; import requestActivePortForwardInjectable from "./request-active-port-forward.injectable"; import apiBaseInjectable from "../../../common/k8s-api/api-base.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; const portForwardStoreInjectable = getInjectable({ id: "port-forward-store", @@ -24,6 +25,7 @@ const portForwardStoreInjectable = getInjectable({ notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable), apiBase: di.inject(apiBaseInjectable), requestActivePortForward: di.inject(requestActivePortForwardInjectable), + logger: di.inject(loggerInjectable), }); }, }); diff --git a/src/renderer/port-forward/port-forward-store/port-forward-store.ts b/src/renderer/port-forward/port-forward-store/port-forward-store.ts index 179dd2f39c..b68903b171 100644 --- a/src/renderer/port-forward/port-forward-store/port-forward-store.ts +++ b/src/renderer/port-forward/port-forward-store/port-forward-store.ts @@ -10,14 +10,15 @@ import { autoBind, disposer } from "../../utils"; import type { ForwardedPort } from "../port-forward-item"; import { PortForwardItem } from "../port-forward-item"; import { waitUntilFree } from "tcp-port-used"; -import logger from "../../../common/logger"; +import type { Logger } from "../../../common/logger"; import type { JsonApi } from "../../../common/k8s-api/json-api"; import type { RequestActivePortForward } from "./request-active-port-forward.injectable"; interface Dependencies { readonly storage: StorageLayer; - notifyErrorPortForwarding: (message: string) => void; readonly apiBase: JsonApi; // TODO: replace with individual dependencies + readonly logger: Logger; + notifyErrorPortForwarding: (message: string) => void; requestActivePortForward: RequestActivePortForward; } @@ -38,7 +39,7 @@ export class PortForwardStore extends ItemStore { const savedPortForwards = this.dependencies.storage.get(); // undefined on first load if (Array.isArray(savedPortForwards) && savedPortForwards.length > 0) { - logger.info("[PORT-FORWARD-STORE] starting saved port-forwards"); + this.dependencies.logger.info("[PORT-FORWARD-STORE] starting saved port-forwards"); // add the disabled ones await Promise.all(savedPortForwards.filter(pf => pf.status === "Disabled").map(this.add)); @@ -169,7 +170,7 @@ export class PortForwardStore extends ItemStore { if (!pf) { const error = new Error("port-forward not found"); - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] Error getting port-forward: ${error}`, portForward, ); @@ -181,7 +182,7 @@ export class PortForwardStore extends ItemStore { await this.stop(portForward); } catch (error) { if (pf.status === "Active") { - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] Error removing port-forward: ${error}`, portForward, ); @@ -214,7 +215,7 @@ export class PortForwardStore extends ItemStore { const pf = this.findPortForward(portForward); if (!pf) { - logger.warn( + this.dependencies.logger.warn( "[PORT-FORWARD-STORE] Error getting port-forward: port-forward not found", portForward, ); @@ -231,7 +232,7 @@ export class PortForwardStore extends ItemStore { ); await waitUntilFree(+forwardPort, 200, 1000); } catch (error) { - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] Error stopping active port-forward: ${error}`, portForward, ); @@ -289,7 +290,7 @@ export class PortForwardStore extends ItemStore { response?.port && response.port != +pf.forwardPort ) { - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] specified ${pf.forwardPort}, got ${response.port}`, ); } @@ -297,7 +298,7 @@ export class PortForwardStore extends ItemStore { pf.forwardPort = response.port; pf.status = "Active"; } catch (error) { - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] Error starting port-forward: ${error}`, pf, ); @@ -328,7 +329,7 @@ export class PortForwardStore extends ItemStore { const pf = await this.dependencies.requestActivePortForward(portForward); if (pf?.forwardPort && pf.forwardPort !== portForward.forwardPort) { - logger.warn( + this.dependencies.logger.warn( `[PORT-FORWARD-STORE] local port, expected ${pf.forwardPort}, got ${portForward.forwardPort}`, ); }