1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/src/main/shell-session.ts
Jari Kolehmainen 1d0815abd2
Lens app source code (#119)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
2020-03-15 09:52:02 +02:00

162 lines
4.7 KiB
TypeScript

import * as pty from "node-pty"
import * as WebSocket from "ws"
import { EventEmitter } from "events";
import * as path from "path"
import shellEnv = require("shell-env")
import { app } from "electron"
import { Kubectl } from "./kubectl"
import { tracker } from "./tracker"
import { Cluster, ClusterPreferences } from "./cluster"
import { helmCli } from "./helm-cli"
export class ShellSession extends EventEmitter {
protected websocket: WebSocket
protected shellProcess: pty.IPty
protected kubeconfigPath: string
protected nodeShellPod: string;
protected kubectl: Kubectl;
protected kubectlBinDir: string;
protected helmBinDir: string;
protected preferences: ClusterPreferences;
constructor(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster) {
super()
this.websocket = socket
this.kubeconfigPath = pathToKubeconfig
this.kubectl = new Kubectl(cluster.version)
this.preferences = cluster.preferences || {}
}
public async open() {
this.kubectlBinDir = await this.kubectl.binDir()
this.helmBinDir = helmCli.getBinaryDir()
await helmCli.binaryPath()
const env = this.getShellEnv()
const shell = env.PTYSHELL
const args = await this.getShellArgs(shell)
this.shellProcess = pty.spawn(shell, args, {
cols: 80,
cwd: this.cwd() || env.HOME,
env: env,
name: "xterm-256color",
rows: 30,
});
this.pipeStdout()
this.pipeStdin()
this.closeWebsocketOnProcessExit()
this.exitProcessOnWebsocketClose()
tracker.event("shell", "open")
}
protected cwd(): string {
if(!this.preferences || !this.preferences.terminalCWD || this.preferences.terminalCWD === "") {
return null
}
return this.preferences.terminalCWD
}
protected async getShellArgs(shell: string): Promise<Array<string>> {
switch(path.basename(shell)) {
case "powershell.exe":
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.kubectlBinDir};${this.helmBinDir};$Env:PATH"}`]
break
case "bash":
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]
break
case "fish":
return ["--login", "--init-command", `export PATH="${this.kubectlBinDir}:${this.helmBinDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]
break
case "zsh":
return ["--login"]
default:
return []
}
}
protected getShellEnv() {
const env = JSON.parse(JSON.stringify(shellEnv.sync()))
const pathStr = [this.kubectlBinDir, this.helmBinDir, process.env.PATH].join(path.delimiter)
if(process.platform === "win32") {
env["SystemRoot"] = process.env.SystemRoot
env["PTYSHELL"] = "powershell.exe"
env["PATH"] = pathStr
} else if(typeof(process.env.SHELL) != "undefined") {
env["PTYSHELL"] = process.env.SHELL
env["PATH"] = pathStr
} else {
env["PTYSHELL"] = "" // blank runs the system default shell
}
if(path.basename(env["PTYSHELL"]) === "zsh") {
env["OLD_ZDOTDIR"] = env.ZDOTDIR || env.HOME
env["ZDOTDIR"] = this.kubectlBinDir
}
env["PTYPID"] = process.pid.toString()
env["KUBECONFIG"] = this.kubeconfigPath
env["TERM_PROGRAM"] = app.getName()
env["TERM_PROGRAM_VERSION"] = app.getVersion()
if (this.preferences.httpsProxy) {
env["HTTPS_PROXY"] = this.preferences.httpsProxy
}
if (env.DEBUG) { // do not pass debug option to bash
delete env["DEBUG"]
}
return(env)
}
protected pipeStdout() {
// send shell output to websocket
this.shellProcess.on("data", ((data: string) => {
this.sendResponse(data)
}).bind(this));
}
protected pipeStdin() {
// write websocket messages to shellProcess
this.websocket.on("message", function(data: string) {
const message = Buffer.from(data.slice(1, data.length), "base64").toString()
switch (data[0]) {
case "0":
this.shellProcess.write(message)
break;
case "4":
const resizeMsgObj = JSON.parse(message)
this.shellProcess.resize(resizeMsgObj["Width"], resizeMsgObj["Height"])
break;
case "9":
this.token = message
this.emit('newToken', this.token)
break;
}
}.bind(this))
}
protected exit(code = 1000) {
this.websocket.close(code)
this.emit('exit')
}
protected closeWebsocketOnProcessExit() {
this.shellProcess.on("exit", (_code) => {
this.exit()
});
}
protected exitProcessOnWebsocketClose() {
this.websocket.on("close", () => {
if (this.shellProcess) {
this.shellProcess.kill();
}
})
}
protected sendResponse(msg: string) {
this.websocket.send("1" + Buffer.from(msg).toString("base64"))
}
}