diff --git a/extensions/node-menu/src/node-menu.tsx b/extensions/node-menu/src/node-menu.tsx index 12ded04899..c1a9e3caa1 100644 --- a/extensions/node-menu/src/node-menu.tsx +++ b/extensions/node-menu/src/node-menu.tsx @@ -20,7 +20,7 @@ */ import React from "react"; -import { Renderer } from "@k8slens/extensions"; +import { Common, Renderer } from "@k8slens/extensions"; type Node = Renderer.K8sApi.Node; @@ -34,6 +34,9 @@ const { }, Navigation } = Renderer; +const { + App, +} = Common; export interface NodeMenuProps extends Renderer.Component.KubeObjectMenuProps { @@ -42,8 +45,12 @@ export interface NodeMenuProps extends Renderer.Component.KubeObjectMenuProps { terminalStore.sendCommand(command, { @@ -62,15 +69,15 @@ export function NodeMenu(props: NodeMenuProps) { }; const cordon = () => { - sendToTerminal(`kubectl cordon ${nodeName}`); + sendToTerminal(`${kubectlPath} cordon ${nodeName}`); }; const unCordon = () => { - sendToTerminal(`kubectl uncordon ${nodeName}`); + sendToTerminal(`${kubectlPath} uncordon ${nodeName}`); }; const drain = () => { - const command = `kubectl drain ${nodeName} --delete-local-data --ignore-daemonsets --force`; + const command = `${kubectlPath} drain ${nodeName} --delete-local-data --ignore-daemonsets --force`; ConfirmDialog.open({ ok: () => sendToTerminal(command), diff --git a/extensions/pod-menu/src/attach-menu.tsx b/extensions/pod-menu/src/attach-menu.tsx index ad02782641..0c0d2539af 100644 --- a/extensions/pod-menu/src/attach-menu.tsx +++ b/extensions/pod-menu/src/attach-menu.tsx @@ -39,6 +39,7 @@ const { } = Renderer; const { Util, + App, } = Common; export interface PodAttachMenuProps extends Renderer.Component.KubeObjectMenuProps { @@ -47,22 +48,34 @@ export interface PodAttachMenuProps extends Renderer.Component.KubeObjectMenuPro export class PodAttachMenu extends React.Component { async attachToPod(container?: string) { const { object: pod } = this.props; - const containerParam = container ? `-c ${container}` : ""; - let command = `kubectl attach -i -t -n ${pod.getNs()} ${pod.getName()} ${containerParam}`; + + const kubectlPath = App.Preferences.getKubectlPath() || "kubectl"; + const commandParts = [ + kubectlPath, + "attach", + "-i", + "-t", + "-n", pod.getNs(), + pod.getName(), + ]; if (window.navigator.platform !== "Win32") { - command = `exec ${command}`; + commandParts.unshift("exec"); + } + + if (container) { + commandParts.push("-c", container); } const shell = createTerminalTab({ title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()}) [Attached]` }); - terminalStore.sendCommand(command, { + terminalStore.sendCommand(commandParts.join(" "), { enter: true, tabId: shell.id }); - + Navigation.hideDetails(); } diff --git a/extensions/pod-menu/src/shell-menu.tsx b/extensions/pod-menu/src/shell-menu.tsx index 4c52379525..71bf94127c 100644 --- a/extensions/pod-menu/src/shell-menu.tsx +++ b/extensions/pod-menu/src/shell-menu.tsx @@ -39,6 +39,7 @@ const { } = Renderer; const { Util, + App, } = Common; export interface PodShellMenuProps extends Renderer.Component.KubeObjectMenuProps { @@ -46,29 +47,44 @@ export interface PodShellMenuProps extends Renderer.Component.KubeObjectMenuProp export class PodShellMenu extends React.Component { async execShell(container?: string) { - Navigation.hideDetails(); const { object: pod } = this.props; - const containerParam = container ? `-c ${container}` : ""; - let command = `kubectl exec -i -t -n ${pod.getNs()} ${pod.getName()} ${containerParam} "--"`; + + const kubectlPath = App.Preferences.getKubectlPath() || "kubectl"; + const commandParts = [ + kubectlPath, + "exec", + "-i", + "-t", + "-n", pod.getNs(), + pod.getName(), + ]; if (window.navigator.platform !== "Win32") { - command = `exec ${command}`; + commandParts.unshift("exec"); } + if (container) { + commandParts.push("-c", container); + } + + commandParts.push("--"); + if (pod.getSelectedNodeOs() === "windows") { - command = `${command} powershell`; + commandParts.push("powershell"); } else { - command = `${command} sh -c "clear; (bash || ash || sh)"`; + commandParts.push('sh -c "clear; (bash || ash || sh)"'); } const shell = createTerminalTab({ title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()})` }); - terminalStore.sendCommand(command, { + terminalStore.sendCommand(commandParts.join(" "), { enter: true, tabId: shell.id }); + + Navigation.hideDetails(); } render() { diff --git a/src/extensions/common-api/app.ts b/src/extensions/common-api/app.ts index 0b5e463962..401fc4d283 100644 --- a/src/extensions/common-api/app.ts +++ b/src/extensions/common-api/app.ts @@ -21,6 +21,7 @@ import { getAppVersion } from "../../common/utils"; import { ExtensionsStore } from "../extensions-store"; +import * as Preferences from "./user-preferences"; export const version = getAppVersion(); export { isSnap, isWindows, isMac, isLinux, appName, slackUrl, issuesTrackerUrl } from "../../common/vars"; @@ -28,3 +29,5 @@ export { isSnap, isWindows, isMac, isLinux, appName, slackUrl, issuesTrackerUrl export function getEnabledExtensions(): string[] { return ExtensionsStore.getInstance().enabledExtensions; } + +export { Preferences }; diff --git a/src/extensions/common-api/index.ts b/src/extensions/common-api/index.ts index 9fdf3a837b..19b4f3a930 100644 --- a/src/extensions/common-api/index.ts +++ b/src/extensions/common-api/index.ts @@ -35,5 +35,5 @@ export { Store, Types, Util, - logger + logger, }; diff --git a/src/extensions/common-api/user-preferences.ts b/src/extensions/common-api/user-preferences.ts new file mode 100644 index 0000000000..36ee2dd1e4 --- /dev/null +++ b/src/extensions/common-api/user-preferences.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { UserStore } from "../../common/user-store"; + +/** + * Get the configured kubectl binaries path. + */ +export function getKubectlPath(): string | undefined { + return UserStore.getInstance().kubectlBinariesPath; +}