From a0e15c453fd9337550a8b29a58bf276d8d00e791 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 9 Aug 2022 14:58:21 -0400 Subject: [PATCH] Convert the rest of shell sessions to be DI-ed - This is a prerequesit for using the new createKubeJsonApiForClusterInjectable Signed-off-by: Sebastian Malton --- .../shell-api-request.injectable.ts | 4 +- .../shell-api-request/shell-api-request.ts | 20 ++------ .../create-shell-session.injectable.ts | 32 +++++++----- .../local-shell-session.injectable.ts | 33 ------------ .../local-shell-session.ts | 31 ++++++------ .../local-shell-session/open.injectable.ts | 49 ++++++++++++++++++ .../node-shell-session.injectable.ts | 32 ------------ .../node-shell-session/node-shell-session.ts | 28 ++++++----- .../node-shell-session/open.injectable.ts | 43 ++++++++++++++++ .../modify-terminal-shell-env.injectable.ts | 49 ++++++++++++++++++ .../terminal-shell-env-modifiers.ts | 42 ---------------- .../terminal-shell-env-modify.injectable.ts | 21 -------- src/main/shell-session/shell-session.ts | 50 +++++++++++++------ 13 files changed, 233 insertions(+), 201 deletions(-) delete mode 100644 src/main/shell-session/local-shell-session/local-shell-session.injectable.ts create mode 100644 src/main/shell-session/local-shell-session/open.injectable.ts delete mode 100644 src/main/shell-session/node-shell-session/node-shell-session.injectable.ts create mode 100644 src/main/shell-session/node-shell-session/open.injectable.ts create mode 100644 src/main/shell-session/shell-env-modifier/modify-terminal-shell-env.injectable.ts delete mode 100644 src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts delete mode 100644 src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts 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 index a9dc0c6747..550a8d2560 100644 --- 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 @@ -4,15 +4,15 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { shellApiRequest } from "./shell-api-request"; -import createShellSessionInjectable from "../../../shell-session/create-shell-session.injectable"; import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable"; import clusterManagerInjectable from "../../../cluster/manager.injectable"; +import openShellSessionInjectable from "../../../shell-session/create-shell-session.injectable"; const shellApiRequestInjectable = getInjectable({ id: "shell-api-request", instantiate: (di) => shellApiRequest({ - createShellSession: di.inject(createShellSessionInjectable), + openShellSession: di.inject(openShellSessionInjectable), authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate, clusterManager: di.inject(clusterManagerInjectable), }), 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 index b01a1db44b..f4492e96f6 100644 --- 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 @@ -4,28 +4,20 @@ */ import logger from "../../../logger"; -import type WebSocket from "ws"; import { Server as WebSocketServer } from "ws"; import type { ProxyApiRequestArgs } from "../types"; import type { ClusterManager } from "../../../cluster/manager"; import URLParse from "url-parse"; -import type { Cluster } from "../../../../common/cluster/cluster"; import type { ClusterId } from "../../../../common/cluster-types"; +import type { OpenShellSession } from "../../../shell-session/create-shell-session.injectable"; interface Dependencies { authenticateRequest: (clusterId: ClusterId, tabId: string, shellToken: string | undefined) => boolean; - - createShellSession: (args: { - webSocket: WebSocket; - cluster: Cluster; - tabId: string; - nodeName?: string; - }) => { open: () => Promise }; - + openShellSession: OpenShellSession; clusterManager: ClusterManager; } -export const shellApiRequest = ({ createShellSession, authenticateRequest, clusterManager }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => { +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); @@ -37,10 +29,8 @@ export const shellApiRequest = ({ createShellSession, authenticateRequest, clust const ws = new WebSocketServer({ noServer: true }); - ws.handleUpgrade(req, socket, head, (webSocket) => { - const shell = createShellSession({ webSocket, cluster, tabId, nodeName }); - - shell.open() + 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/shell-session/create-shell-session.injectable.ts b/src/main/shell-session/create-shell-session.injectable.ts index 5bd5569053..6af65dc908 100644 --- a/src/main/shell-session/create-shell-session.injectable.ts +++ b/src/main/shell-session/create-shell-session.injectable.ts @@ -5,25 +5,31 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../../common/cluster/cluster"; import type WebSocket from "ws"; -import localShellSessionInjectable from "./local-shell-session/local-shell-session.injectable"; -import nodeShellSessionInjectable from "./node-shell-session/node-shell-session.injectable"; +import openLocalShellSessionInjectable from "./local-shell-session/open.injectable"; +import openNodeShellSessionInjectable from "./node-shell-session/open.injectable"; -interface Args { - webSocket: WebSocket; +export interface OpenShellSessionArgs { + websocket: WebSocket; cluster: Cluster; tabId: string; nodeName?: string; } -const createShellSessionInjectable = getInjectable({ - id: "create-shell-session", +export type OpenShellSession = (args: OpenShellSessionArgs) => Promise; - instantiate: - (di) => - ({ nodeName, ...rest }: Args) => - !nodeName - ? di.inject(localShellSessionInjectable, rest) - : di.inject(nodeShellSessionInjectable, { nodeName, ...rest }), +const openShellSessionInjectable = getInjectable({ + id: "open-shell-session", + + instantiate: (di): OpenShellSession => { + const openLocalShellSession = di.inject(openLocalShellSessionInjectable); + const openNodeShellSession = di.inject(openNodeShellSessionInjectable); + + return ({ nodeName, ...args }) => ( + nodeName + ? openNodeShellSession({ nodeName, ...args }) + : openLocalShellSession(args) + ); + }, }); -export default createShellSessionInjectable; +export default openShellSessionInjectable; diff --git a/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts b/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts deleted file mode 100644 index ed9b86819b..0000000000 --- a/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { LocalShellSession } from "./local-shell-session"; -import type { Cluster } from "../../../common/cluster/cluster"; -import type WebSocket from "ws"; -import createKubectlInjectable from "../../kubectl/create-kubectl.injectable"; -import terminalShellEnvModifiersInjectable from "../shell-env-modifier/terminal-shell-env-modify.injectable"; - -interface InstantiationParameter { - webSocket: WebSocket; - cluster: Cluster; - tabId: string; -} - -const localShellSessionInjectable = getInjectable({ - id: "local-shell-session", - - instantiate: (di, { cluster, tabId, webSocket }: InstantiationParameter) => { - const createKubectl = di.inject(createKubectlInjectable); - const localShellEnvModify = di.inject(terminalShellEnvModifiersInjectable); - - const kubectl = createKubectl(cluster.version); - - return new LocalShellSession(localShellEnvModify, kubectl, webSocket, cluster, tabId); - }, - - lifecycle: lifecycleEnum.transient, -}); - -export default localShellSessionInjectable; diff --git a/src/main/shell-session/local-shell-session/local-shell-session.ts b/src/main/shell-session/local-shell-session/local-shell-session.ts index 9f42c7f242..cae62affe2 100644 --- a/src/main/shell-session/local-shell-session/local-shell-session.ts +++ b/src/main/shell-session/local-shell-session/local-shell-session.ts @@ -3,24 +3,27 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type WebSocket from "ws"; import path from "path"; -import { UserStore } from "../../../common/user-store"; -import type { Cluster } from "../../../common/cluster/cluster"; -import type { ClusterId } from "../../../common/cluster-types"; +import type { UserStore } from "../../../common/user-store"; +import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session"; import { ShellSession } from "../shell-session"; -import type { Kubectl } from "../../kubectl/kubectl"; -import { baseBinariesDir } from "../../../common/vars"; +import type { ModifyTerminalShellEnv } from "../shell-env-modifier/modify-terminal-shell-env.injectable"; + +export interface LocalShellSessionDependencies extends ShellSessionDependencies { + modifyTerminalShellEnv: ModifyTerminalShellEnv; + readonly directoryForBinaries: string; + readonly userStore: UserStore; +} export class LocalShellSession extends ShellSession { ShellType = "shell"; - constructor(protected shellEnvModify: (clusterId: ClusterId, env: Record) => Record, kubectl: Kubectl, websocket: WebSocket, cluster: Cluster, terminalId: string) { - super(kubectl, websocket, cluster, terminalId); + constructor(protected readonly dependencies: LocalShellSessionDependencies, args: ShellSessionArgs) { + super(dependencies, args); } protected getPathEntries(): string[] { - return [baseBinariesDir.get()]; + return [this.dependencies.directoryForBinaries]; } protected get cwd(): string | undefined { @@ -31,7 +34,7 @@ export class LocalShellSession extends ShellSession { let env = await this.getCachedShellEnv(); // extensions can modify the env - env = this.shellEnvModify(this.cluster.id, env); + env = this.dependencies.modifyTerminalShellEnv(this.cluster.id, env); const shell = env.PTYSHELL; @@ -45,16 +48,16 @@ export class LocalShellSession extends ShellSession { } protected async getShellArgs(shell: string): Promise { - const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath(); - const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences); + const pathFromPreferences = this.dependencies.userStore.kubectlBinariesPath || this.kubectl.getBundledPath(); + const kubectlPathDir = this.dependencies.userStore.downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences); switch(path.basename(shell)) { case "powershell.exe": - return ["-NoExit", "-command", `& {$Env:PATH="${kubectlPathDir};${baseBinariesDir.get()};$Env:PATH"}`]; + return ["-NoExit", "-command", `& {$Env:PATH="${kubectlPathDir};${this.dependencies.directoryForBinaries};$Env:PATH"}`]; case "bash": return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")]; case "fish": - return ["--login", "--init-command", `export PATH="${kubectlPathDir}:${baseBinariesDir.get()}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`]; + return ["--login", "--init-command", `export PATH="${kubectlPathDir}:${this.dependencies.directoryForBinaries}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`]; case "zsh": return ["--login"]; default: diff --git a/src/main/shell-session/local-shell-session/open.injectable.ts b/src/main/shell-session/local-shell-session/open.injectable.ts new file mode 100644 index 0000000000..fefba0902c --- /dev/null +++ b/src/main/shell-session/local-shell-session/open.injectable.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { LocalShellSessionDependencies } from "./local-shell-session"; +import { LocalShellSession } from "./local-shell-session"; +import createKubectlInjectable from "../../kubectl/create-kubectl.injectable"; +import modifyTerminalShellEnvInjectable from "../shell-env-modifier/modify-terminal-shell-env.injectable"; +import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable"; +import isMacInjectable from "../../../common/vars/is-mac.injectable"; +import type { Cluster } from "../../../common/cluster/cluster"; +import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import userStoreInjectable from "../../../common/user-store/user-store.injectable"; +import type WebSocket from "ws"; + +export interface OpenLocalShellSessionArgs { + websocket: WebSocket; + cluster: Cluster; + tabId: string; +} + +export type OpenLocalShellSession = (args: OpenLocalShellSessionArgs) => Promise; + +const openLocalShellSessionInjectable = getInjectable({ + id: "open-local-shell-session", + + instantiate: (di): OpenLocalShellSession => { + const createKubectl = di.inject(createKubectlInjectable); + const dependencies: LocalShellSessionDependencies = { + directoryForBinaries: di.inject(directoryForBinariesInjectable), + isMac: di.inject(isMacInjectable), + modifyTerminalShellEnv: di.inject(modifyTerminalShellEnvInjectable), + isWindows: di.inject(isWindowsInjectable), + logger: di.inject(loggerInjectable), + userStore: di.inject(userStoreInjectable), + }; + + return (args) => { + const kubectl = createKubectl(args.cluster.version); + const session = new LocalShellSession(dependencies, { kubectl, ...args }); + + return session.open(); + }; + }, +}); + +export default openLocalShellSessionInjectable; diff --git a/src/main/shell-session/node-shell-session/node-shell-session.injectable.ts b/src/main/shell-session/node-shell-session/node-shell-session.injectable.ts deleted file mode 100644 index e2514708e6..0000000000 --- a/src/main/shell-session/node-shell-session/node-shell-session.injectable.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import type { Cluster } from "../../../common/cluster/cluster"; -import type WebSocket from "ws"; -import createKubectlInjectable from "../../kubectl/create-kubectl.injectable"; -import { NodeShellSession } from "./node-shell-session"; - -interface InstantiationParameter { - webSocket: WebSocket; - cluster: Cluster; - tabId: string; - nodeName: string; -} - -const nodeShellSessionInjectable = getInjectable({ - id: "node-shell-session", - - instantiate: (di, { cluster, tabId, webSocket, nodeName }: InstantiationParameter) => { - const createKubectl = di.inject(createKubectlInjectable); - - const kubectl = createKubectl(cluster.version); - - return new NodeShellSession(nodeName, kubectl, webSocket, cluster, tabId); - }, - - lifecycle: lifecycleEnum.transient, -}); - -export default nodeShellSessionInjectable; diff --git a/src/main/shell-session/node-shell-session/node-shell-session.ts b/src/main/shell-session/node-shell-session/node-shell-session.ts index 4c619a612c..7a2ab09cd7 100644 --- a/src/main/shell-session/node-shell-session/node-shell-session.ts +++ b/src/main/shell-session/node-shell-session/node-shell-session.ts @@ -3,28 +3,30 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type WebSocket from "ws"; import { v4 as uuid } from "uuid"; import { Watch, CoreV1Api } from "@kubernetes/client-node"; import type { KubeConfig } from "@kubernetes/client-node"; -import type { Cluster } from "../../../common/cluster/cluster"; +import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session"; import { ShellOpenError, ShellSession } from "../shell-session"; import { get, once } from "lodash"; import { Node, NodeApi } from "../../../common/k8s-api/endpoints"; import { KubeJsonApi } from "../../../common/k8s-api/kube-json-api"; -import logger from "../../logger"; -import type { Kubectl } from "../../kubectl/kubectl"; import { TerminalChannels } from "../../../common/terminal/channels"; +export interface NodeShellSessionArgs extends ShellSessionArgs { + nodeName: string; +} + export class NodeShellSession extends ShellSession { ShellType = "node-shell"; protected readonly podName = `node-shell-${uuid()}`; - + protected readonly nodeName: string; protected readonly cwd: string | undefined = undefined; - constructor(protected nodeName: string, kubectl: Kubectl, socket: WebSocket, cluster: Cluster, terminalId: string) { - super(kubectl, socket, cluster, terminalId); + constructor(dependencies: ShellSessionDependencies, { nodeName, ...args }: NodeShellSessionArgs) { + super(dependencies, args); + this.nodeName = nodeName; } public async open() { @@ -35,7 +37,7 @@ export class NodeShellSession extends ShellSession { const cleanup = once(() => { coreApi .deleteNamespacedPod(this.podName, "kube-system") - .catch(error => logger.warn(`[NODE-SHELL]: failed to remove pod shell`, error)); + .catch(error => this.dependencies.logger.warn(`[NODE-SHELL]: failed to remove pod shell`, error)); }); this.websocket.once("close", cleanup); @@ -75,7 +77,7 @@ export class NodeShellSession extends ShellSession { switch (nodeOs) { default: - logger.warn(`[NODE-SHELL-SESSION]: could not determine node OS, falling back with assumption of linux`); + this.dependencies.logger.warn(`[NODE-SHELL-SESSION]: could not determine node OS, falling back with assumption of linux`); // fallthrough case "linux": args.push("sh", "-c", "((clear && bash) || (clear && ash) || (clear && sh))"); @@ -127,7 +129,7 @@ export class NodeShellSession extends ShellSession { } protected waitForRunningPod(kc: KubeConfig): Promise { - logger.debug(`[NODE-SHELL]: waiting for ${this.podName} to be running`); + this.dependencies.logger.debug(`[NODE-SHELL]: waiting for ${this.podName} to be running`); return new Promise((resolve, reject) => { new Watch(kc) @@ -146,19 +148,19 @@ export class NodeShellSession extends ShellSession { }, // done callback is called if the watch terminates normally (err) => { - logger.error(`[NODE-SHELL]: ${this.podName} was not created in time`); + this.dependencies.logger.error(`[NODE-SHELL]: ${this.podName} was not created in time`); reject(err); }, ) .then(req => { setTimeout(() => { - logger.error(`[NODE-SHELL]: aborting wait for ${this.podName}, timing out`); + this.dependencies.logger.error(`[NODE-SHELL]: aborting wait for ${this.podName}, timing out`); req.abort(); reject("Pod creation timed out"); }, 2 * 60 * 1000); // 2 * 60 * 1000 }) .catch(error => { - logger.error(`[NODE-SHELL]: waiting for ${this.podName} failed: ${error}`); + this.dependencies.logger.error(`[NODE-SHELL]: waiting for ${this.podName} failed: ${error}`); reject(error); }); }); diff --git a/src/main/shell-session/node-shell-session/open.injectable.ts b/src/main/shell-session/node-shell-session/open.injectable.ts new file mode 100644 index 0000000000..bc9224eb8f --- /dev/null +++ b/src/main/shell-session/node-shell-session/open.injectable.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Cluster } from "../../../common/cluster/cluster"; +import type WebSocket from "ws"; +import createKubectlInjectable from "../../kubectl/create-kubectl.injectable"; +import { NodeShellSession } from "./node-shell-session"; +import type { ShellSessionDependencies } from "../shell-session"; +import isMacInjectable from "../../../common/vars/is-mac.injectable"; +import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; + +export interface NodeShellSessionArgs { + websocket: WebSocket; + cluster: Cluster; + tabId: string; + nodeName: string; +} + +export type OpenNodeShellSession = (args: NodeShellSessionArgs) => Promise; + +const openNodeShellSessionInjectable = getInjectable({ + id: "open-node-shell-session", + instantiate: (di): OpenNodeShellSession => { + const createKubectl = di.inject(createKubectlInjectable); + const dependencies: ShellSessionDependencies = { + isMac: di.inject(isMacInjectable), + isWindows: di.inject(isWindowsInjectable), + logger: di.inject(loggerInjectable), + }; + + return (args) => { + const kubectl = createKubectl(args.cluster.version); + const session = new NodeShellSession(dependencies, { kubectl, ...args }); + + return session.open(); + }; + }, +}); + +export default openNodeShellSessionInjectable; diff --git a/src/main/shell-session/shell-env-modifier/modify-terminal-shell-env.injectable.ts b/src/main/shell-session/shell-env-modifier/modify-terminal-shell-env.injectable.ts new file mode 100644 index 0000000000..559dbcf645 --- /dev/null +++ b/src/main/shell-session/shell-env-modifier/modify-terminal-shell-env.injectable.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import type { ClusterId } from "../../../common/cluster-types"; +import { isDefined } from "../../../common/utils"; +import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; +import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; + +export type ModifyTerminalShellEnv = (clusterId: ClusterId, env: Partial>) => Partial>; + +const modifyTerminalShellEnvInjectable = getInjectable({ + id: "terminal-shell-env-modify", + + instantiate: (di): ModifyTerminalShellEnv => { + const extensions = di.inject(mainExtensionsInjectable); + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const terminalShellEnvModifiers = computed(() => ( + extensions.get() + .map((extension) => extension.terminalShellEnvModifier) + .filter(isDefined) + )); + + return (clusterId, env) => { + const modifiers = terminalShellEnvModifiers.get(); + + if (modifiers.length === 0) { + return env; + } + + const entity = catalogEntityRegistry.findById(clusterId); + + if (entity) { + const ctx = { catalogEntity: entity }; + + // clone it so the passed value is not also modified + env = JSON.parse(JSON.stringify(env)); + env = modifiers.reduce((env, modifier) => modifier(ctx, env), env); + } + + return env; + }; + }, +}); + +export default modifyTerminalShellEnvInjectable; diff --git a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts deleted file mode 100644 index bf6a290052..0000000000 --- a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IComputedValue } from "mobx"; -import { computed } from "mobx"; -import type { ClusterId } from "../../../common/cluster-types"; -import { isDefined } from "../../../common/utils"; -import type { LensMainExtension } from "../../../extensions/lens-main-extension"; -import type { CatalogEntityRegistry } from "../../catalog"; - -interface Dependencies { - extensions: IComputedValue; - catalogEntityRegistry: CatalogEntityRegistry; -} - -export const terminalShellEnvModify = ({ extensions, catalogEntityRegistry }: Dependencies) => - (clusterId: ClusterId, env: Record) => { - const terminalShellEnvModifiers = computed(() => ( - extensions.get() - .map((extension) => extension.terminalShellEnvModifier) - .filter(isDefined) - )) - .get(); - - if (terminalShellEnvModifiers.length === 0) { - return env; - } - - const entity = catalogEntityRegistry.findById(clusterId); - - if (entity) { - const ctx = { catalogEntity: entity }; - - // clone it so the passed value is not also modified - env = JSON.parse(JSON.stringify(env)); - env = terminalShellEnvModifiers.reduce((env, modifier) => modifier(ctx, env), env); - } - - return env; - }; diff --git a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts deleted file mode 100644 index 7d8cb37a1c..0000000000 --- a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.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 mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; -import { terminalShellEnvModify } from "./terminal-shell-env-modifiers"; -import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; - -const terminalShellEnvModifyInjectable = getInjectable({ - id: "terminal-shell-env-modify", - - instantiate: (di) => - terminalShellEnvModify({ - extensions: di.inject(mainExtensionsInjectable), - catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), - }), -}); - -export default terminalShellEnvModifyInjectable; diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index 42286ee24d..98d1303d83 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -11,14 +11,13 @@ import { app } from "electron"; import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars"; import path from "path"; import os from "os"; -import { isMac, isWindows } from "../../common/vars"; import { UserStore } from "../../common/user-store"; import * as pty from "node-pty"; import { appEventBus } from "../../common/app-event-bus/event-bus"; -import logger from "../logger"; import { stat } from "fs/promises"; import { getOrInsertWith } from "../../common/utils"; import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels"; +import type { Logger } from "../../common/logger"; export class ShellOpenError extends Error { constructor(message: string, options?: ErrorOptions) { @@ -104,6 +103,19 @@ export enum WebSocketCloseEvent { TlsHandshake = 1015, } +export interface ShellSessionDependencies { + readonly isWindows: boolean; + readonly isMac: boolean; + readonly logger: Logger; +} + +export interface ShellSessionArgs { + kubectl: Kubectl; + websocket: WebSocket; + cluster: Cluster; + tabId: string; +} + export abstract class ShellSession { abstract readonly ShellType: string; @@ -130,6 +142,9 @@ export abstract class ShellSession { protected readonly kubectlBinDirP: Promise; protected readonly kubeconfigPathP: Promise; protected readonly terminalId: string; + protected readonly kubectl: Kubectl; + protected readonly websocket: WebSocket; + protected readonly cluster: Cluster; protected abstract get cwd(): string | undefined; @@ -147,12 +162,15 @@ export abstract class ShellSession { }) )); - logger.info(`[SHELL-SESSION]: PTY for ${this.terminalId} is ${resume ? "resumed" : "started"} with PID=${shellProcess.pid}`); + this.dependencies.logger.info(`[SHELL-SESSION]: PTY for ${this.terminalId} is ${resume ? "resumed" : "started"} with PID=${shellProcess.pid}`); return { shellProcess, resume }; } - constructor(protected readonly kubectl: Kubectl, protected readonly websocket: WebSocket, protected readonly cluster: Cluster, terminalId: string) { + constructor(protected readonly dependencies: ShellSessionDependencies, { kubectl, websocket, cluster, tabId: terminalId }: ShellSessionArgs) { + this.kubectl = kubectl; + this.websocket = websocket; + this.cluster = cluster; this.kubeconfigPathP = this.cluster.getProxyKubeconfigPath(); this.kubectlBinDirP = this.kubectl.binDir(); this.terminalId = `${cluster.id}:${terminalId}`; @@ -165,7 +183,7 @@ export abstract class ShellSession { protected async getCwd(env: Record): Promise { const cwdOptions = [this.cwd]; - if (isWindows) { + if (this.dependencies.isWindows) { cwdOptions.push( env.USERPROFILE, os.homedir(), @@ -177,7 +195,7 @@ export abstract class ShellSession { os.homedir(), ); - if (isMac) { + if (this.dependencies.isMac) { cwdOptions.push("/Users"); } else { cwdOptions.push("/home"); @@ -214,7 +232,7 @@ export abstract class ShellSession { this.running = true; shellProcess.onData(data => this.send({ type: TerminalChannels.STDOUT, data })); shellProcess.onExit(({ exitCode }) => { - logger.info(`[SHELL-SESSION]: shell has exited for ${this.terminalId} closed with exitcode=${exitCode}`); + this.dependencies.logger.info(`[SHELL-SESSION]: shell has exited for ${this.terminalId} closed with exitcode=${exitCode}`); // This might already be false because of the kill() within the websocket.on("close") handler if (this.running) { @@ -232,11 +250,11 @@ export abstract class ShellSession { this.websocket .on("message", (rawData: unknown): void => { if (!this.running) { - return void logger.debug(`[SHELL-SESSION]: received message from ${this.terminalId}, but shellProcess isn't running`); + return void this.dependencies.logger.debug(`[SHELL-SESSION]: received message from ${this.terminalId}, but shellProcess isn't running`); } if (!(rawData instanceof Buffer)) { - return void logger.error(`[SHELL-SESSION]: Received message non-buffer message.`, { rawData }); + return void this.dependencies.logger.error(`[SHELL-SESSION]: Received message non-buffer message.`, { rawData }); } const data = rawData.toString(); @@ -252,18 +270,18 @@ export abstract class ShellSession { shellProcess.resize(message.data.width, message.data.height); break; case TerminalChannels.PING: - logger.silly(`[SHELL-SESSION]: ${this.terminalId} ping!`); + this.dependencies.logger.silly(`[SHELL-SESSION]: ${this.terminalId} ping!`); break; default: - logger.warn(`[SHELL-SESSION]: unknown or unhandleable message type for ${this.terminalId}`, message); + this.dependencies.logger.warn(`[SHELL-SESSION]: unknown or unhandleable message type for ${this.terminalId}`, message); break; } } catch (error) { - logger.error(`[SHELL-SESSION]: failed to handle message for ${this.terminalId}`, error); + this.dependencies.logger.error(`[SHELL-SESSION]: failed to handle message for ${this.terminalId}`, error); } }) .once("close", code => { - logger.info(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${WebSocketCloseEvent[code]}(${code})`, { cluster: this.cluster.getMeta() }); + this.dependencies.logger.info(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${WebSocketCloseEvent[code]}(${code})`, { cluster: this.cluster.getMeta() }); const stopShellSession = this.running && ( @@ -278,11 +296,11 @@ export abstract class ShellSession { this.running = false; try { - logger.info(`[SHELL-SESSION]: Killing shell process (pid=${shellProcess.pid}) for ${this.terminalId}`); + this.dependencies.logger.info(`[SHELL-SESSION]: Killing shell process (pid=${shellProcess.pid}) for ${this.terminalId}`); shellProcess.kill(); ShellSession.processes.delete(this.terminalId); } catch (error) { - logger.warn(`[SHELL-SESSION]: failed to kill shell process (pid=${shellProcess.pid}) for ${this.terminalId}`, error); + this.dependencies.logger.warn(`[SHELL-SESSION]: failed to kill shell process (pid=${shellProcess.pid}) for ${this.terminalId}`, error); } } }); @@ -319,7 +337,7 @@ export abstract class ShellSession { delete env.DEBUG; // don't pass DEBUG into shells - if (isWindows) { + if (this.dependencies.isWindows) { env.SystemRoot = process.env.SystemRoot; env.PTYSHELL = shell || "powershell.exe"; env.PATH = pathStr;