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:
parent
28227c923d
commit
a733641f62
@ -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;
|
||||||
18
src/common/user-store/kubectl-binaries-path.injectable.ts
Normal file
18
src/common/user-store/kubectl-binaries-path.injectable.ts
Normal 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;
|
||||||
@ -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 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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":
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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 });
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user