Skip to content

Commit 00eb857

Browse files
committed
Allow to run node shell on Bootlerocket and Windows nodes
1 parent f1a960f commit 00eb857

4 files changed

Lines changed: 103 additions & 29 deletions

File tree

packages/core/src/common/cluster-types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export interface ClusterPreferences extends ClusterPrometheusPreferences {
108108
httpsProxy?: string;
109109
hiddenMetrics?: string[];
110110
nodeShellImage?: string;
111+
nodeShellWindowsImage?: string;
111112
imagePullSecret?: string;
112113
defaultNamespace?: string;
113114
}
@@ -177,7 +178,12 @@ export enum ClusterMetricsResourceType {
177178
/**
178179
* The default node shell image
179180
*/
180-
export const initialNodeShellImage = "docker.io/alpine:3.13";
181+
export const initialNodeShellImage = "docker.io/library/alpine";
182+
183+
/**
184+
* The default node shell image for Windows
185+
*/
186+
export const initialNodeShellWindowsImage = "mcr.microsoft.com/powershell";
181187

182188
/**
183189
* The data representing a cluster's state, for passing between main and renderer

packages/core/src/main/shell-session/node-shell-session/node-shell-session.ts

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { NodeApi } from "@k8slens/kube-api";
1313
import { TerminalChannels } from "../../../common/terminal/channels";
1414
import type { CreateKubeJsonApiForCluster } from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable";
1515
import type { CreateKubeApi } from "../../../common/k8s-api/create-kube-api.injectable";
16-
import { initialNodeShellImage } from "../../../common/cluster-types";
16+
import { initialNodeShellImage, initialNodeShellWindowsImage } from "../../../common/cluster-types";
1717
import type { LoadProxyKubeconfig } from "../../cluster/load-proxy-kubeconfig.injectable";
1818
import type { Pod } from "@k8slens/kube-object";
1919

@@ -71,7 +71,23 @@ export class NodeShellSession extends ShellSession {
7171
}
7272

7373
const env = await this.getCachedShellEnv();
74-
const args = ["exec", "-i", "-t", "-n", "kube-system", this.podName, "--"];
74+
const args = ["attach", "-q", "-i", "-t", "-n", "kube-system", this.podName];
75+
76+
await this.openShellProcess(await this.kubectl.getPath(), args, env);
77+
}
78+
79+
protected async createNodeShellPod(coreApi: CoreV1Api) {
80+
const {
81+
imagePullSecret,
82+
nodeShellImage,
83+
} = this.cluster.preferences;
84+
85+
const imagePullSecrets = imagePullSecret
86+
? [{
87+
name: imagePullSecret,
88+
}]
89+
: undefined;
90+
7591
const nodeApi = this.dependencies.createKubeApi(NodeApi, {
7692
request: this.dependencies.createKubeJsonApiForCluster(this.cluster.id),
7793
});
@@ -82,34 +98,50 @@ export class NodeShellSession extends ShellSession {
8298
}
8399

84100
const nodeOs = node.getOperatingSystem();
101+
const nodeOsImage = node.getOperatingSystemImage();
102+
const nodeKernelVersion = node.getKernelVersion();
103+
104+
let image: string;
105+
let command: string[];
106+
let args: string[];
107+
let securityContext: any;
85108

86109
switch (nodeOs) {
87110
default:
88111
this.dependencies.logger.warn(`[NODE-SHELL-SESSION]: could not determine node OS, falling back with assumption of linux`);
89112
// fallthrough
90113
case "linux":
91-
args.push("sh", "-c", "((clear && bash) || (clear && ash) || (clear && sh))");
114+
image = nodeShellImage || initialNodeShellImage;
115+
command = ["nsenter"];
116+
117+
if (nodeOsImage && nodeOsImage.startsWith("Bottlerocket OS")) {
118+
args = ["-t", "1", "-m", "-u", "-i", "-n", "-p", "--", "apiclient", "exec", "admin", "bash", "-l"];
119+
} else {
120+
args = ["-t", "1", "-m", "-u", "-i", "-n", "-p", "--", "bash", "-l"];
121+
}
122+
123+
securityContext = {
124+
privileged: true,
125+
};
92126
break;
93127
case "windows":
94-
args.push("powershell");
128+
if (nodeKernelVersion) {
129+
image = nodeShellImage || initialNodeShellWindowsImage;
130+
} else {
131+
throw new Error(`No status with kernel version for node ${this.nodeName} found`);
132+
}
133+
command = ["cmd.exe"];
134+
args = ["/c", "%CONTAINER_SANDBOX_MOUNT_POINT%\\Program Files\\PowerShell\\latest\\pwsh.exe", "-nol", "-wd", "C:\\"];
135+
securityContext = {
136+
privileged: true,
137+
windowsOptions: {
138+
hostProcess: true,
139+
runAsUserName: "NT AUTHORITY\\SYSTEM",
140+
},
141+
};
95142
break;
96143
}
97144

98-
await this.openShellProcess(await this.kubectl.getPath(), args, env);
99-
}
100-
101-
protected createNodeShellPod(coreApi: CoreV1Api) {
102-
const {
103-
imagePullSecret,
104-
nodeShellImage,
105-
} = this.cluster.preferences;
106-
107-
const imagePullSecrets = imagePullSecret
108-
? [{
109-
name: imagePullSecret,
110-
}]
111-
: undefined;
112-
113145
return coreApi
114146
.createNamespacedPod("kube-system", {
115147
metadata: {
@@ -129,12 +161,13 @@ export class NodeShellSession extends ShellSession {
129161
priorityClassName: "system-node-critical",
130162
containers: [{
131163
name: "shell",
132-
image: nodeShellImage || initialNodeShellImage,
133-
securityContext: {
134-
privileged: true,
135-
},
136-
command: ["nsenter"],
137-
args: ["-t", "1", "-m", "-u", "-i", "-n", "sleep", "14000"],
164+
image,
165+
securityContext,
166+
command,
167+
args,
168+
stdin: true,
169+
stdinOnce: true,
170+
tty: true,
138171
}],
139172
imagePullSecrets,
140173
},

packages/core/src/renderer/components/cluster-settings/node-shell-setting.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React from "react";
1010
import { Input } from "../input/input";
1111
import { observer } from "mobx-react";
1212
import { Icon } from "@k8slens/icon";
13-
import { initialNodeShellImage } from "../../../common/cluster-types";
13+
import { initialNodeShellImage, initialNodeShellWindowsImage } from "../../../common/cluster-types";
1414
import Gutter from "../gutter/gutter";
1515

1616
export interface ClusterNodeShellSettingProps {
@@ -20,6 +20,7 @@ export interface ClusterNodeShellSettingProps {
2020
@observer
2121
export class ClusterNodeShellSetting extends React.Component<ClusterNodeShellSettingProps> {
2222
@observable nodeShellImage = this.props.cluster.preferences?.nodeShellImage || "";
23+
@observable nodeShellWindowsImage = this.props.cluster.preferences?.nodeShellWindowsImage || "";
2324
@observable imagePullSecret = this.props.cluster.preferences?.imagePullSecret || "";
2425

2526
constructor(props: ClusterNodeShellSettingProps) {
@@ -30,6 +31,7 @@ export class ClusterNodeShellSetting extends React.Component<ClusterNodeShellSet
3031
componentWillUnmount() {
3132
runInAction(() => {
3233
this.props.cluster.preferences.nodeShellImage = this.nodeShellImage || undefined;
34+
this.props.cluster.preferences.nodeShellWindowsImage = this.nodeShellWindowsImage || undefined;
3335
this.props.cluster.preferences.imagePullSecret = this.imagePullSecret || undefined;
3436
});
3537
}
@@ -38,7 +40,7 @@ export class ClusterNodeShellSetting extends React.Component<ClusterNodeShellSet
3840
return (
3941
<>
4042
<section>
41-
<SubTitle title="Node shell image" id="node-shell-image"/>
43+
<SubTitle title="Node shell image for Linux" id="node-shell-image"/>
4244
<Input
4345
theme="round-black"
4446
placeholder={`Default image: ${initialNodeShellImage}`}
@@ -58,7 +60,32 @@ export class ClusterNodeShellSetting extends React.Component<ClusterNodeShellSet
5860
}
5961
/>
6062
<small className="hint">
61-
Node shell image. Used for creating node shell pod.
63+
Node shell image. Used for creating node shell pod on Linux nodes.
64+
</small>
65+
</section>
66+
<Gutter />
67+
<section>
68+
<SubTitle title="Node shell image for Windows" id="node-shell-windows-image"/>
69+
<Input
70+
theme="round-black"
71+
placeholder={`Default image: ${initialNodeShellWindowsImage}`}
72+
value={this.nodeShellWindowsImage}
73+
onChange={value => this.nodeShellWindowsImage = value}
74+
iconRight={
75+
this.nodeShellWindowsImage
76+
? (
77+
<Icon
78+
smallest
79+
material="close"
80+
onClick={() => this.nodeShellWindowsImage = ""}
81+
tooltip="Reset"
82+
/>
83+
)
84+
: undefined
85+
}
86+
/>
87+
<small className="hint">
88+
Node shell image. Used for creating node shell pod on Windows nodes.
6289
</small>
6390
</section>
6491
<Gutter />

packages/kube-object/src/specifics/node.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ export class Node extends KubeObject<ClusterScopedMetadata, NodeStatus, NodeSpec
247247
);
248248
}
249249

250+
getOperatingSystemImage(): string | undefined {
251+
return this.status?.nodeInfo?.osImage;
252+
}
253+
254+
getKernelVersion(): string | undefined {
255+
return this.status?.nodeInfo?.kernelVersion;
256+
}
257+
250258
isUnschedulable() {
251259
return this.spec.unschedulable;
252260
}

0 commit comments

Comments
 (0)