mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Allow extensions to register kube-object menus + details (#1108)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
91bef77997
commit
99db7aca19
101 files changed
+7858
-1604
No files matched your search
@@ -0,0 +1,5 @@
|
||||
install-deps:
|
||||
npm install
|
||||
|
||||
build: install-deps
|
||||
npm run build
|
||||
Generated
+3508
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "lens-node-menu",
|
||||
"version": "0.1.0",
|
||||
"description": "Lens node menu",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
"metadata": {},
|
||||
"styles": []
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Registry, LensRendererExtension } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
import { NodeMenu } from "./src/node-menu"
|
||||
|
||||
export default class NodeMenuRendererExtension extends LensRendererExtension {
|
||||
async onActivate() {
|
||||
console.log("node-menu extension activated")
|
||||
}
|
||||
|
||||
registerKubeObjectMenus(registry: Registry.KubeObjectMenuRegistry) {
|
||||
this.disposers.push(
|
||||
registry.add({
|
||||
kind: "Node",
|
||||
apiVersions: ["v1"],
|
||||
components: {
|
||||
MenuItem: (props) => <NodeMenu {...props} />
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import { Component, K8sApi, Navigation} from "@k8slens/extensions"
|
||||
|
||||
export function NodeMenu(props: Component.KubeObjectMenuProps<K8sApi.Node>) {
|
||||
const { object: node, toolbar } = props;
|
||||
if (!node) return null;
|
||||
const nodeName = node.getName();
|
||||
|
||||
const sendToTerminal = (command: string) => {
|
||||
Component.terminalStore.sendCommand(command, {
|
||||
enter: true,
|
||||
newTab: true,
|
||||
});
|
||||
Navigation.hideDetails();
|
||||
}
|
||||
|
||||
const shell = () => {
|
||||
Component.createTerminalTab({
|
||||
title: `Node: ${nodeName}`,
|
||||
node: nodeName,
|
||||
});
|
||||
Navigation.hideDetails();
|
||||
}
|
||||
|
||||
const cordon = () => {
|
||||
sendToTerminal(`kubectl cordon ${nodeName}`);
|
||||
}
|
||||
|
||||
const unCordon = () => {
|
||||
sendToTerminal(`kubectl uncordon ${nodeName}`)
|
||||
}
|
||||
|
||||
const drain = () => {
|
||||
const command = `kubectl drain ${nodeName} --delete-local-data --ignore-daemonsets --force`;
|
||||
Component.ConfirmDialog.open({
|
||||
ok: () => sendToTerminal(command),
|
||||
labelOk: `Drain Node`,
|
||||
message: (
|
||||
<p>
|
||||
Are you sure you want to drain <b>{nodeName}</b>?
|
||||
</p>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Component.MenuItem onClick={shell}>
|
||||
<Component.Icon svg="ssh" interactive={toolbar} title="Node shell"/>
|
||||
<span className="title">Shell</span>
|
||||
</Component.MenuItem>
|
||||
{!node.isUnschedulable() && (
|
||||
<Component.MenuItem onClick={cordon}>
|
||||
<Component.Icon material="pause_circle_filled" title="Cordon" interactive={toolbar}/>
|
||||
<span className="title">Cordon</span>
|
||||
</Component.MenuItem>
|
||||
)}
|
||||
{node.isUnschedulable() && (
|
||||
<Component.MenuItem onClick={unCordon}>
|
||||
<Component.Icon material="play_circle_filled" title="Uncordon" interactive={toolbar}/>
|
||||
<span className="title">Uncordon</span>
|
||||
</Component.MenuItem>
|
||||
)}
|
||||
<Component.MenuItem onClick={drain}>
|
||||
<Component.Icon material="delete_sweep" title="Drain" interactive={toolbar}/>
|
||||
<span className="title">Drain</span>
|
||||
</Component.MenuItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"module": "CommonJS",
|
||||
"target": "ES2017",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"moduleResolution": "Node",
|
||||
"sourceMap": false,
|
||||
"declaration": false,
|
||||
"strict": false,
|
||||
"noImplicitAny": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"../../src/extensions/npm/**/*.d.ts",
|
||||
"./*.ts",
|
||||
"./*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"*.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
entry: './renderer.tsx',
|
||||
context: __dirname,
|
||||
target: "electron-renderer",
|
||||
mode: "production",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: [
|
||||
{
|
||||
"@k8slens/extensions": "var global.LensExtensions",
|
||||
"react": "var global.React",
|
||||
"mobx": "var global.Mobx"
|
||||
}
|
||||
],
|
||||
resolve: {
|
||||
extensions: [ '.tsx', '.ts', '.js' ],
|
||||
},
|
||||
output: {
|
||||
libraryTarget: "commonjs2",
|
||||
globalObject: "this",
|
||||
filename: 'renderer.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
install-deps:
|
||||
npm install
|
||||
|
||||
build: install-deps
|
||||
npm run build
|
||||
Generated
+3508
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "lens-pod-menu",
|
||||
"version": "0.1.0",
|
||||
"description": "Lens pod menu",
|
||||
"renderer": "dist/renderer.js",
|
||||
"lens": {
|
||||
"metadata": {},
|
||||
"styles": []
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Registry, LensRendererExtension } from "@k8slens/extensions";
|
||||
import { PodShellMenu } from "./src/shell-menu"
|
||||
import { PodLogsMenu } from "./src/logs-menu"
|
||||
import React from "react"
|
||||
|
||||
export default class PodMenuRendererExtension extends LensRendererExtension {
|
||||
async onActivate() {
|
||||
console.log("pod-menu extension activated")
|
||||
}
|
||||
|
||||
registerKubeObjectMenus(registry: Registry.KubeObjectMenuRegistry) {
|
||||
this.disposers.push(
|
||||
registry.add({
|
||||
kind: "Pod",
|
||||
apiVersions: ["v1"],
|
||||
components: {
|
||||
MenuItem: (props) => <PodShellMenu {...props} />
|
||||
}
|
||||
})
|
||||
)
|
||||
this.disposers.push(
|
||||
registry.add({
|
||||
kind: "Pod",
|
||||
apiVersions: ["v1"],
|
||||
components: {
|
||||
MenuItem: (props) => <PodLogsMenu {...props} />
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from "react";
|
||||
import { Component, K8sApi, Util, Navigation } from "@k8slens/extensions";
|
||||
|
||||
interface Props extends Component.KubeObjectMenuProps<K8sApi.Pod> {
|
||||
}
|
||||
|
||||
export class PodLogsMenu extends React.Component<Props> {
|
||||
showLogs(container: K8sApi.IPodContainer) {
|
||||
Navigation.hideDetails();
|
||||
const pod = this.props.object;
|
||||
Component.createPodLogsTab({
|
||||
pod,
|
||||
containers: pod.getContainers(),
|
||||
initContainers: pod.getInitContainers(),
|
||||
selectedContainer: container,
|
||||
showTimestamps: false,
|
||||
previous: false,
|
||||
tailLines: 1000
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { object: pod, toolbar } = this.props
|
||||
const containers = pod.getAllContainers();
|
||||
const statuses = pod.getContainerStatuses();
|
||||
if (!containers.length) return;
|
||||
return (
|
||||
<Component.MenuItem onClick={Util.prevDefault(() => this.showLogs(containers[0]))}>
|
||||
<Component.Icon material="subject" title="Logs" interactive={toolbar}/>
|
||||
<span className="title">Logs</span>
|
||||
{containers.length > 1 && (
|
||||
<>
|
||||
<Component.Icon className="arrow" material="keyboard_arrow_right"/>
|
||||
<Component.SubMenu>
|
||||
{
|
||||
containers.map(container => {
|
||||
const { name } = container
|
||||
const status = statuses.find(status => status.name === name);
|
||||
const brick = status ? (
|
||||
<Component.StatusBrick
|
||||
className={Util.cssNames(Object.keys(status.state)[0], { ready: status.ready })}
|
||||
/>
|
||||
) : null
|
||||
return (
|
||||
<Component.MenuItem key={name} onClick={Util.prevDefault(() => this.showLogs(container))} className="flex align-center">
|
||||
{brick}
|
||||
{name}
|
||||
</Component.MenuItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Component.SubMenu>
|
||||
</>
|
||||
)}
|
||||
</Component.MenuItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
|
||||
import React from "react";
|
||||
import { Component, K8sApi, Util, Navigation } from "@k8slens/extensions";
|
||||
|
||||
interface Props extends Component.KubeObjectMenuProps<K8sApi.Pod> {
|
||||
}
|
||||
|
||||
export class PodShellMenu extends React.Component<Props> {
|
||||
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} "--"`
|
||||
if (window.navigator.platform !== "Win32") {
|
||||
command = `exec ${command}`
|
||||
}
|
||||
if (pod.getSelectedNodeOs() === "windows") {
|
||||
command = `${command} powershell`
|
||||
} else {
|
||||
command = `${command} sh -c "clear; (bash || ash || sh)"`
|
||||
}
|
||||
|
||||
const shell = Component.createTerminalTab({
|
||||
title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()})`
|
||||
});
|
||||
|
||||
Component.terminalStore.sendCommand(command, {
|
||||
enter: true,
|
||||
tabId: shell.id
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { object, toolbar } = this.props
|
||||
console.log(Object.keys(object))
|
||||
const containers = object.getRunningContainers();
|
||||
if (!containers.length) return;
|
||||
return (
|
||||
<Component.MenuItem onClick={Util.prevDefault(() => this.execShell(containers[0].name))}>
|
||||
<Component.Icon svg="ssh" interactive={toolbar} title="Pod shell"/>
|
||||
<span className="title">Shell</span>
|
||||
{containers.length > 1 && (
|
||||
<>
|
||||
<Component.Icon className="arrow" material="keyboard_arrow_right"/>
|
||||
<Component.SubMenu>
|
||||
{
|
||||
containers.map(container => {
|
||||
const { name } = container;
|
||||
return (
|
||||
<Component.MenuItem key={name} onClick={Util.prevDefault(() => this.execShell(name))} className="flex align-center">
|
||||
<Component.StatusBrick/>
|
||||
{name}
|
||||
</Component.MenuItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Component.SubMenu>
|
||||
</>
|
||||
)}
|
||||
</Component.MenuItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"module": "CommonJS",
|
||||
"target": "ES2017",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"moduleResolution": "Node",
|
||||
"sourceMap": false,
|
||||
"declaration": false,
|
||||
"strict": false,
|
||||
"noImplicitAny": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"../../src/extensions/npm/**/*.d.ts",
|
||||
"./*.ts",
|
||||
"./*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"*.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
entry: './renderer.tsx',
|
||||
context: __dirname,
|
||||
target: "electron-renderer",
|
||||
mode: "production",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: [
|
||||
{
|
||||
"@k8slens/extensions": "var global.LensExtensions",
|
||||
"react": "var global.React",
|
||||
"mobx": "var global.Mobx"
|
||||
}
|
||||
],
|
||||
resolve: {
|
||||
extensions: [ '.tsx', '.ts', '.js' ],
|
||||
},
|
||||
output: {
|
||||
libraryTarget: "commonjs2",
|
||||
globalObject: "this",
|
||||
filename: 'renderer.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in new issue
Block a user