1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Make shell sessions even more injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-07-06 11:04:52 -04:00
parent 28227c923d
commit a733641f62
10 changed files with 113 additions and 16 deletions

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import userStoreInjectable from "./user-store.injectable";
const downloadKubectlBinariesInjectable = getInjectable({
id: "download-kubectl-binaries",
instantiate: (di) => {
const store = di.inject(userStoreInjectable);
return computed(() => store.downloadKubectlBinaries);
},
});
export default downloadKubectlBinariesInjectable;

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import userStoreInjectable from "./user-store.injectable";
const kubectlBinariesPathInjectable = getInjectable({
id: "kubectl-binaries-path",
instantiate: (di) => {
const store = di.inject(userStoreInjectable);
return computed(() => store.kubectlBinariesPath);
},
});
export default kubectlBinariesPathInjectable;

View File

@ -7,14 +7,14 @@ import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars";
describe("clearKubeconfigEnvVars tests", () => { describe("clearKubeconfigEnvVars tests", () => {
it("should not touch non kubeconfig keys", () => { it("should not touch non kubeconfig keys", () => {
expect(clearKubeconfigEnvVars({ a: 1 })).toStrictEqual({ a: 1 }); expect(clearKubeconfigEnvVars({ a: "1" })).toStrictEqual({ a: 1 });
}); });
it("should remove a single kubeconfig key", () => { it("should remove a single kubeconfig key", () => {
expect(clearKubeconfigEnvVars({ a: 1, kubeconfig: "1" })).toStrictEqual({ a: 1 }); expect(clearKubeconfigEnvVars({ a: "1", kubeconfig: "1" })).toStrictEqual({ a: 1 });
}); });
it("should remove a two kubeconfig key", () => { it("should remove a two kubeconfig key", () => {
expect(clearKubeconfigEnvVars({ a: 1, kubeconfig: "1", kUbeconfig: "1" })).toStrictEqual({ a: 1 }); expect(clearKubeconfigEnvVars({ a: "1", kubeconfig: "1", kUbeconfig: "1" })).toStrictEqual({ a: 1 });
}); });
}); });

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { IComputedValue } from "mobx";
import path from "path"; import path from "path";
import { UserStore } from "../../../common/user-store";
import type { TerminalShellEnvModify } from "../shell-env-modifier/terminal-shell-env-modify.injectable"; import type { TerminalShellEnvModify } from "../shell-env-modifier/terminal-shell-env-modify.injectable";
import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session"; import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session";
import { ShellSession } from "../shell-session"; import { ShellSession } from "../shell-session";
@ -12,6 +12,8 @@ import { ShellSession } from "../shell-session";
export interface LocalShellSessionDependencies extends ShellSessionDependencies { export interface LocalShellSessionDependencies extends ShellSessionDependencies {
terminalShellEnvModify: TerminalShellEnvModify; terminalShellEnvModify: TerminalShellEnvModify;
readonly baseBundeledBinariesDirectory: string; readonly baseBundeledBinariesDirectory: string;
readonly kubectlBinariesPath: IComputedValue<string | undefined>;
readonly downloadKubectlBinaries: IComputedValue<boolean>;
} }
export class LocalShellSession extends ShellSession { export class LocalShellSession extends ShellSession {
@ -30,11 +32,8 @@ export class LocalShellSession extends ShellSession {
} }
public async open() { public async open() {
let env = await this.getCachedShellEnv(); const cachedEnv = await this.getCachedShellEnv();
const env = this.dependencies.terminalShellEnvModify(this.cluster.id, cachedEnv);
// extensions can modify the env
env = this.dependencies.terminalShellEnvModify(this.cluster.id, env);
const shell = env.PTYSHELL; const shell = env.PTYSHELL;
if (!shell) { if (!shell) {
@ -47,8 +46,8 @@ export class LocalShellSession extends ShellSession {
} }
protected async getShellArgs(shell: string): Promise<string[]> { protected async getShellArgs(shell: string): Promise<string[]> {
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath(); const pathFromPreferences = this.dependencies.kubectlBinariesPath.get() || this.kubectl.getBundledPath();
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences); const kubectlPathDir = this.dependencies.downloadKubectlBinaries.get() ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
switch(path.basename(shell)) { switch(path.basename(shell)) {
case "powershell.exe": case "powershell.exe":

View File

@ -10,6 +10,12 @@ import type WebSocket from "ws";
import terminalShellEnvModifyInjectable from "../shell-env-modifier/terminal-shell-env-modify.injectable"; import terminalShellEnvModifyInjectable from "../shell-env-modifier/terminal-shell-env-modify.injectable";
import baseBundeledBinariesDirectoryInjectable from "../../../common/vars/base-bundled-binaries-dir.injectable"; import baseBundeledBinariesDirectoryInjectable from "../../../common/vars/base-bundled-binaries-dir.injectable";
import type { Kubectl } from "../../kubectl/kubectl"; import type { Kubectl } from "../../kubectl/kubectl";
import kubectlBinariesPathInjectable from "../../../common/user-store/kubectl-binaries-path.injectable";
import downloadKubectlBinariesInjectable from "../../../common/user-store/download-kubectl-binaries.injectable";
import ensureShellProcessInjectable from "../ensure-shell-process.injectable";
import getCachedShellEnvInjectable from "../get-cached-shell-env.injectable";
import getValidCwdInjectable from "../get-valid-cwd.injectable";
import loggerInjectable from "../../../common/logger.injectable";
export interface OpenLocalShellSessionArgs { export interface OpenLocalShellSessionArgs {
websocket: WebSocket; websocket: WebSocket;
@ -26,6 +32,12 @@ const openLocalShellSessionInjectable = getInjectable({
const deps: LocalShellSessionDependencies = { const deps: LocalShellSessionDependencies = {
terminalShellEnvModify: di.inject(terminalShellEnvModifyInjectable), terminalShellEnvModify: di.inject(terminalShellEnvModifyInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundeledBinariesDirectoryInjectable), baseBundeledBinariesDirectory: di.inject(baseBundeledBinariesDirectoryInjectable),
kubectlBinariesPath: di.inject(kubectlBinariesPathInjectable),
downloadKubectlBinaries: di.inject(downloadKubectlBinariesInjectable),
ensureShellProcess: di.inject(ensureShellProcessInjectable),
getCachedShellEnv: di.inject(getCachedShellEnvInjectable),
getValidCwd: di.inject(getValidCwdInjectable),
logger: di.inject(loggerInjectable),
}; };
return (args) => new LocalShellSession(deps, args).open(); return (args) => new LocalShellSession(deps, args).open();

View File

@ -5,8 +5,13 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { Cluster } from "../../../common/cluster/cluster"; import type { Cluster } from "../../../common/cluster/cluster";
import type WebSocket from "ws"; import type WebSocket from "ws";
import type { NodeShellSessionDependencies } from "./node-shell-session";
import { NodeShellSession } from "./node-shell-session"; import { NodeShellSession } from "./node-shell-session";
import type { Kubectl } from "../../kubectl/kubectl"; import type { Kubectl } from "../../kubectl/kubectl";
import loggerInjectable from "../../../common/logger.injectable";
import ensureShellProcessInjectable from "../ensure-shell-process.injectable";
import getCachedShellEnvInjectable from "../get-cached-shell-env.injectable";
import getValidCwdInjectable from "../get-valid-cwd.injectable";
export interface OpenNodeShellSessionArgs { export interface OpenNodeShellSessionArgs {
websocket: WebSocket; websocket: WebSocket;
@ -21,7 +26,16 @@ export type OpenNodeShellSession = (args: OpenNodeShellSessionArgs) => Promise<v
const openNodeShellSessionInjectable = getInjectable({ const openNodeShellSessionInjectable = getInjectable({
id: "node-shell-session", id: "node-shell-session",
instantiate: (): OpenNodeShellSession => (args) => new NodeShellSession(args).open(), instantiate: (di): OpenNodeShellSession => {
const dependencies: NodeShellSessionDependencies = {
ensureShellProcess: di.inject(ensureShellProcessInjectable),
getCachedShellEnv: di.inject(getCachedShellEnvInjectable),
getValidCwd: di.inject(getValidCwdInjectable),
logger: di.inject(loggerInjectable),
};
return (args) => new NodeShellSession(dependencies, args).open();
},
causesSideEffects: true, causesSideEffects: true,
}); });

View File

@ -5,9 +5,11 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { spawn } from "node-pty"; import { spawn } from "node-pty";
export type SpawnPty = typeof spawn;
const spawnPtyInjectable = getInjectable({ const spawnPtyInjectable = getInjectable({
id: "spawn-pty", id: "spawn-pty",
instantiate: () => spawn, instantiate: (): SpawnPty => spawn,
causesSideEffects: true, causesSideEffects: true,
}); });

View File

@ -12,6 +12,7 @@ import terminalConfigInjectable from "../../../../common/user-store/terminal-con
import terminalCopyOnSelectInjectable from "../../../../common/user-store/terminal-copy-on-select.injectable"; import terminalCopyOnSelectInjectable from "../../../../common/user-store/terminal-copy-on-select.injectable";
import themeStoreInjectable from "../../../themes/store.injectable"; import themeStoreInjectable from "../../../themes/store.injectable";
import allowTerminalTransparencyInjectable from "./allow-transparency.injectable"; import allowTerminalTransparencyInjectable from "./allow-transparency.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal; export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal;
@ -24,6 +25,7 @@ const createTerminalInjectable = getInjectable({
terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable), terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable),
themeStore: di.inject(themeStoreInjectable), themeStore: di.inject(themeStoreInjectable),
allowTransparency: di.inject(allowTerminalTransparencyInjectable), allowTransparency: di.inject(allowTerminalTransparencyInjectable),
logger: di.inject(loggerInjectable),
}; };
return (tabId, api) => new Terminal(dependencies, { tabId, api }); return (tabId, api) => new Terminal(dependencies, { tabId, api });

View File

@ -15,7 +15,7 @@ import { disposer } from "../../../utils";
import { isMac } from "../../../../common/vars"; import { isMac } from "../../../../common/vars";
import { once } from "lodash"; import { once } from "lodash";
import { clipboard } from "electron"; 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 type { TerminalConfig } from "../../../../common/user-store/preferences-helpers";
import assert from "assert"; import assert from "assert";
import { TerminalChannels } from "../../../../common/terminal/channels"; import { TerminalChannels } from "../../../../common/terminal/channels";
@ -26,6 +26,7 @@ export interface TerminalDependencies {
readonly terminalCopyOnSelect: IComputedValue<boolean>; readonly terminalCopyOnSelect: IComputedValue<boolean>;
readonly themeStore: ThemeStore; readonly themeStore: ThemeStore;
readonly allowTransparency: boolean; readonly allowTransparency: boolean;
readonly logger: Logger;
} }
export interface TerminalArguments { export interface TerminalArguments {
@ -196,14 +197,14 @@ export class Terminal {
}; };
setFontSize = (fontSize: number) => { 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.xterm.options.fontSize = fontSize;
this.fit(); this.fit();
}; };
setFontFamily = (fontFamily: string) => { 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.xterm.options.fontFamily = fontFamily;
this.fit(); this.fit();

View File

@ -4,18 +4,44 @@
*/ */
import type { RenderResult } from "@testing-library/react"; import type { RenderResult } from "@testing-library/react";
import { waitFor } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import getShellAuthTokenChannelHandlerInjectable from "../../main/lens-proxy/proxy-functions/shell/auth-token-channel-handler.injectable"; import getShellAuthTokenChannelHandlerInjectable from "../../main/lens-proxy/proxy-functions/shell/auth-token-channel-handler.injectable";
import type { GetShellAuthToken } from "../../common/shell-authentication/get-auth-token.injectable"; import type { GetShellAuthToken } from "../../common/shell-authentication/get-auth-token.injectable";
import type { SpawnPty } from "../../main/shell-session/spawn-pty.injectable";
import spawnPtyInjectable from "../../main/shell-session/spawn-pty.injectable";
import type { IPty } from "node-pty";
describe("local shell session techincal tests", () => { describe("local shell session techincal tests", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
let result: RenderResult; let result: RenderResult;
let authenticationSpy: jest.SpyInstance<Uint8Array | Promise<Uint8Array>, Parameters<GetShellAuthToken>>; let authenticationSpy: jest.SpyInstance<Uint8Array | Promise<Uint8Array>, Parameters<GetShellAuthToken>>;
let spawnPtyMock: jest.MockedFunction<SpawnPty>;
let ptyMock: jest.MockedObject<IPty>;
beforeEach(async () => { beforeEach(async () => {
builder = getApplicationBuilder() builder = getApplicationBuilder()
.beforeApplicationStart(() => {
spawnPtyMock = jest.fn();
builder.dis.mainDi.override(spawnPtyInjectable, () => spawnPtyMock);
spawnPtyMock.mockImplementation(() => ptyMock = {
cols: 80,
rows: 40,
pid: 12346,
process: "my-mocked-shell",
handleFlowControl: true,
kill: jest.fn(),
onData: jest.fn(),
onExit: jest.fn(),
on: jest.fn(),
resize: jest.fn(),
write: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
});
})
.beforeRender(() => { .beforeRender(() => {
const shellAuthentication = builder.dis.mainDi.inject(getShellAuthTokenChannelHandlerInjectable); const shellAuthentication = builder.dis.mainDi.inject(getShellAuthTokenChannelHandlerInjectable);
@ -34,4 +60,9 @@ describe("local shell session techincal tests", () => {
it("should call the authentication function", () => { it("should call the authentication function", () => {
expect(authenticationSpy).toBeCalled(); expect(authenticationSpy).toBeCalled();
}); });
it("should create a pty instance", async () => {
await waitFor(() => expect(spawnPtyMock).toBeCalled());
void ptyMock;
});
}); });