diff --git a/packages/core/src/common/cluster-types.ts b/packages/core/src/common/cluster-types.ts index a8ce7da912..2a31c894e3 100644 --- a/packages/core/src/common/cluster-types.ts +++ b/packages/core/src/common/cluster-types.ts @@ -108,6 +108,7 @@ export interface ClusterPreferences extends ClusterPrometheusPreferences { httpsProxy?: string; hiddenMetrics?: string[]; nodeShellImage?: string; + nodeShellWindowsImage?: string; imagePullSecret?: string; defaultNamespace?: string; } @@ -177,7 +178,12 @@ export enum ClusterMetricsResourceType { /** * The default node shell image */ -export const initialNodeShellImage = "docker.io/alpine:3.13"; +export const initialNodeShellImage = "docker.io/library/alpine"; + +/** + * The default node shell image for Windows + */ +export const initialNodeShellWindowsImage = "mcr.microsoft.com/powershell"; /** * The data representing a cluster's state, for passing between main and renderer diff --git a/packages/core/src/main/shell-session/node-shell-session/node-shell-session.ts b/packages/core/src/main/shell-session/node-shell-session/node-shell-session.ts index b564b33073..c283f0450d 100644 --- a/packages/core/src/main/shell-session/node-shell-session/node-shell-session.ts +++ b/packages/core/src/main/shell-session/node-shell-session/node-shell-session.ts @@ -13,7 +13,7 @@ import { NodeApi } from "@k8slens/kube-api"; import { TerminalChannels } from "../../../common/terminal/channels"; import type { CreateKubeJsonApiForCluster } from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; import type { CreateKubeApi } from "../../../common/k8s-api/create-kube-api.injectable"; -import { initialNodeShellImage } from "../../../common/cluster-types"; +import { initialNodeShellImage, initialNodeShellWindowsImage } from "../../../common/cluster-types"; import type { LoadProxyKubeconfig } from "../../cluster/load-proxy-kubeconfig.injectable"; import type { Pod } from "@k8slens/kube-object"; @@ -71,34 +71,12 @@ export class NodeShellSession extends ShellSession { } const env = await this.getCachedShellEnv(); - const args = ["exec", "-i", "-t", "-n", "kube-system", this.podName, "--"]; - const nodeApi = this.dependencies.createKubeApi(NodeApi, { - request: this.dependencies.createKubeJsonApiForCluster(this.cluster.id), - }); - const node = await nodeApi.get({ name: this.nodeName }); - - if (!node) { - throw new Error(`No node with name=${this.nodeName} found`); - } - - const nodeOs = node.getOperatingSystem(); - - switch (nodeOs) { - default: - this.dependencies.logger.warn(`[NODE-SHELL-SESSION]: could not determine node OS, falling back with assumption of linux`); - // fallthrough - case "linux": - args.push("sh", "-c", "((clear && bash) || (clear && ash) || (clear && sh))"); - break; - case "windows": - args.push("powershell"); - break; - } + const args = ["attach", "-q", "-i", "-t", "-n", "kube-system", this.podName]; await this.openShellProcess(await this.kubectl.getPath(), args, env); } - protected createNodeShellPod(coreApi: CoreV1Api) { + protected async createNodeShellPod(coreApi: CoreV1Api) { const { imagePullSecret, nodeShellImage, @@ -110,6 +88,60 @@ export class NodeShellSession extends ShellSession { }] : undefined; + const nodeApi = this.dependencies.createKubeApi(NodeApi, { + request: this.dependencies.createKubeJsonApiForCluster(this.cluster.id), + }); + const node = await nodeApi.get({ name: this.nodeName }); + + if (!node) { + throw new Error(`No node with name=${this.nodeName} found`); + } + + const nodeOs = node.getOperatingSystem(); + const nodeOsImage = node.getOperatingSystemImage(); + const nodeKernelVersion = node.getKernelVersion(); + + let image: string; + let command: string[]; + let args: string[]; + let securityContext: any; + + switch (nodeOs) { + default: + this.dependencies.logger.warn(`[NODE-SHELL-SESSION]: could not determine node OS, falling back with assumption of linux`); + // fallthrough + case "linux": + image = nodeShellImage || initialNodeShellImage; + command = ["nsenter"]; + + if (nodeOsImage && nodeOsImage.startsWith("Bottlerocket OS")) { + args = ["-t", "1", "-m", "-u", "-i", "-n", "-p", "--", "apiclient", "exec", "admin", "bash", "-l"]; + } else { + args = ["-t", "1", "-m", "-u", "-i", "-n", "-p", "--", "bash", "-l"]; + } + + securityContext = { + privileged: true, + }; + break; + case "windows": + if (nodeKernelVersion) { + image = nodeShellImage || initialNodeShellWindowsImage; + } else { + throw new Error(`No status with kernel version for node ${this.nodeName} found`); + } + command = ["cmd.exe"]; + args = ["/c", "%CONTAINER_SANDBOX_MOUNT_POINT%\\Program Files\\PowerShell\\latest\\pwsh.exe", "-nol", "-wd", "C:\\"]; + securityContext = { + privileged: true, + windowsOptions: { + hostProcess: true, + runAsUserName: "NT AUTHORITY\\SYSTEM", + }, + }; + break; + } + return coreApi .createNamespacedPod("kube-system", { metadata: { @@ -129,12 +161,13 @@ export class NodeShellSession extends ShellSession { priorityClassName: "system-node-critical", containers: [{ name: "shell", - image: nodeShellImage || initialNodeShellImage, - securityContext: { - privileged: true, - }, - command: ["nsenter"], - args: ["-t", "1", "-m", "-u", "-i", "-n", "sleep", "14000"], + image, + securityContext, + command, + args, + stdin: true, + stdinOnce: true, + tty: true, }], imagePullSecrets, }, diff --git a/packages/core/src/renderer/components/cluster-settings/node-shell-setting.tsx b/packages/core/src/renderer/components/cluster-settings/node-shell-setting.tsx index 1c7f367d4b..2bf80b7c6a 100644 --- a/packages/core/src/renderer/components/cluster-settings/node-shell-setting.tsx +++ b/packages/core/src/renderer/components/cluster-settings/node-shell-setting.tsx @@ -10,7 +10,7 @@ import React from "react"; import { Input } from "../input/input"; import { observer } from "mobx-react"; import { Icon } from "@k8slens/icon"; -import { initialNodeShellImage } from "../../../common/cluster-types"; +import { initialNodeShellImage, initialNodeShellWindowsImage } from "../../../common/cluster-types"; import Gutter from "../gutter/gutter"; export interface ClusterNodeShellSettingProps { @@ -20,6 +20,7 @@ export interface ClusterNodeShellSettingProps { @observer export class ClusterNodeShellSetting extends React.Component { @observable nodeShellImage = this.props.cluster.preferences?.nodeShellImage || ""; + @observable nodeShellWindowsImage = this.props.cluster.preferences?.nodeShellWindowsImage || ""; @observable imagePullSecret = this.props.cluster.preferences?.imagePullSecret || ""; constructor(props: ClusterNodeShellSettingProps) { @@ -30,6 +31,7 @@ export class ClusterNodeShellSetting extends React.Component { this.props.cluster.preferences.nodeShellImage = this.nodeShellImage || undefined; + this.props.cluster.preferences.nodeShellWindowsImage = this.nodeShellWindowsImage || undefined; this.props.cluster.preferences.imagePullSecret = this.imagePullSecret || undefined; }); } @@ -38,7 +40,7 @@ export class ClusterNodeShellSetting extends React.Component
- + - Node shell image. Used for creating node shell pod. + Node shell image. Used for creating node shell pod on Linux nodes. + +
+ +
+ + this.nodeShellWindowsImage = value} + iconRight={ + this.nodeShellWindowsImage + ? ( + this.nodeShellWindowsImage = ""} + tooltip="Reset" + /> + ) + : undefined + } + /> + + Node shell image. Used for creating node shell pod on Windows nodes.
diff --git a/packages/kube-object/src/specifics/node.ts b/packages/kube-object/src/specifics/node.ts index 2b5ce77c0a..12efeddee6 100644 --- a/packages/kube-object/src/specifics/node.ts +++ b/packages/kube-object/src/specifics/node.ts @@ -247,6 +247,14 @@ export class Node extends KubeObject