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

Fix failing to start shell being hidden from user

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-06-14 12:20:10 -04:00
parent 8f9dd11420
commit ed6f5226bd
5 changed files with 54 additions and 17 deletions

View File

@ -10,6 +10,7 @@ export enum TerminalChannels {
CONNECTED = "connected", CONNECTED = "connected",
RESIZE = "resize", RESIZE = "resize",
PING = "ping", PING = "ping",
ERROR = "error",
} }
export type TerminalMessage = { export type TerminalMessage = {
@ -28,4 +29,7 @@ export type TerminalMessage = {
}; };
} | { } | {
type: TerminalChannels.PING; type: TerminalChannels.PING;
} | {
type: TerminalChannels.ERROR;
data: string;
}; };

View File

@ -10,6 +10,7 @@ import type { ModifyTerminalShellEnv } from "../shell-env-modifier/modify-termin
import type { JoinPaths } from "../../../common/path/join-paths.injectable"; import type { JoinPaths } from "../../../common/path/join-paths.injectable";
import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable"; import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable";
import type { GetBasenameOfPath } from "../../../common/path/get-basename.injectable"; import type { GetBasenameOfPath } from "../../../common/path/get-basename.injectable";
import { TerminalChannels } from "../../../common/terminal/channels";
export interface LocalShellSessionDependencies extends ShellSessionDependencies { export interface LocalShellSessionDependencies extends ShellSessionDependencies {
readonly directoryForBinaries: string; readonly directoryForBinaries: string;
@ -41,7 +42,13 @@ export class LocalShellSession extends ShellSession {
const shell = env.PTYSHELL; const shell = env.PTYSHELL;
if (!shell) { if (!shell) {
throw new Error("PTYSHELL is not defined with the environment"); this.send({
type: TerminalChannels.ERROR,
data: "PTYSHELL is not defined with the environment",
});
this.dependencies.logger.warn(`[LOCAL-SHELL-SESSION]: PTYSHELL env var is not defined for ${this.terminalId}`);
return;
} }
const args = await this.getShellArgs(shell); const args = await this.getShellArgs(shell);

View File

@ -156,23 +156,34 @@ export abstract class ShellSession {
protected abstract get cwd(): string | undefined; protected abstract get cwd(): string | undefined;
protected ensureShellProcess(shell: string, args: string[], env: Partial<Record<string, string>>, cwd: string): { shellProcess: pty.IPty; resume: boolean } { protected ensureShellProcess(shell: string, args: string[], env: Partial<Record<string, string>>, cwd: string): { shellProcess: pty.IPty; resume: boolean } | null {
const resume = ShellSession.processes.has(this.terminalId); try {
const shellProcess = getOrInsertWith(ShellSession.processes, this.terminalId, () => ( const resume = ShellSession.processes.has(this.terminalId);
this.dependencies.spawnPty(shell, args, {
rows: 30,
cols: 80,
cwd,
env,
name: "xterm-256color",
// TODO: Something else is broken here so we need to force the use of winPty on windows
useConpty: false,
})
));
this.dependencies.logger.info(`[SHELL-SESSION]: PTY for ${this.terminalId} is ${resume ? "resumed" : "started"} with PID=${shellProcess.pid}`); const shellProcess = getOrInsertWith(ShellSession.processes, this.terminalId, () => (
this.dependencies.spawnPty(shell, args, {
rows: 30,
cols: 80,
cwd,
env,
name: "xterm-256color",
// TODO: Something else is broken here so we need to force the use of winPty on windows
useConpty: false,
})
));
return { shellProcess, resume }; this.dependencies.logger.info(`[SHELL-SESSION]: PTY for ${this.terminalId} is ${resume ? "resumed" : "started"} with PID=${shellProcess.pid}`);
return { shellProcess, resume };
} catch (error) {
this.send({
type: TerminalChannels.ERROR,
data: `Failed to start shell (${shell}): ${error}`,
});
this.dependencies.logger.warn(`[SHELL-SESSION]: Failed to start PTY for ${this.terminalId}: ${error}`, { shell });
return null;
}
} }
constructor(protected readonly dependencies: ShellSessionDependencies, { kubectl, websocket, cluster, tabId: terminalId }: ShellSessionArgs) { constructor(protected readonly dependencies: ShellSessionDependencies, { kubectl, websocket, cluster, tabId: terminalId }: ShellSessionArgs) {
@ -231,7 +242,13 @@ export abstract class ShellSession {
protected async openShellProcess(shell: string, args: string[], env: Record<string, string | undefined>) { protected async openShellProcess(shell: string, args: string[], env: Record<string, string | undefined>) {
const cwd = await this.getCwd(env); const cwd = await this.getCwd(env);
const { shellProcess, resume } = this.ensureShellProcess(shell, args, env, cwd); const ensured = this.ensureShellProcess(shell, args, env, cwd);
if (!ensured) {
return;
}
const { shellProcess, resume } = ensured;
if (resume) { if (resume) {
this.send({ type: TerminalChannels.CONNECTED }); this.send({ type: TerminalChannels.CONNECTED });

View File

@ -34,6 +34,7 @@ export interface TerminalApiQuery extends Record<string, string | undefined> {
export interface TerminalEvents extends WebSocketEvents { export interface TerminalEvents extends WebSocketEvents {
ready: () => void; ready: () => void;
connected: () => void; connected: () => void;
error: (error: string) => void;
} }
export interface TerminalApiDependencies extends WebSocketApiDependencies { export interface TerminalApiDependencies extends WebSocketApiDependencies {
@ -145,6 +146,9 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
case TerminalChannels.CONNECTED: case TerminalChannels.CONNECTED:
this.emit("connected"); this.emit("connected");
break; break;
case TerminalChannels.ERROR:
this.emit("error", message.data);
break;
default: default:
this.dependencies.logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message); this.dependencies.logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message);
break; break;

View File

@ -104,6 +104,7 @@ export class Terminal {
this.api.once("ready", clearOnce); this.api.once("ready", clearOnce);
this.api.once("connected", clearOnce); this.api.once("connected", clearOnce);
this.api.on("data", this.onApiData); this.api.on("data", this.onApiData);
this.api.on("error", this.onApiError);
window.addEventListener("resize", this.onResize); window.addEventListener("resize", this.onResize);
const linkProvider = new LinkProvider( const linkProvider = new LinkProvider(
@ -152,6 +153,10 @@ export class Terminal {
this.xterm.write(data); this.xterm.write(data);
}; };
onApiError = (data: string) => {
this.xterm.writeln(data);
};
onData = (data: string) => { onData = (data: string) => {
if (!this.api.isReady) return; if (!this.api.isReady) return;
this.api.sendMessage({ this.api.sendMessage({