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", () => {
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", () => {
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", () => {
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.
*/
import type { IComputedValue } from "mobx";
import path from "path";
import { UserStore } from "../../../common/user-store";
import type { TerminalShellEnvModify } from "../shell-env-modifier/terminal-shell-env-modify.injectable";
import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session";
import { ShellSession } from "../shell-session";
@ -12,6 +12,8 @@ import { ShellSession } from "../shell-session";
export interface LocalShellSessionDependencies extends ShellSessionDependencies {
terminalShellEnvModify: TerminalShellEnvModify;
readonly baseBundeledBinariesDirectory: string;
readonly kubectlBinariesPath: IComputedValue<string | undefined>;
readonly downloadKubectlBinaries: IComputedValue<boolean>;
}
export class LocalShellSession extends ShellSession {
@ -30,11 +32,8 @@ export class LocalShellSession extends ShellSession {
}
public async open() {
let env = await this.getCachedShellEnv();
// extensions can modify the env
env = this.dependencies.terminalShellEnvModify(this.cluster.id, env);
const cachedEnv = await this.getCachedShellEnv();
const env = this.dependencies.terminalShellEnvModify(this.cluster.id, cachedEnv);
const shell = env.PTYSHELL;
if (!shell) {
@ -47,8 +46,8 @@ export class LocalShellSession extends ShellSession {
}
protected async getShellArgs(shell: string): Promise<string[]> {
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath();
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
const pathFromPreferences = this.dependencies.kubectlBinariesPath.get() || this.kubectl.getBundledPath();
const kubectlPathDir = this.dependencies.downloadKubectlBinaries.get() ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
switch(path.basename(shell)) {
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 baseBundeledBinariesDirectoryInjectable from "../../../common/vars/base-bundled-binaries-dir.injectable";
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 {
websocket: WebSocket;
@ -26,6 +32,12 @@ const openLocalShellSessionInjectable = getInjectable({
const deps: LocalShellSessionDependencies = {
terminalShellEnvModify: di.inject(terminalShellEnvModifyInjectable),
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();

View File

@ -5,8 +5,13 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { Cluster } from "../../../common/cluster/cluster";
import type WebSocket from "ws";
import type { NodeShellSessionDependencies } from "./node-shell-session";
import { NodeShellSession } from "./node-shell-session";
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 {
websocket: WebSocket;
@ -21,7 +26,16 @@ export type OpenNodeShellSession = (args: OpenNodeShellSessionArgs) => Promise<v
const openNodeShellSessionInjectable = getInjectable({
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,
});

View File

@ -5,9 +5,11 @@
import { getInjectable } from "@ogre-tools/injectable";
import { spawn } from "node-pty";
export type SpawnPty = typeof spawn;
const spawnPtyInjectable = getInjectable({
id: "spawn-pty",
instantiate: () => spawn,
instantiate: (): SpawnPty => spawn,
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 themeStoreInjectable from "../../../themes/store.injectable";
import allowTerminalTransparencyInjectable from "./allow-transparency.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal;
@ -24,6 +25,7 @@ const createTerminalInjectable = getInjectable({
terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable),
themeStore: di.inject(themeStoreInjectable),
allowTransparency: di.inject(allowTerminalTransparencyInjectable),
logger: di.inject(loggerInjectable),
};
return (tabId, api) => new Terminal(dependencies, { tabId, api });

View File

@ -15,7 +15,7 @@ import { disposer } from "../../../utils";
import { isMac } from "../../../../common/vars";
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<boolean>;
readonly themeStore: ThemeStore;
readonly allowTransparency: boolean;
readonly logger: Logger;
}
export interface TerminalArguments {
@ -196,14 +197,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();

View File

@ -4,18 +4,44 @@
*/
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 { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
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 { 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", () => {
let builder: ApplicationBuilder;
let result: RenderResult;
let authenticationSpy: jest.SpyInstance<Uint8Array | Promise<Uint8Array>, Parameters<GetShellAuthToken>>;
let spawnPtyMock: jest.MockedFunction<SpawnPty>;
let ptyMock: jest.MockedObject<IPty>;
beforeEach(async () => {
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(() => {
const shellAuthentication = builder.dis.mainDi.inject(getShellAuthTokenChannelHandlerInjectable);
@ -34,4 +60,9 @@ describe("local shell session techincal tests", () => {
it("should call the authentication function", () => {
expect(authenticationSpy).toBeCalled();
});
it("should create a pty instance", async () => {
await waitFor(() => expect(spawnPtyMock).toBeCalled());
void ptyMock;
});
});