1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

add no-unused and react/recommended to eslint (#1523)

* add no-unused-vars and no-unused-imports

* added quotes: double, and remove ignore pattern

* move itif and describeif into utils

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2020-11-27 08:48:38 -05:00 committed by GitHub
parent 10eb082854
commit 7451869c25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
199 changed files with 1178 additions and 807 deletions

View File

@ -1,62 +1,92 @@
const packageJson = require("./package.json");
module.exports = { module.exports = {
ignorePatterns: [ ignorePatterns: [
"**/node_modules/**/*", "**/node_modules/**/*",
"**/dist/**/*", "**/dist/**/*",
], ],
settings: {
react: {
version: packageJson.devDependencies.react || "detect",
}
},
overrides: [ overrides: [
{ {
files: [ files: [
"src/renderer/**/*.js", "**/*.js"
"build/**/*.js",
"extensions/**/*.js"
], ],
extends: [ extends: [
'eslint:recommended', "eslint:recommended",
], ],
env: { env: {
node: true node: true
}, },
parserOptions: { parserOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: 'module', sourceType: "module",
}, },
plugins: [
"unused-imports"
],
rules: { rules: {
"indent": ["error", 2, { "indent": ["error", 2, {
"SwitchCase": 1, "SwitchCase": 1,
}], }],
"no-unused-vars": "off", "no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true,
}
],
"quotes": ["error", "double", {
"avoidEscape": true,
"allowTemplateLiterals": true,
}],
"semi": ["error", "always"], "semi": ["error", "always"],
"object-shorthand": "error", "object-shorthand": "error",
} }
}, },
{ {
files: [ files: [
"build/*.ts", "**/*.ts",
"src/**/*.ts",
"integration/**/*.ts",
"src/extensions/**/*.ts*",
"extensions/**/*.ts*",
"__mocks__/*.ts",
], ],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
extends: [ extends: [
'plugin:@typescript-eslint/recommended', "plugin:@typescript-eslint/recommended",
],
plugins: [
"unused-imports"
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: 'module', sourceType: "module",
}, },
rules: { rules: {
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-types": "off", "@typescript-eslint/ban-types": "off",
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports-ts": "error",
"unused-imports/no-unused-vars-ts": [
"warn", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true,
}
],
"indent": ["error", 2, { "indent": ["error", 2, {
"SwitchCase": 1, "SwitchCase": 1,
}], }],
"quotes": ["error", "double", {
"avoidEscape": true,
"allowTemplateLiterals": true,
}],
"semi": "off", "semi": "off",
"@typescript-eslint/semi": ["error"], "@typescript-eslint/semi": ["error"],
"object-shorthand": "error", "object-shorthand": "error",
@ -64,21 +94,24 @@ module.exports = {
}, },
{ {
files: [ files: [
"src/renderer/**/*.tsx", "**/*.tsx",
], ],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
plugins: [
"unused-imports"
],
extends: [ extends: [
'plugin:@typescript-eslint/recommended', "plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: 'module', sourceType: "module",
jsx: true, jsx: true,
}, },
rules: { rules: {
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",
@ -87,9 +120,23 @@ module.exports = {
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-types": "off", "@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"react/display-name": "off",
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports-ts": "error",
"unused-imports/no-unused-vars-ts": [
"warn", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true,
}
],
"indent": ["error", 2, { "indent": ["error", 2, {
"SwitchCase": 1, "SwitchCase": 1,
}], }],
"quotes": ["error", "double", {
"avoidEscape": true,
"allowTemplateLiterals": true,
}],
"semi": "off", "semi": "off",
"@typescript-eslint/semi": ["error"], "@typescript-eslint/semi": ["error"],
"object-shorthand": "error", "object-shorthand": "error",

View File

@ -4,9 +4,7 @@ module.exports = {
app: { app: {
getVersion: jest.fn().mockReturnValue("3.0.0"), getVersion: jest.fn().mockReturnValue("3.0.0"),
getLocale: jest.fn().mockRejectedValue("en"), getLocale: jest.fn().mockRejectedValue("en"),
getPath: jest.fn((name: string) => { getPath: jest.fn(() => "tmp"),
return "tmp";
}),
}, },
remote: { remote: {
app: { app: {

View File

@ -92,12 +92,12 @@ class KubectlDownloader {
} }
const downloadVersion = packageInfo.config.bundledKubectlVersion; const downloadVersion = packageInfo.config.bundledKubectlVersion;
const baseDir = path.join(process.env.INIT_CWD, 'binaries', 'client'); const baseDir = path.join(process.env.INIT_CWD, "binaries", "client");
const downloads = [ const downloads = [
{ platform: 'linux', arch: 'amd64', target: path.join(baseDir, 'linux', 'x64', 'kubectl') }, { platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "kubectl") },
{ platform: 'darwin', arch: 'amd64', target: path.join(baseDir, 'darwin', 'x64', 'kubectl') }, { platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "kubectl") },
{ platform: 'windows', arch: 'amd64', target: path.join(baseDir, 'windows', 'x64', 'kubectl.exe') }, { platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "kubectl.exe") },
{ platform: 'windows', arch: '386', target: path.join(baseDir, 'windows', 'ia32', 'kubectl.exe') } { platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") }
]; ];
downloads.forEach((dlOpts) => { downloads.forEach((dlOpts) => {

View File

@ -1,8 +1,8 @@
const { notarize } = require('electron-notarize'); const { notarize } = require("electron-notarize");
exports.default = async function notarizing(context) { exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context; const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') { if (electronPlatformName !== "darwin") {
return; return;
} }
if (!process.env.APPLEID || !process.env.APPLEIDPASS) { if (!process.env.APPLEID || !process.env.APPLEIDPASS) {
@ -12,7 +12,7 @@ exports.default = async function notarizing(context) {
const appName = context.packager.appInfo.productFilename; const appName = context.packager.appInfo.productFilename;
return await notarize({ return await notarize({
appBundleId: 'io.kontena.lens-app', appBundleId: "io.kontena.lens-app",
appPath: `${appOutDir}/${appName}.app`, appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLEID, appleId: process.env.APPLEID,
appleIdPassword: process.env.APPLEIDPASS, appleIdPassword: process.env.APPLEIDPASS,

View File

@ -2,10 +2,10 @@ import { LensMainExtension } from "@k8slens/extensions";
export default class ExampleExtensionMain extends LensMainExtension { export default class ExampleExtensionMain extends LensMainExtension {
onActivate() { onActivate() {
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.name, this.id); console.log("EXAMPLE EXTENSION MAIN: ACTIVATED", this.name, this.id);
} }
onDeactivate() { onDeactivate() {
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.name, this.id); console.log("EXAMPLE EXTENSION MAIN: DEACTIVATED", this.name, this.id);
} }
} }

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './main.ts', entry: "./main.ts",
context: __dirname, context: __dirname,
target: "electron-main", target: "electron-main",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,16 +23,16 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
filename: 'main.js', filename: "main.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -40,7 +40,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -53,13 +53,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
]; ];

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,13 +23,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
]; ];

View File

@ -1,10 +1,10 @@
import path from "path"; import path from "path";
const outputPath = path.resolve(__dirname, 'dist'); const outputPath = path.resolve(__dirname, "dist");
export default [ export default [
{ {
entry: './main.ts', entry: "./main.ts",
context: __dirname, context: __dirname,
target: "electron-main", target: "electron-main",
mode: "production", mode: "production",
@ -12,7 +12,7 @@ export default [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -22,12 +22,12 @@ export default [
"mobx": "var global.Mobx", "mobx": "var global.Mobx",
}, },
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js'], extensions: [".tsx", ".ts", ".js"],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'main.js', filename: "main.js",
path: outputPath, path: outputPath,
}, },
}, },

View File

@ -7,15 +7,13 @@ export default class ClusterMetricsFeatureExtension extends LensRendererExtensio
{ {
title: "Metrics Stack", title: "Metrics Stack",
components: { components: {
Description: () => { Description: () => (
return ( <span>
<span> Enable timeseries data visualization (Prometheus stack) for your cluster.
Enable timeseries data visualization (Prometheus stack) for your cluster. Install this only if you don&apos;t have existing Prometheus stack installed.
Install this only if you don't have existing Prometheus stack installed. You can see preview of manifests <a href="https://github.com/lensapp/lens/tree/master/extensions/lens-metrics/resources" rel="noreferrer" target="_blank">here</a>.
You can see preview of manifests <a href="https://github.com/lensapp/lens/tree/master/extensions/lens-metrics/resources" target="_blank">here</a>. </span>
</span> )
);
}
}, },
feature: new MetricsFeature() feature: new MetricsFeature()
} }

View File

@ -54,8 +54,8 @@ export class MetricsFeature extends ClusterFeature.Feature {
const storageClassApi = K8sApi.forCluster(cluster, K8sApi.StorageClass); const storageClassApi = K8sApi.forCluster(cluster, K8sApi.StorageClass);
const scs = await storageClassApi.list(); const scs = await storageClassApi.list();
this.templateContext.persistence.enabled = scs.some(sc => ( this.templateContext.persistence.enabled = scs.some(sc => (
sc.metadata?.annotations?.['storageclass.kubernetes.io/is-default-class'] === 'true' || sc.metadata?.annotations?.["storageclass.kubernetes.io/is-default-class"] === "true" ||
sc.metadata?.annotations?.['storageclass.beta.kubernetes.io/is-default-class'] === 'true' sc.metadata?.annotations?.["storageclass.beta.kubernetes.io/is-default-class"] === "true"
)); ));
super.applyResources(cluster, path.join(__dirname, "../resources/")); super.applyResources(cluster, path.join(__dirname, "../resources/"));

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,13 +23,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
node: { node: {
__dirname: false __dirname: false

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,13 +23,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
]; ];

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,13 +23,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
]; ];

View File

@ -1,8 +1,8 @@
const path = require('path'); const path = require("path");
module.exports = [ module.exports = [
{ {
entry: './main.ts', entry: "./main.ts",
context: __dirname, context: __dirname,
target: "electron-main", target: "electron-main",
mode: "production", mode: "production",
@ -10,7 +10,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -23,17 +23,17 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'main.js', filename: "main.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
{ {
entry: './renderer.tsx', entry: "./renderer.tsx",
context: __dirname, context: __dirname,
target: "electron-renderer", target: "electron-renderer",
mode: "production", mode: "production",
@ -41,7 +41,7 @@ module.exports = [
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
@ -55,13 +55,13 @@ module.exports = [
} }
], ],
resolve: { resolve: {
extensions: [ '.tsx', '.ts', '.js' ], extensions: [ ".tsx", ".ts", ".js" ],
}, },
output: { output: {
libraryTarget: "commonjs2", libraryTarget: "commonjs2",
globalObject: "this", globalObject: "this",
filename: 'renderer.js', filename: "renderer.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
}, },
}, },
]; ];

View File

@ -8,9 +8,6 @@ import { Application } from "spectron";
import * as util from "../helpers/utils"; import * as util from "../helpers/utils";
import { spawnSync } from "child_process"; import { spawnSync } from "child_process";
const describeif = (condition: boolean) => condition ? describe : describe.skip;
const itif = (condition: boolean) => condition ? it : it.skip;
jest.setTimeout(60000); jest.setTimeout(60000);
// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) // FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below)
@ -82,18 +79,18 @@ describe("Lens integration tests", () => {
}); });
it('shows "add cluster"', async () => { it('shows "add cluster"', async () => {
await app.electron.ipcRenderer.send('test-menu-item-click', "File", "Add Cluster"); await app.electron.ipcRenderer.send("test-menu-item-click", "File", "Add Cluster");
await app.client.waitUntilTextExists("h2", "Add Cluster"); await app.client.waitUntilTextExists("h2", "Add Cluster");
}); });
describe("preferences page", () => { describe("preferences page", () => {
it('shows "preferences"', async () => { it('shows "preferences"', async () => {
const appName: string = process.platform === "darwin" ? "Lens" : "File"; const appName: string = process.platform === "darwin" ? "Lens" : "File";
await app.electron.ipcRenderer.send('test-menu-item-click', appName, "Preferences"); await app.electron.ipcRenderer.send("test-menu-item-click", appName, "Preferences");
await app.client.waitUntilTextExists("h2", "Preferences"); await app.client.waitUntilTextExists("h2", "Preferences");
}); });
it('ensures helm repos', async () => { it("ensures helm repos", async () => {
await app.client.waitUntilTextExists("div.repos #message-bitnami", "bitnami"); // wait for the helm-cli to fetch the bitnami repo await app.client.waitUntilTextExists("div.repos #message-bitnami", "bitnami"); // wait for the helm-cli to fetch the bitnami repo
await app.client.click("#HelmRepoSelect"); // click the repo select to activate the drop-down await app.client.click("#HelmRepoSelect"); // click the repo select to activate the drop-down
await app.client.waitUntilTextExists("div.Select__option", ""); // wait for at least one option to appear (any text) await app.client.waitUntilTextExists("div.Select__option", ""); // wait for at least one option to appear (any text)
@ -101,12 +98,12 @@ describe("Lens integration tests", () => {
}); });
it.skip('quits Lens"', async () => { it.skip('quits Lens"', async () => {
await app.client.keys(['Meta', 'Q']); await app.client.keys(["Meta", "Q"]);
await app.client.keys('Meta'); await app.client.keys("Meta");
}); });
}); });
describeif(ready)("workspaces", () => { util.describeIf(ready)("workspaces", () => {
beforeAll(appStart, 20000); beforeAll(appStart, 20000);
afterAll(async () => { afterAll(async () => {
@ -115,27 +112,27 @@ describe("Lens integration tests", () => {
} }
}); });
it('creates new workspace', async () => { it("creates new workspace", async () => {
await clickWhatsNew(app); await clickWhatsNew(app);
await app.client.click('#current-workspace .Icon'); await app.client.click("#current-workspace .Icon");
await app.client.click('a[href="/workspaces"]'); await app.client.click('a[href="/workspaces"]');
await app.client.click('.Workspaces button.Button'); await app.client.click(".Workspaces button.Button");
await app.client.keys("test-workspace"); await app.client.keys("test-workspace");
await app.client.click('.Workspaces .Input.description input'); await app.client.click(".Workspaces .Input.description input");
await app.client.keys("test description"); await app.client.keys("test description");
await app.client.click('.Workspaces .workspace.editing .Icon'); await app.client.click(".Workspaces .workspace.editing .Icon");
await app.client.waitUntilTextExists(".workspace .name a", "test-workspace"); await app.client.waitUntilTextExists(".workspace .name a", "test-workspace");
}); });
it('adds cluster in default workspace', async () => { it("adds cluster in default workspace", async () => {
await addMinikubeCluster(app); await addMinikubeCluster(app);
await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started");
await app.client.waitForExist(`iframe[name="minikube"]`); await app.client.waitForExist(`iframe[name="minikube"]`);
await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active");
}); });
it('adds cluster in test-workspace', async () => { it("adds cluster in test-workspace", async () => {
await app.client.click('#current-workspace .Icon'); await app.client.click("#current-workspace .Icon");
await app.client.waitForVisible('.WorkspaceMenu li[title="test description"]'); await app.client.waitForVisible('.WorkspaceMenu li[title="test description"]');
await app.client.click('.WorkspaceMenu li[title="test description"]'); await app.client.click('.WorkspaceMenu li[title="test description"]');
await addMinikubeCluster(app); await addMinikubeCluster(app);
@ -143,10 +140,10 @@ describe("Lens integration tests", () => {
await app.client.waitForExist(`iframe[name="minikube"]`); await app.client.waitForExist(`iframe[name="minikube"]`);
}); });
it('checks if default workspace has active cluster', async () => { it("checks if default workspace has active cluster", async () => {
await app.client.click('#current-workspace .Icon'); await app.client.click("#current-workspace .Icon");
await app.client.waitForVisible('.WorkspaceMenu > li:first-of-type'); await app.client.waitForVisible(".WorkspaceMenu > li:first-of-type");
await app.client.click('.WorkspaceMenu > li:first-of-type'); await app.client.click(".WorkspaceMenu > li:first-of-type");
await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active");
}); });
}); });
@ -170,7 +167,7 @@ describe("Lens integration tests", () => {
await app.client.waitUntilTextExists("span.link-text", "Cluster"); await app.client.waitUntilTextExists("span.link-text", "Cluster");
}; };
describeif(ready)("cluster tests", () => { util.describeIf(ready)("cluster tests", () => {
let clusterAdded = false; let clusterAdded = false;
const addCluster = async () => { const addCluster = async () => {
@ -190,7 +187,7 @@ describe("Lens integration tests", () => {
} }
}); });
it('allows to add a cluster', async () => { it("allows to add a cluster", async () => {
await addCluster(); await addCluster();
clusterAdded = true; clusterAdded = true;
}); });
@ -515,7 +512,7 @@ describe("Lens integration tests", () => {
} }
}); });
it('shows default namespace', async () => { it("shows default namespace", async () => {
expect(clusterAdded).toBe(true); expect(clusterAdded).toBe(true);
await app.client.click('a[href="/namespaces"]'); await app.client.click('a[href="/namespaces"]');
await app.client.waitUntilTextExists("div.TableCell", "default"); await app.client.waitUntilTextExists("div.TableCell", "default");
@ -539,7 +536,7 @@ describe("Lens integration tests", () => {
await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods");
await app.client.click('a[href^="/pods"]'); await app.client.click('a[href^="/pods"]');
await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver");
await app.client.click('.Icon.new-dock-tab'); await app.client.click(".Icon.new-dock-tab");
await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource"); await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource");
await app.client.click("li.MenuItem.create-resource-tab"); await app.client.click("li.MenuItem.create-resource-tab");
await app.client.waitForVisible(".CreateResource div.ace_content"); await app.client.waitForVisible(".CreateResource div.ace_content");

View File

@ -6,6 +6,14 @@ const AppPaths: Partial<Record<NodeJS.Platform, string>> = {
"darwin": "./dist/mac/Lens.app/Contents/MacOS/Lens", "darwin": "./dist/mac/Lens.app/Contents/MacOS/Lens",
}; };
export function itIf(condition: boolean) {
return condition ? it : it.skip;
}
export function describeIf(condition: boolean) {
return condition ? describe : describe.skip;
}
export function setup(): Application { export function setup(): Application {
return new Application({ return new Application({
path: AppPaths[process.platform], // path to electron app path: AppPaths[process.platform], // path to electron app

View File

@ -338,6 +338,8 @@
"electron-builder": "^22.7.0", "electron-builder": "^22.7.0",
"electron-notarize": "^0.3.0", "electron-notarize": "^0.3.0",
"eslint": "^7.7.0", "eslint": "^7.7.0",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-unused-imports": "^1.0.0",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"flex.box": "^3.4.4", "flex.box": "^3.4.4",
"fork-ts-checker-webpack-plugin": "^5.0.0", "fork-ts-checker-webpack-plugin": "^5.0.0",

View File

@ -13,8 +13,8 @@ describe("empty config", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({}) "lens-cluster-store.json": JSON.stringify({})
} }
}; };
mockFs(mockOpts); mockFs(mockOpts);
@ -144,8 +144,8 @@ describe("config with existing clusters", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "99.99.99" version: "99.99.99"
@ -153,24 +153,24 @@ describe("config with existing clusters", () => {
}, },
clusters: [ clusters: [
{ {
id: 'cluster1', id: "cluster1",
kubeConfig: 'foo', kubeConfig: "foo",
contextName: 'foo', contextName: "foo",
preferences: { terminalCWD: '/foo' }, preferences: { terminalCWD: "/foo" },
workspace: 'default' workspace: "default"
}, },
{ {
id: 'cluster2', id: "cluster2",
kubeConfig: 'foo2', kubeConfig: "foo2",
contextName: 'foo2', contextName: "foo2",
preferences: { terminalCWD: '/foo2' } preferences: { terminalCWD: "/foo2" }
}, },
{ {
id: 'cluster3', id: "cluster3",
kubeConfig: 'foo', kubeConfig: "foo",
contextName: 'foo', contextName: "foo",
preferences: { terminalCWD: '/foo' }, preferences: { terminalCWD: "/foo" },
workspace: 'foo' workspace: "foo"
}, },
] ]
}) })
@ -186,27 +186,27 @@ describe("config with existing clusters", () => {
}); });
it("allows to retrieve a cluster", () => { it("allows to retrieve a cluster", () => {
const storedCluster = clusterStore.getById('cluster1'); const storedCluster = clusterStore.getById("cluster1");
expect(storedCluster.id).toBe('cluster1'); expect(storedCluster.id).toBe("cluster1");
expect(storedCluster.preferences.terminalCWD).toBe('/foo'); expect(storedCluster.preferences.terminalCWD).toBe("/foo");
}); });
it("allows to delete a cluster", () => { it("allows to delete a cluster", () => {
clusterStore.removeById('cluster2'); clusterStore.removeById("cluster2");
const storedCluster = clusterStore.getById('cluster1'); const storedCluster = clusterStore.getById("cluster1");
expect(storedCluster).toBeTruthy(); expect(storedCluster).toBeTruthy();
const storedCluster2 = clusterStore.getById('cluster2'); const storedCluster2 = clusterStore.getById("cluster2");
expect(storedCluster2).toBeUndefined(); expect(storedCluster2).toBeUndefined();
}); });
it("allows getting all of the clusters", async () => { it("allows getting all of the clusters", async () => {
const storedClusters = clusterStore.clustersList; const storedClusters = clusterStore.clustersList;
expect(storedClusters.length).toBe(3); expect(storedClusters.length).toBe(3);
expect(storedClusters[0].id).toBe('cluster1'); expect(storedClusters[0].id).toBe("cluster1");
expect(storedClusters[0].preferences.terminalCWD).toBe('/foo'); expect(storedClusters[0].preferences.terminalCWD).toBe("/foo");
expect(storedClusters[1].id).toBe('cluster2'); expect(storedClusters[1].id).toBe("cluster2");
expect(storedClusters[1].preferences.terminalCWD).toBe('/foo2'); expect(storedClusters[1].preferences.terminalCWD).toBe("/foo2");
expect(storedClusters[2].id).toBe('cluster3'); expect(storedClusters[2].id).toBe("cluster3");
}); });
}); });
@ -214,14 +214,14 @@ describe("pre 2.0 config with an existing cluster", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "1.0.0" version: "1.0.0"
} }
}, },
cluster1: 'kubeconfig content' cluster1: "kubeconfig content"
}) })
} }
}; };
@ -244,8 +244,8 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "2.4.1" version: "2.4.1"
@ -270,8 +270,8 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
const file = clusterStore.clustersList[0].kubeConfigPath; const file = clusterStore.clustersList[0].kubeConfigPath;
const config = fs.readFileSync(file, "utf8"); const config = fs.readFileSync(file, "utf8");
const kc = yaml.safeLoad(config); const kc = yaml.safeLoad(config);
expect(kc.users[0].user['auth-provider'].config['access-token']).toBe("should be string"); expect(kc.users[0].user["auth-provider"].config["access-token"]).toBe("should be string");
expect(kc.users[0].user['auth-provider'].config['expiry']).toBe("should be string"); expect(kc.users[0].user["auth-provider"].config["expiry"]).toBe("should be string");
}); });
}); });
@ -279,8 +279,8 @@ describe("pre 2.6.0 config with a cluster icon", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "2.4.1" version: "2.4.1"
@ -308,8 +308,8 @@ describe("pre 2.6.0 config with a cluster icon", () => {
it("moves the icon into preferences", async () => { it("moves the icon into preferences", async () => {
const storedClusterData = clusterStore.clustersList[0]; const storedClusterData = clusterStore.clustersList[0];
expect(storedClusterData.hasOwnProperty('icon')).toBe(false); expect(storedClusterData.hasOwnProperty("icon")).toBe(false);
expect(storedClusterData.preferences.hasOwnProperty('icon')).toBe(true); expect(storedClusterData.preferences.hasOwnProperty("icon")).toBe(true);
expect(storedClusterData.preferences.icon.startsWith("data:;base64,")).toBe(true); expect(storedClusterData.preferences.icon.startsWith("data:;base64,")).toBe(true);
}); });
}); });
@ -318,8 +318,8 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "2.6.6" version: "2.6.6"
@ -345,7 +345,7 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
it("adds cluster to default workspace", async () => { it("adds cluster to default workspace", async () => {
const storedClusterData = clusterStore.clustersList[0]; const storedClusterData = clusterStore.clustersList[0];
expect(storedClusterData.workspace).toBe('default'); expect(storedClusterData.workspace).toBe("default");
}); });
}); });
@ -353,8 +353,8 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
beforeEach(() => { beforeEach(() => {
ClusterStore.resetInstance(); ClusterStore.resetInstance();
const mockOpts = { const mockOpts = {
'tmp': { "tmp": {
'lens-cluster-store.json': JSON.stringify({ "lens-cluster-store.json": JSON.stringify({
__internal__: { __internal__: {
migrations: { migrations: {
version: "3.5.0" version: "3.5.0"
@ -362,9 +362,9 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
}, },
clusters: [ clusters: [
{ {
id: 'cluster1', id: "cluster1",
kubeConfig: 'kubeconfig content', kubeConfig: "kubeconfig content",
contextName: 'cluster', contextName: "cluster",
preferences: { preferences: {
icon: "store://icon_path", icon: "store://icon_path",
} }

View File

@ -3,9 +3,9 @@ import mockFs from "mock-fs";
jest.mock("electron", () => { jest.mock("electron", () => {
return { return {
app: { app: {
getVersion: () => '99.99.99', getVersion: () => "99.99.99",
getPath: () => 'tmp', getPath: () => "tmp",
getLocale: () => 'en' getLocale: () => "en"
} }
}; };
}); });
@ -18,7 +18,7 @@ describe("user store tests", () => {
describe("for an empty config", () => { describe("for an empty config", () => {
beforeEach(() => { beforeEach(() => {
UserStore.resetInstance(); UserStore.resetInstance();
mockFs({ tmp: { 'config.json': "{}" } }); mockFs({ tmp: { "config.json": "{}" } });
}); });
afterEach(() => { afterEach(() => {
@ -35,26 +35,26 @@ describe("user store tests", () => {
it("allows adding and listing seen contexts", () => { it("allows adding and listing seen contexts", () => {
const us = UserStore.getInstance<UserStore>(); const us = UserStore.getInstance<UserStore>();
us.seenContexts.add('foo'); us.seenContexts.add("foo");
expect(us.seenContexts.size).toBe(1); expect(us.seenContexts.size).toBe(1);
us.seenContexts.add('foo'); us.seenContexts.add("foo");
us.seenContexts.add('bar'); us.seenContexts.add("bar");
expect(us.seenContexts.size).toBe(2); // check 'foo' isn't added twice expect(us.seenContexts.size).toBe(2); // check 'foo' isn't added twice
expect(us.seenContexts.has('foo')).toBe(true); expect(us.seenContexts.has("foo")).toBe(true);
expect(us.seenContexts.has('bar')).toBe(true); expect(us.seenContexts.has("bar")).toBe(true);
}); });
it("allows setting and getting preferences", () => { it("allows setting and getting preferences", () => {
const us = UserStore.getInstance<UserStore>(); const us = UserStore.getInstance<UserStore>();
us.preferences.httpsProxy = 'abcd://defg'; us.preferences.httpsProxy = "abcd://defg";
expect(us.preferences.httpsProxy).toBe('abcd://defg'); expect(us.preferences.httpsProxy).toBe("abcd://defg");
expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme); expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme);
us.preferences.colorTheme = "light"; us.preferences.colorTheme = "light";
expect(us.preferences.colorTheme).toBe('light'); expect(us.preferences.colorTheme).toBe("light");
}); });
it("correctly resets theme to default value", async () => { it("correctly resets theme to default value", async () => {
@ -80,11 +80,11 @@ describe("user store tests", () => {
beforeEach(() => { beforeEach(() => {
UserStore.resetInstance(); UserStore.resetInstance();
mockFs({ mockFs({
'tmp': { "tmp": {
'config.json': JSON.stringify({ "config.json": JSON.stringify({
user: { username: 'foobar' }, user: { username: "foobar" },
preferences: { colorTheme: 'light' }, preferences: { colorTheme: "light" },
lastSeenAppVersion: '1.2.3' lastSeenAppVersion: "1.2.3"
}) })
} }
}); });
@ -97,7 +97,7 @@ describe("user store tests", () => {
it("sets last seen app version to 0.0.0", () => { it("sets last seen app version to 0.0.0", () => {
const us = UserStore.getInstance<UserStore>(); const us = UserStore.getInstance<UserStore>();
expect(us.lastSeenAppVersion).toBe('0.0.0'); expect(us.lastSeenAppVersion).toBe("0.0.0");
}); });
}); });
}); });

View File

@ -3,9 +3,9 @@ import mockFs from "mock-fs";
jest.mock("electron", () => { jest.mock("electron", () => {
return { return {
app: { app: {
getVersion: () => '99.99.99', getVersion: () => "99.99.99",
getPath: () => 'tmp', getPath: () => "tmp",
getLocale: () => 'en' getLocale: () => "en"
} }
}; };
}); });
@ -16,7 +16,7 @@ describe("workspace store tests", () => {
describe("for an empty config", () => { describe("for an empty config", () => {
beforeEach(async () => { beforeEach(async () => {
WorkspaceStore.resetInstance(); WorkspaceStore.resetInstance();
mockFs({ tmp: { 'lens-workspace-store.json': "{}" } }); mockFs({ tmp: { "lens-workspace-store.json": "{}" } });
await WorkspaceStore.getInstance<WorkspaceStore>().load(); await WorkspaceStore.getInstance<WorkspaceStore>().load();
}); });
@ -146,7 +146,7 @@ describe("workspace store tests", () => {
WorkspaceStore.resetInstance(); WorkspaceStore.resetInstance();
mockFs({ mockFs({
tmp: { tmp: {
'lens-workspace-store.json': JSON.stringify({ "lens-workspace-store.json": JSON.stringify({
currentWorkspace: "abc", currentWorkspace: "abc",
workspaces: [{ workspaces: [{
id: "abc", id: "abc",

View File

@ -2,7 +2,7 @@ import path from "path";
import Config from "conf"; import Config from "conf";
import { Options as ConfOptions } from "conf/dist/source/types"; import { Options as ConfOptions } from "conf/dist/source/types";
import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron"; import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron";
import { action, IReactionOptions, observable, reaction, runInAction, toJS, when } from "mobx"; import { IReactionOptions, observable, reaction, runInAction, when } from "mobx";
import Singleton from "./utils/singleton"; import Singleton from "./utils/singleton";
import { getAppVersion } from "./utils/app-version"; import { getAppVersion } from "./utils/app-version";
import logger from "../main/logger"; import logger from "../main/logger";

View File

@ -16,7 +16,7 @@ export async function requestMain(channel: string, ...args: any[]) {
async function getSubFrames(): Promise<number[]> { async function getSubFrames(): Promise<number[]> {
const subFrames: number[] = []; const subFrames: number[] = [];
clusterFrameMap.forEach((frameId, _) => { clusterFrameMap.forEach(frameId => {
subFrames.push(frameId); subFrames.push(frameId);
}); });
return subFrames; return subFrames;

View File

@ -7,7 +7,7 @@ import logger from "../main/logger";
import commandExists from "command-exists"; import commandExists from "command-exists";
import { ExecValidationNotFoundError } from "./custom-errors"; import { ExecValidationNotFoundError } from "./custom-errors";
export const kubeConfigDefaultPath = path.join(os.homedir(), '.kube', 'config'); export const kubeConfigDefaultPath = path.join(os.homedir(), ".kube", "config");
function resolveTilde(filePath: string) { function resolveTilde(filePath: string) {
if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) { if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) {
@ -78,15 +78,15 @@ export function dumpConfigYaml(kubeConfig: Partial<KubeConfig>): string {
apiVersion: "v1", apiVersion: "v1",
kind: "Config", kind: "Config",
preferences: {}, preferences: {},
'current-context': kubeConfig.currentContext, "current-context": kubeConfig.currentContext,
clusters: kubeConfig.clusters.map(cluster => { clusters: kubeConfig.clusters.map(cluster => {
return { return {
name: cluster.name, name: cluster.name,
cluster: { cluster: {
'certificate-authority-data': cluster.caData, "certificate-authority-data": cluster.caData,
'certificate-authority': cluster.caFile, "certificate-authority": cluster.caFile,
server: cluster.server, server: cluster.server,
'insecure-skip-tls-verify': cluster.skipTLSVerify "insecure-skip-tls-verify": cluster.skipTLSVerify
} }
}; };
}), }),
@ -104,11 +104,11 @@ export function dumpConfigYaml(kubeConfig: Partial<KubeConfig>): string {
return { return {
name: user.name, name: user.name,
user: { user: {
'client-certificate-data': user.certData, "client-certificate-data": user.certData,
'client-certificate': user.certFile, "client-certificate": user.certFile,
'client-key-data': user.keyData, "client-key-data": user.keyData,
'client-key': user.keyFile, "client-key": user.keyFile,
'auth-provider': user.authProvider, "auth-provider": user.authProvider,
exec: user.exec, exec: user.exec,
token: user.token, token: user.token,
username: user.username, username: user.username,

View File

@ -1,5 +1,5 @@
import type { ThemeId } from "../renderer/theme.store"; import type { ThemeId } from "../renderer/theme.store";
import { app, remote } from 'electron'; import { app, remote } from "electron";
import semver from "semver"; import semver from "semver";
import { readFile } from "fs-extra"; import { readFile } from "fs-extra";
import { action, observable, reaction, toJS } from "mobx"; import { action, observable, reaction, toJS } from "mobx";
@ -9,7 +9,7 @@ import { getAppVersion } from "./utils/app-version";
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers"; import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
import { appEventBus } from "./event-bus"; import { appEventBus } from "./event-bus";
import logger from "../main/logger"; import logger from "../main/logger";
import path from 'path'; import path from "path";
export interface UserStoreModel { export interface UserStoreModel {
kubeConfigPath: string; kubeConfigPath: string;

View File

@ -2,7 +2,7 @@
export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Promise<T>, timeout = 0): (...args: F) => Promise<T> { export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Promise<T>, timeout = 0): (...args: F) => Promise<T> {
let timer: NodeJS.Timeout; let timer: NodeJS.Timeout;
return (...params: any[]) => new Promise((resolve, reject) => { return (...params: any[]) => new Promise(resolve => {
clearTimeout(timer); clearTimeout(timer);
timer = global.setTimeout(() => resolve(func.apply(this, params)), timeout); timer = global.setTimeout(() => resolve(func.apply(this, params)), timeout);
}); });

View File

@ -1,5 +1,5 @@
// Helper to sanitize / escape special chars for passing to RegExp-constructor // Helper to sanitize / escape special chars for passing to RegExp-constructor
export function escapeRegExp(str: string) { export function escapeRegExp(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
} }

View File

@ -8,7 +8,7 @@ jest.mock(
"electron", "electron",
() => ({ () => ({
ipcRenderer: { ipcRenderer: {
invoke: jest.fn(async (channel: string, ...args: any[]) => { invoke: jest.fn(async (channel: string) => {
if (channel === "extensions:loaded") { if (channel === "extensions:loaded") {
return [ return [
[ [

View File

@ -112,7 +112,7 @@ export abstract class ClusterFeature {
fs.readdirSync(folderPath).forEach(filename => { fs.readdirSync(folderPath).forEach(filename => {
const file = path.join(folderPath, filename); const file = path.join(folderPath, filename);
const raw = fs.readFileSync(file); const raw = fs.readFileSync(file);
if (filename.endsWith('.hb')) { if (filename.endsWith(".hb")) {
const template = hb.compile(raw.toString()); const template = hb.compile(raw.toString());
resources.push(template(this.templateContext)); resources.push(template(this.templateContext));
} else { } else {

View File

@ -162,7 +162,7 @@ export class ExtensionDiscovery {
if (path.relative(this.localFolderPath, filePath) === extensionFolderName) { if (path.relative(this.localFolderPath, filePath) === extensionFolderName) {
const extensionName: string | undefined = Object const extensionName: string | undefined = Object
.entries(this.packagesJson.dependencies) .entries(this.packagesJson.dependencies)
.find(([_name, extensionFolder]) => filePath === extensionFolder)?.[0]; .find(([, extensionFolder]) => filePath === extensionFolder)?.[0];
if (extensionName !== undefined) { if (extensionName !== undefined) {
delete this.packagesJson.dependencies[extensionName]; delete this.packagesJson.dependencies[extensionName];
@ -244,7 +244,6 @@ export class ExtensionDiscovery {
isBundled?: boolean; isBundled?: boolean;
} = {}): Promise<InstalledExtension | null> { } = {}): Promise<InstalledExtension | null> {
let manifestJson: LensExtensionManifest; let manifestJson: LensExtensionManifest;
let isEnabled: boolean;
try { try {
// check manifest file for existence // check manifest file for existence

View File

@ -1,4 +1,4 @@
import AwaitLock from 'await-lock'; import AwaitLock from "await-lock";
import child_process from "child_process"; import child_process from "child_process";
import fs from "fs-extra"; import fs from "fs-extra";
import path from "path"; import path from "path";
@ -27,7 +27,7 @@ export class ExtensionInstaller {
} }
get npmPath() { get npmPath() {
return __non_webpack_require__.resolve('npm/bin/npm-cli'); return __non_webpack_require__.resolve("npm/bin/npm-cli");
} }
installDependencies(): Promise<void> { installDependencies(): Promise<void> {

View File

@ -21,13 +21,9 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
@computed @computed
get enabledExtensions() { get enabledExtensions() {
const extensions: string[] = []; return Array.from(this.state.values())
this.state.forEach((state, id) => { .filter(({enabled}) => enabled)
if (state.enabled) { .map(({name}) => name);
extensions.push(state.name);
}
});
return extensions;
} }
protected state = observable.map<LensExtensionId, LensExtensionState>(); protected state = observable.map<LensExtensionId, LensExtensionState>();

View File

@ -1,5 +1,4 @@
import type { MenuRegistration } from "./registries/menu-registry"; import type { MenuRegistration } from "./registries/menu-registry";
import { observable } from "mobx";
import { LensExtension } from "./lens-extension"; import { LensExtension } from "./lens-extension";
import { WindowManager } from "../main/window-manager"; import { WindowManager } from "../main/window-manager";
import { getExtensionPageUrl } from "./registries/page-registry"; import { getExtensionPageUrl } from "./registries/page-registry";

View File

@ -1,6 +1,5 @@
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"; import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries";
import type { Cluster } from "../main/cluster"; import type { Cluster } from "../main/cluster";
import { observable } from "mobx";
import { LensExtension } from "./lens-extension"; import { LensExtension } from "./lens-extension";
import { getExtensionPageUrl } from "./registries/page-registry"; import { getExtensionPageUrl } from "./registries/page-registry";
@ -30,6 +29,7 @@ export class LensRendererExtension extends LensExtension {
/** /**
* Defines if extension is enabled for a given cluster. Defaults to `true`. * Defines if extension is enabled for a given cluster. Defaults to `true`.
*/ */
// eslint-disable-next-line unused-imports/no-unused-vars-ts
async isEnabledForCluster(cluster: Cluster): Promise<Boolean> { async isEnabledForCluster(cluster: Cluster): Promise<Boolean> {
return true; return true;
} }

View File

@ -1,4 +1,4 @@
import { getExtensionPageUrl, globalPageRegistry, PageRegistration } from "../page-registry"; import { getExtensionPageUrl, globalPageRegistry } from "../page-registry";
import { LensExtension } from "../../lens-extension"; import { LensExtension } from "../../lens-extension";
import React from "react"; import React from "react";
@ -53,18 +53,18 @@ describe("globalPageRegistry", () => {
{ {
id: "test-page", id: "test-page",
components: { components: {
Page: () => React.createElement('Text') Page: () => React.createElement("Text")
} }
}, },
{ {
id: "another-page", id: "another-page",
components: { components: {
Page: () => React.createElement('Text') Page: () => React.createElement("Text")
}, },
}, },
{ {
components: { components: {
Page: () => React.createElement('Default') Page: () => React.createElement("Default")
} }
}, },
], ext); ], ext);

View File

@ -1,7 +1,7 @@
// Extensions-api -> Register page menu items // Extensions-api -> Register page menu items
import type { IconProps } from "../../renderer/components/icon"; import type { IconProps } from "../../renderer/components/icon";
import type React from "react"; import type React from "react";
import { action, computed } from "mobx"; import { action } from "mobx";
import { BaseRegistry } from "./base-registry"; import { BaseRegistry } from "./base-registry";
import { LensExtension } from "../lens-extension"; import { LensExtension } from "../lens-extension";
import { RegisteredPage } from "./page-registry"; import { RegisteredPage } from "./page-registry";

View File

@ -146,7 +146,7 @@ describe("create clusters", () => {
} }
})); }));
mockedRequest.mockImplementationOnce(((uri: any, _options: any) => { mockedRequest.mockImplementationOnce(((uri: any) => {
expect(uri).toBe(`http://localhost:${port}/api-kube/version`); expect(uri).toBe(`http://localhost:${port}/api-kube/version`);
return Promise.resolve({ gitVersion: "1.2.3" }); return Promise.resolve({ gitVersion: "1.2.3" });
}) as any); }) as any);

View File

@ -31,10 +31,10 @@ import { Cluster } from "../cluster";
import { KubeAuthProxy } from "../kube-auth-proxy"; import { KubeAuthProxy } from "../kube-auth-proxy";
import { getFreePort } from "../port"; import { getFreePort } from "../port";
import { broadcastMessage } from "../../common/ipc"; import { broadcastMessage } from "../../common/ipc";
import { ChildProcess, spawn, SpawnOptions } from "child_process"; import { ChildProcess, spawn } from "child_process";
import { bundledKubectlPath, Kubectl } from "../kubectl"; import { bundledKubectlPath, Kubectl } from "../kubectl";
import { mock, MockProxy } from 'jest-mock-extended'; import { mock, MockProxy } from "jest-mock-extended";
import { waitUntilUsed } from 'tcp-port-used'; import { waitUntilUsed } from "tcp-port-used";
import { Readable } from "stream"; import { Readable } from "stream";
const mockBroadcastIpc = broadcastMessage as jest.MockedFunction<typeof broadcastMessage>; const mockBroadcastIpc = broadcastMessage as jest.MockedFunction<typeof broadcastMessage>;
@ -81,7 +81,7 @@ describe("kube auth proxy tests", () => {
listeners[`stdout/${event}`] = listener; listeners[`stdout/${event}`] = listener;
return mockedCP.stdout; return mockedCP.stdout;
}); });
mockSpawn.mockImplementationOnce((command: string, args: readonly string[], options: SpawnOptions): ChildProcess => { mockSpawn.mockImplementationOnce((command: string): ChildProcess => {
expect(command).toBe(bundledKubectlPath()); expect(command).toBe(bundledKubectlPath());
return mockedCP; return mockedCP;
}); });

View File

@ -9,7 +9,7 @@ import { ContextHandler } from "./context-handler";
import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"; import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node";
import { Kubectl } from "./kubectl"; import { Kubectl } from "./kubectl";
import { KubeconfigManager } from "./kubeconfig-manager"; import { KubeconfigManager } from "./kubeconfig-manager";
import { getNodeWarningConditions, loadConfig, podHasIssues } from "../common/kube-helpers"; import { loadConfig } from "../common/kube-helpers";
import request, { RequestPromiseOptions } from "request-promise-native"; import request, { RequestPromiseOptions } from "request-promise-native";
import { apiResources } from "../common/rbac"; import { apiResources } from "../common/rbac";
import logger from "./logger"; import logger from "./logger";

View File

@ -3,8 +3,8 @@
* The dependency is not bundled to the production build. * The dependency is not bundled to the production build.
*/ */
export const installDeveloperTools = async () => { export const installDeveloperTools = async () => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
const { default: devToolsInstaller, REACT_DEVELOPER_TOOLS } = await import('electron-devtools-installer'); const { default: devToolsInstaller, REACT_DEVELOPER_TOOLS } = await import("electron-devtools-installer");
return devToolsInstaller([REACT_DEVELOPER_TOOLS]); return devToolsInstaller([REACT_DEVELOPER_TOOLS]);
} }

View File

@ -11,7 +11,7 @@ export function exitApp() {
appEventBus.emit({ name: "service", action: "close" }); appEventBus.emit({ name: "service", action: "close" });
windowManager.hide(); windowManager.hide();
clusterManager.stop(); clusterManager.stop();
logger.info('SERVICE:QUIT'); logger.info("SERVICE:QUIT");
setTimeout(() => { setTimeout(() => {
app.exit(); app.exit();
}, 1000); }, 1000);

View File

@ -36,10 +36,10 @@ export class HelmChartManager {
public async getReadme(name: string, version = "") { public async getReadme(name: string, version = "") {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
if(version && version != "") { if(version && version != "") {
const { stdout, stderr} = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} else { } else {
const { stdout, stderr} = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} }
} }
@ -47,11 +47,11 @@ export class HelmChartManager {
public async getValues(name: string, version = "") { public async getValues(name: string, version = "") {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
if(version && version != "") { if(version && version != "") {
const { stdout, stderr} = await promiseExec(`"${helm}" show values ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" show values ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} else { } else {
const { stdout, stderr} = await promiseExec(`"${helm}" show values ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" show values ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} }
@ -59,12 +59,12 @@ export class HelmChartManager {
protected async cachedYaml(): Promise<CachedYaml> { protected async cachedYaml(): Promise<CachedYaml> {
if (!(this.repo.name in this.cache)) { if (!(this.repo.name in this.cache)) {
const cacheFile = await fs.promises.readFile(this.repo.cacheFilePath, 'utf-8'); const cacheFile = await fs.promises.readFile(this.repo.cacheFilePath, "utf-8");
const data = yaml.safeLoad(cacheFile); const data = yaml.safeLoad(cacheFile);
for(const key in data["entries"]) { for(const key in data["entries"]) {
data["entries"][key].forEach((version: any) => { data["entries"][key].forEach((version: any) => {
version['repo'] = this.repo.name; version["repo"] = this.repo.name;
version['created'] = Date.parse(version.created).toString(); version["created"] = Date.parse(version.created).toString();
}); });
} }
this.cache[this.repo.name] = Buffer.from(JSON.stringify(data)); this.cache[this.repo.name] = Buffer.from(JSON.stringify(data));

View File

@ -34,8 +34,8 @@ export class HelmReleaseManager {
generateName = "--generate-name"; generateName = "--generate-name";
name = ""; name = "";
} }
const { stdout, stderr } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`).catch((error) => { throw(error.stderr);});
const releaseName = stdout.split("\n")[0].split(' ')[1].trim(); const releaseName = stdout.split("\n")[0].split(" ")[1].trim();
return { return {
log: stdout, log: stdout,
release: { release: {
@ -54,7 +54,7 @@ export class HelmReleaseManager {
await fs.promises.writeFile(fileName, yaml.safeDump(values)); await fs.promises.writeFile(fileName, yaml.safeDump(values));
try { try {
const { stdout, stderr } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);});
return { return {
log: stdout, log: stdout,
release: this.getRelease(name, namespace, cluster) release: this.getRelease(name, namespace, cluster)
@ -66,7 +66,7 @@ export class HelmReleaseManager {
public async getRelease(name: string, namespace: string, cluster: Cluster) { public async getRelease(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const {stdout, stderr} = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);});
const release = JSON.parse(stdout); const release = JSON.parse(stdout);
release.resources = await this.getResources(name, namespace, cluster); release.resources = await this.getResources(name, namespace, cluster);
return release; return release;
@ -74,26 +74,26 @@ export class HelmReleaseManager {
public async deleteRelease(name: string, namespace: string, pathToKubeconfig: string) { public async deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout, stderr } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} }
public async getValues(name: string, namespace: string, pathToKubeconfig: string) { public async getValues(name: string, namespace: string, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout, stderr } = await promiseExec(`"${helm}" get values ${name} --all --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);}); const { stdout, } = await promiseExec(`"${helm}" get values ${name} --all --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} }
public async getHistory(name: string, namespace: string, pathToKubeconfig: string) { public async getHistory(name: string, namespace: string, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const {stdout, stderr} = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return JSON.parse(stdout); return JSON.parse(stdout);
} }
public async rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) { public async rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const {stdout, stderr} = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);}); const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout; return stdout;
} }
@ -101,7 +101,7 @@ export class HelmReleaseManager {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const kubectl = await cluster.kubeCtl.getPath(); const kubectl = await cluster.kubeCtl.getPath();
const pathToKubeconfig = cluster.getProxyKubeconfigPath(); const pathToKubeconfig = cluster.getProxyKubeconfigPath();
const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch((error) => { const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch(() => {
return { stdout: JSON.stringify({items: []})}; return { stdout: JSON.stringify({items: []})};
}); });
return stdout; return stdout;

View File

@ -77,7 +77,7 @@ export class HelmRepoManager extends Singleton {
} }
try { try {
const repoConfigFile = this.helmEnv.HELM_REPOSITORY_CONFIG; const repoConfigFile = this.helmEnv.HELM_REPOSITORY_CONFIG;
const { repositories }: HelmRepoConfig = await readFile(repoConfigFile, 'utf8') const { repositories }: HelmRepoConfig = await readFile(repoConfigFile, "utf8")
.then((yamlContent: string) => yaml.safeLoad(yamlContent)) .then((yamlContent: string) => yaml.safeLoad(yamlContent))
.catch(() => ({ .catch(() => ({
repositories: [] repositories: []
@ -121,7 +121,7 @@ export class HelmRepoManager extends Singleton {
public async removeRepo({ name, url }: HelmRepo): Promise<string> { public async removeRepo({ name, url }: HelmRepo): Promise<string> {
logger.info(`[HELM]: removing repo "${name}" from ${url}`); logger.info(`[HELM]: removing repo "${name}" from ${url}`);
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout, stderr } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => { const { stdout } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => {
throw(error.stderr); throw(error.stderr);
}); });
return stdout; return stdout;

View File

@ -85,7 +85,7 @@ class HelmService {
for (const key in entries) { for (const key in entries) {
entries[key] = entries[key].filter((entry: any) => { entries[key] = entries[key].filter((entry: any) => {
if (Array.isArray(entry)) { if (Array.isArray(entry)) {
return entry[0]['deprecated'] != true; return entry[0]["deprecated"] != true;
} }
return entry["deprecated"] != true; return entry["deprecated"] != true;
}); });

View File

@ -77,6 +77,7 @@ app.on("ready", async () => {
// run proxy // run proxy
try { try {
// eslint-disable-next-line unused-imports/no-unused-vars-ts
proxyServer = LensProxy.create(proxyPort, clusterManager); proxyServer = LensProxy.create(proxyPort, clusterManager);
} catch (error) { } catch (error) {
logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error.message}`); logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error.message}`);
@ -108,7 +109,7 @@ app.on("ready", async () => {
}); });
app.on("activate", (event, hasVisibleWindows) => { app.on("activate", (event, hasVisibleWindows) => {
logger.info('APP:ACTIVATE', { hasVisibleWindows }); logger.info("APP:ACTIVATE", { hasVisibleWindows });
if (!hasVisibleWindows) { if (!hasVisibleWindows) {
windowManager.initMainWindow(); windowManager.initMainWindow();
} }
@ -116,7 +117,7 @@ app.on("activate", (event, hasVisibleWindows) => {
// Quit app on Cmd+Q (MacOS) // Quit app on Cmd+Q (MacOS)
app.on("will-quit", (event) => { app.on("will-quit", (event) => {
logger.info('APP:QUIT'); logger.info("APP:QUIT");
appEventBus.emit({name: "app", action: "close"}); appEventBus.emit({name: "app", action: "close"});
event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.) event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.)
clusterManager?.stop(); // close cluster connections clusterManager?.stop(); // close cluster connections

View File

@ -60,7 +60,7 @@ export class KubeAuthProxy {
this.exit(); this.exit();
}); });
this.proxyProcess.stdout.on('data', (data) => { this.proxyProcess.stdout.on("data", (data) => {
let logItem = data.toString(); let logItem = data.toString();
if (logItem.startsWith("Starting to serve on")) { if (logItem.startsWith("Starting to serve on")) {
logItem = "Authentication proxy started\n"; logItem = "Authentication proxy started\n";
@ -68,7 +68,7 @@ export class KubeAuthProxy {
this.sendIpcLogMessage({ data: logItem }); this.sendIpcLogMessage({ data: logItem });
}); });
this.proxyProcess.stderr.on('data', (data) => { this.proxyProcess.stderr.on("data", (data) => {
this.lastError = this.parseError(data.toString()); this.lastError = this.parseError(data.toString());
this.sendIpcLogMessage({ data: data.toString(), error: true }); this.sendIpcLogMessage({ data: data.toString(), error: true });
}); });

View File

@ -41,7 +41,7 @@ export class KubeconfigManager {
* This way any user of the config does not need to know anything about the auth etc. details. * This way any user of the config does not need to know anything about the auth etc. details.
*/ */
protected async createProxyKubeconfig(): Promise<string> { protected async createProxyKubeconfig(): Promise<string> {
const { configDir, cluster, contextHandler } = this; const { configDir, cluster } = this;
const { contextName, kubeConfigPath, id } = cluster; const { contextName, kubeConfigPath, id } = cluster;
const tempFile = path.join(configDir, `kubeconfig-${id}`); const tempFile = path.join(configDir, `kubeconfig-${id}`);
const kubeConfig = loadConfig(kubeConfigPath); const kubeConfig = loadConfig(kubeConfigPath);
@ -81,7 +81,7 @@ export class KubeconfigManager {
return; return;
} }
logger.info('Deleting temporary kubeconfig: ' + this.tempFile); logger.info("Deleting temporary kubeconfig: " + this.tempFile);
await fs.unlink(this.tempFile); await fs.unlink(this.tempFile);
this.tempFile = undefined; this.tempFile = undefined;
} }

View File

@ -123,6 +123,10 @@ export class Kubectl {
} }
public async getPath(bundled = false): Promise<string> { public async getPath(bundled = false): Promise<string> {
if (bundled) {
return this.getBundledPath();
}
if (userStore.preferences?.downloadKubectlBinaries === false) { if (userStore.preferences?.downloadKubectlBinaries === false) {
return this.getPathFromPreferences(); return this.getPathFromPreferences();
} }
@ -167,7 +171,7 @@ export class Kubectl {
return true; return true;
} }
let version: string = output.clientVersion.gitVersion; let version: string = output.clientVersion.gitVersion;
if (version[0] === 'v') { if (version[0] === "v") {
version = version.slice(1); version = version.slice(1);
} }
if (version === this.kubectlVersion) { if (version === this.kubectlVersion) {
@ -274,7 +278,7 @@ export class Kubectl {
const kubectlPath = userStore.preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences()); const kubectlPath = userStore.preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences());
const helmPath = helmCli.getBinaryDir(); const helmPath = helmCli.getBinaryDir();
const fsPromises = fs.promises; const fsPromises = fs.promises;
const bashScriptPath = path.join(this.dirname, '.bash_set_path'); const bashScriptPath = path.join(this.dirname, ".bash_set_path");
let bashScript = "" + initScriptVersionString; let bashScript = "" + initScriptVersionString;
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"; bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n";
@ -297,7 +301,7 @@ export class Kubectl {
bashScript += "unset tempkubeconfig\n"; bashScript += "unset tempkubeconfig\n";
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 }); await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 });
const zshScriptPath = path.join(this.dirname, '.zlogin'); const zshScriptPath = path.join(this.dirname, ".zlogin");
let zshScript = "" + initScriptVersionString; let zshScript = "" + initScriptVersionString;

View File

@ -121,12 +121,12 @@ export class LensBinary {
} }
protected async untarBinary() { protected async untarBinary() {
return new Promise<void>((resolve, reject) => { return new Promise<void>(resolve => {
this.logger.debug(`Extracting ${this.originalBinaryName} binary`); this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
tar.x({ tar.x({
file: this.tarPath, file: this.tarPath,
cwd: this.dirname cwd: this.dirname
}).then((_ => { }).then((() => {
resolve(); resolve();
})); }));
}); });

View File

@ -88,23 +88,23 @@ export class LensProxy {
proxySocket.setTimeout(0); proxySocket.setTimeout(0);
socket.setTimeout(0); socket.setTimeout(0);
proxySocket.on('data', function (chunk) { proxySocket.on("data", function (chunk) {
socket.write(chunk); socket.write(chunk);
}); });
proxySocket.on('end', function () { proxySocket.on("end", function () {
socket.end(); socket.end();
}); });
proxySocket.on('error', function (err) { proxySocket.on("error", function () {
socket.write("HTTP/" + req.httpVersion + " 500 Connection error\r\n\r\n"); socket.write("HTTP/" + req.httpVersion + " 500 Connection error\r\n\r\n");
socket.end(); socket.end();
}); });
socket.on('data', function (chunk) { socket.on("data", function (chunk) {
proxySocket.write(chunk); proxySocket.write(chunk);
}); });
socket.on('end', function () { socket.on("end", function () {
proxySocket.end(); proxySocket.end();
}); });
socket.on('error', function () { socket.on("error", function () {
proxySocket.end(); proxySocket.end();
}); });
} }

View File

@ -65,31 +65,31 @@ export function buildMenu(windowManager: WindowManager) {
showAbout(browserWindow); showAbout(browserWindow);
} }
}, },
{ type: 'separator' }, { type: "separator" },
{ {
label: 'Preferences', label: "Preferences",
accelerator: 'CmdOrCtrl+,', accelerator: "CmdOrCtrl+,",
click() { click() {
navigate(preferencesURL()); navigate(preferencesURL());
} }
}, },
{ {
label: 'Extensions', label: "Extensions",
accelerator: 'CmdOrCtrl+Shift+E', accelerator: "CmdOrCtrl+Shift+E",
click() { click() {
navigate(extensionsURL()); navigate(extensionsURL());
} }
}, },
{ type: 'separator' }, { type: "separator" },
{ role: 'services' }, { role: "services" },
{ type: 'separator' }, { type: "separator" },
{ role: 'hide' }, { role: "hide" },
{ role: 'hideOthers' }, { role: "hideOthers" },
{ role: 'unhide' }, { role: "unhide" },
{ type: 'separator' }, { type: "separator" },
{ {
label: 'Quit', label: "Quit",
accelerator: 'Cmd+Q', accelerator: "Cmd+Q",
click() { click() {
exitApp(); exitApp();
} }
@ -101,16 +101,16 @@ export function buildMenu(windowManager: WindowManager) {
label: "File", label: "File",
submenu: [ submenu: [
{ {
label: 'Add Cluster', label: "Add Cluster",
accelerator: 'CmdOrCtrl+Shift+A', accelerator: "CmdOrCtrl+Shift+A",
click() { click() {
navigate(addClusterURL()); navigate(addClusterURL());
} }
}, },
...activeClusterOnly([ ...activeClusterOnly([
{ {
label: 'Cluster Settings', label: "Cluster Settings",
accelerator: 'CmdOrCtrl+Shift+S', accelerator: "CmdOrCtrl+Shift+S",
click() { click() {
navigate(clusterSettingsURL({ navigate(clusterSettingsURL({
params: { params: {
@ -121,32 +121,32 @@ export function buildMenu(windowManager: WindowManager) {
} }
]), ]),
...ignoreOnMac([ ...ignoreOnMac([
{ type: 'separator' }, { type: "separator" },
{ {
label: 'Preferences', label: "Preferences",
accelerator: 'Ctrl+,', accelerator: "Ctrl+,",
click() { click() {
navigate(preferencesURL()); navigate(preferencesURL());
} }
}, },
{ {
label: 'Extensions', label: "Extensions",
accelerator: 'Ctrl+Shift+E', accelerator: "Ctrl+Shift+E",
click() { click() {
navigate(extensionsURL()); navigate(extensionsURL());
} }
} }
]), ]),
{ type: 'separator' }, { type: "separator" },
{ {
role: 'close', role: "close",
label: "Close Window" label: "Close Window"
}, },
...ignoreOnMac([ ...ignoreOnMac([
{ type: 'separator' }, { type: "separator" },
{ {
label: 'Exit', label: "Exit",
accelerator: 'Alt+F4', accelerator: "Alt+F4",
click() { click() {
exitApp(); exitApp();
} }
@ -156,56 +156,56 @@ export function buildMenu(windowManager: WindowManager) {
}; };
const editMenu: MenuItemConstructorOptions = { const editMenu: MenuItemConstructorOptions = {
label: 'Edit', label: "Edit",
submenu: [ submenu: [
{ role: 'undo' }, { role: "undo" },
{ role: 'redo' }, { role: "redo" },
{ type: 'separator' }, { type: "separator" },
{ role: 'cut' }, { role: "cut" },
{ role: 'copy' }, { role: "copy" },
{ role: 'paste' }, { role: "paste" },
{ role: 'delete' }, { role: "delete" },
{ type: 'separator' }, { type: "separator" },
{ role: 'selectAll' }, { role: "selectAll" },
] ]
}; };
const viewMenu: MenuItemConstructorOptions = { const viewMenu: MenuItemConstructorOptions = {
label: 'View', label: "View",
submenu: [ submenu: [
{ {
label: 'Back', label: "Back",
accelerator: 'CmdOrCtrl+[', accelerator: "CmdOrCtrl+[",
click() { click() {
webContents.getFocusedWebContents()?.goBack(); webContents.getFocusedWebContents()?.goBack();
} }
}, },
{ {
label: 'Forward', label: "Forward",
accelerator: 'CmdOrCtrl+]', accelerator: "CmdOrCtrl+]",
click() { click() {
webContents.getFocusedWebContents()?.goForward(); webContents.getFocusedWebContents()?.goForward();
} }
}, },
{ {
label: 'Reload', label: "Reload",
accelerator: 'CmdOrCtrl+R', accelerator: "CmdOrCtrl+R",
click() { click() {
windowManager.reload(); windowManager.reload();
} }
}, },
{ role: 'toggleDevTools' }, { role: "toggleDevTools" },
{ type: 'separator' }, { type: "separator" },
{ role: 'resetZoom' }, { role: "resetZoom" },
{ role: 'zoomIn' }, { role: "zoomIn" },
{ role: 'zoomOut' }, { role: "zoomOut" },
{ type: 'separator' }, { type: "separator" },
{ role: 'togglefullscreen' } { role: "togglefullscreen" }
] ]
}; };
const helpMenu: MenuItemConstructorOptions = { const helpMenu: MenuItemConstructorOptions = {
role: 'help', role: "help",
submenu: [ submenu: [
{ {
label: "What's new?", label: "What's new?",
@ -265,7 +265,7 @@ export function buildMenu(windowManager: WindowManager) {
if (isTestEnv) { if (isTestEnv) {
// this is a workaround for the test environment (spectron) not being able to directly access // this is a workaround for the test environment (spectron) not being able to directly access
// the application menus (https://github.com/electron-userland/spectron/issues/21) // the application menus (https://github.com/electron-userland/spectron/issues/21)
ipcMain.on('test-menu-item-click', (event: IpcMainEvent, ...names: string[]) => { ipcMain.on("test-menu-item-click", (event: IpcMainEvent, ...names: string[]) => {
let menu: Menu = Menu.getApplicationMenu(); let menu: Menu = Menu.getApplicationMenu();
const parentLabels: string[] = []; const parentLabels: string[] = [];
let menuItem: MenuItem; let menuItem: MenuItem;
@ -286,7 +286,7 @@ export function buildMenu(windowManager: WindowManager) {
} }
const { enabled, visible, click } = menuItem; const { enabled, visible, click } = menuItem;
if (enabled === false || visible === false || typeof click !== 'function') { if (enabled === false || visible === false || typeof click !== "function") {
logger.info(`[MENU:test-menu-item-click] Menu item ${menuPath} not clickable`); logger.info(`[MENU:test-menu-item-click] Menu item ${menuPath} not clickable`);
return; return;
} }

View File

@ -24,7 +24,7 @@ export class NodeShellSession extends ShellSession {
const shell = await this.kubectl.getPath(); const shell = await this.kubectl.getPath();
let args = []; let args = [];
if (this.createNodeShellPod(this.podId, this.nodeName)) { if (this.createNodeShellPod(this.podId, this.nodeName)) {
await this.waitForRunningPod(this.podId).catch((error) => { await this.waitForRunningPod(this.podId).catch(() => {
this.exit(1001); this.exit(1001);
}); });
} }
@ -108,7 +108,7 @@ export class NodeShellSession extends ShellSession {
const req = await watch.watch(`/api/v1/namespaces/kube-system/pods`, {}, const req = await watch.watch(`/api/v1/namespaces/kube-system/pods`, {},
// callback is called for each received object. // callback is called for each received object.
(_type, obj) => { (type, obj) => {
if (obj.metadata.name == podId && obj.status.phase === "Running") { if (obj.metadata.name == podId && obj.status.phase === "Running") {
resolve(true); resolve(true);
} }

View File

@ -1,4 +1,4 @@
import { EventEmitter } from 'events'; import { EventEmitter } from "events";
import { getFreePort } from "./port"; import { getFreePort } from "./port";
let newPort = 0; let newPort = 0;
@ -8,7 +8,7 @@ jest.mock("net", () => {
createServer() { createServer() {
return new class MockServer extends EventEmitter { return new class MockServer extends EventEmitter {
listen = jest.fn(() => { listen = jest.fn(() => {
this.emit('listening'); this.emit("listening");
return this; return this;
}); });
address = () => { address = () => {

View File

@ -24,7 +24,7 @@ export class PrometheusLens implements PrometheusProvider {
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
switch(opts.category) { switch(opts.category) {
case 'cluster': case "cluster":
return { return {
memoryUsage: ` memoryUsage: `
sum( sum(
@ -43,7 +43,7 @@ export class PrometheusLens implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)`, fsSize: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)`,
fsUsage: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)` fsUsage: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)`
}; };
case 'nodes': case "nodes":
return { return {
memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_node)`, memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_node)`,
memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`, memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
@ -52,7 +52,7 @@ export class PrometheusLens implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (kubernetes_node)`, fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (kubernetes_node)`,
fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (kubernetes_node)` fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (kubernetes_node)`
}; };
case 'pods': case "pods":
return { return {
cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
@ -64,12 +64,12 @@ export class PrometheusLens implements PrometheusProvider {
networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})` networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
}; };
case 'pvc': case "pvc":
return { return {
diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`, diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)` diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
}; };
case 'ingress': case "ingress":
const bytesSent = (ingress: string, statuses: string) => const bytesSent = (ingress: string, statuses: string) =>
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`; `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`;
return { return {

View File

@ -32,7 +32,7 @@ export class PrometheusOperator implements PrometheusProvider {
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
switch(opts.category) { switch(opts.category) {
case 'cluster': case "cluster":
return { return {
memoryUsage: ` memoryUsage: `
sum( sum(
@ -51,7 +51,7 @@ export class PrometheusOperator implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`, fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`,
fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})` fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`
}; };
case 'nodes': case "nodes":
return { return {
memoryUsage: `sum((node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`, memoryUsage: `sum((node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`,
memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`, memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
@ -60,7 +60,7 @@ export class PrometheusOperator implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info) by (node)`, fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info) by (node)`,
fsUsage: `sum((node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) * on (pod,namespace) group_left(node) kube_pod_info) by (node)` fsUsage: `sum((node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`
}; };
case 'pods': case "pods":
return { return {
cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
@ -72,12 +72,12 @@ export class PrometheusOperator implements PrometheusProvider {
networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})` networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
}; };
case 'pvc': case "pvc":
return { return {
diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`, diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)` diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
}; };
case 'ingress': case "ingress":
const bytesSent = (ingress: string, statuses: string) => const bytesSent = (ingress: string, statuses: string) =>
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`; `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`;
return { return {

View File

@ -24,7 +24,7 @@ export class PrometheusStacklight implements PrometheusProvider {
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
switch(opts.category) { switch(opts.category) {
case 'cluster': case "cluster":
return { return {
memoryUsage: ` memoryUsage: `
sum( sum(
@ -43,7 +43,7 @@ export class PrometheusStacklight implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`, fsSize: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`,
fsUsage: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)` fsUsage: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`
}; };
case 'nodes': case "nodes":
return { return {
memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`, memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`,
memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`, memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
@ -52,7 +52,7 @@ export class PrometheusStacklight implements PrometheusProvider {
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`, fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`,
fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)` fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)`
}; };
case 'pods': case "pods":
return { return {
cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
@ -64,12 +64,12 @@ export class PrometheusStacklight implements PrometheusProvider {
networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})` networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
}; };
case 'pvc': case "pvc":
return { return {
diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`, diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)` diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
}; };
case 'ingress': case "ingress":
const bytesSent = (ingress: string, statuses: string) => const bytesSent = (ingress: string, statuses: string) =>
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`; `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`;
return { return {

View File

@ -75,7 +75,7 @@ export class ResourceApplier {
delete resource.metadata?.resourceVersion; delete resource.metadata?.resourceVersion;
const annotations = resource.metadata?.annotations; const annotations = resource.metadata?.annotations;
if (annotations) { if (annotations) {
delete annotations['kubectl.kubernetes.io/last-applied-configuration']; delete annotations["kubectl.kubernetes.io/last-applied-configuration"];
} }
return resource; return resource;
} }

View File

@ -100,11 +100,11 @@ export class Router {
try { try {
const filename = path.basename(req.url); const filename = path.basename(req.url);
// redirect requests to [appName].js, [appName].html /sockjs-node/ to webpack-dev-server (for hot-reload support) // redirect requests to [appName].js, [appName].html /sockjs-node/ to webpack-dev-server (for hot-reload support)
const toWebpackDevServer = filename.includes(appName) || filename.includes('hot-update') || req.url.includes('sockjs-node'); const toWebpackDevServer = filename.includes(appName) || filename.includes("hot-update") || req.url.includes("sockjs-node");
if (isDevelopment && toWebpackDevServer) { if (isDevelopment && toWebpackDevServer) {
const redirectLocation = `http://localhost:${webpackDevServerPort}` + req.url; const redirectLocation = `http://localhost:${webpackDevServerPort}` + req.url;
res.statusCode = 307; res.statusCode = 307;
res.setHeader('Location', redirectLocation); res.setHeader("Location", redirectLocation);
res.end(); res.end();
return; return;
} }
@ -126,8 +126,8 @@ export class Router {
protected addRoutes() { protected addRoutes() {
// Static assets // Static assets
this.router.add( this.router.add(
{ method: 'get', path: '/{path*}' }, { method: "get", path: "/{path*}" },
({ params, response, path, raw: { req } }: LensApiRequest) => { ({ params, response, raw: { req } }: LensApiRequest) => {
this.handleStaticFile(params.path, response, req); this.handleStaticFile(params.path, response, req);
}); });

View File

@ -6,36 +6,36 @@ import { CoreV1Api, V1Secret } from "@kubernetes/client-node";
function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) { function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) {
const tokenData = Buffer.from(secret.data["token"], "base64"); const tokenData = Buffer.from(secret.data["token"], "base64");
return { return {
'apiVersion': 'v1', "apiVersion": "v1",
'kind': 'Config', "kind": "Config",
'clusters': [ "clusters": [
{ {
'name': cluster.contextName, "name": cluster.contextName,
'cluster': { "cluster": {
'server': cluster.apiUrl, "server": cluster.apiUrl,
'certificate-authority-data': secret.data["ca.crt"] "certificate-authority-data": secret.data["ca.crt"]
} }
} }
], ],
'users': [ "users": [
{ {
'name': username, "name": username,
'user': { "user": {
'token': tokenData.toString("utf8"), "token": tokenData.toString("utf8"),
} }
} }
], ],
'contexts': [ "contexts": [
{ {
'name': cluster.contextName, "name": cluster.contextName,
'context': { "context": {
'user': username, "user": username,
'cluster': cluster.contextName, "cluster": cluster.contextName,
'namespace': secret.metadata.namespace, "namespace": secret.metadata.namespace,
} }
} }
], ],
'current-context': cluster.contextName "current-context": cluster.contextName
}; };
} }

View File

@ -72,7 +72,7 @@ class ApiWatcher {
class WatchRoute extends LensApi { class WatchRoute extends LensApi {
public async routeWatch(request: LensApiRequest) { public async routeWatch(request: LensApiRequest) {
const { params, response, cluster} = request; const { response, cluster} = request;
const apis: string[] = request.query.getAll("api"); const apis: string[] = request.query.getAll("api");
const watchers: ApiWatcher[] = []; const watchers: ApiWatcher[] = [];

View File

@ -73,7 +73,7 @@ export class ShellSession extends EventEmitter {
case "powershell.exe": case "powershell.exe":
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.helmBinDir};${this.kubectlPathDir};$Env:PATH"}`]; return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.helmBinDir};${this.kubectlPathDir};$Env:PATH"}`];
case "bash": case "bash":
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]; return ["--init-file", path.join(this.kubectlBinDir, ".bash_set_path")];
case "fish": case "fish":
return ["--login", "--init-command", `export PATH="${this.helmBinDir}:${this.kubectlPathDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]; return ["--login", "--init-command", `export PATH="${this.helmBinDir}:${this.kubectlPathDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`];
case "zsh": case "zsh":
@ -156,7 +156,7 @@ export class ShellSession extends EventEmitter {
this.shellProcess.resize(resizeMsgObj["Width"], resizeMsgObj["Height"]); this.shellProcess.resize(resizeMsgObj["Width"], resizeMsgObj["Height"]);
break; break;
case "9": case "9":
this.emit('newToken', message); this.emit("newToken", message);
break; break;
} }
}); });
@ -164,7 +164,7 @@ export class ShellSession extends EventEmitter {
protected exit(code = 1000) { protected exit(code = 1000) {
if (this.websocket.readyState == this.websocket.OPEN) this.websocket.close(code); if (this.websocket.readyState == this.websocket.OPEN) this.websocket.close(code);
this.emit('exit'); this.emit("exit");
} }
protected closeWebsocketOnProcessExit() { protected closeWebsocketOnProcessExit() {

View File

@ -25,7 +25,7 @@ export async function shellSync() {
const env: Env = JSON.parse(JSON.stringify(envVars)); const env: Env = JSON.parse(JSON.stringify(envVars));
if (!env.LANG) { if (!env.LANG) {
// the LANG env var expects an underscore instead of electron's dash // the LANG env var expects an underscore instead of electron's dash
env.LANG = `${app.getLocale().replace('-', '_')}.UTF-8`; env.LANG = `${app.getLocale().replace("-", "_")}.UTF-8`;
} else if (!env.LANG.endsWith(".UTF-8")) { } else if (!env.LANG.endsWith(".UTF-8")) {
env.LANG += ".UTF-8"; env.LANG += ".UTF-8";
} }

View File

@ -66,7 +66,7 @@ export function createTrayMenu(windowManager: WindowManager): Menu {
showAbout(browserWindow); showAbout(browserWindow);
}, },
}, },
{ type: 'separator' }, { type: "separator" },
{ {
label: "Open Lens", label: "Open Lens",
async click() { async click() {
@ -91,7 +91,7 @@ export function createTrayMenu(windowManager: WindowManager): Menu {
submenu: clusters.map(cluster => { submenu: clusters.map(cluster => {
const { id: clusterId, name: label, online, workspace } = cluster; const { id: clusterId, name: label, online, workspace } = cluster;
return { return {
label: `${online ? '✓' : '\x20'.repeat(3)/*offset*/}${label}`, label: `${online ? "✓" : "\x20".repeat(3)/*offset*/}${label}`,
toolTip: clusterId, toolTip: clusterId,
async click() { async click() {
workspaceStore.setActive(workspace); workspaceStore.setActive(workspace);
@ -115,9 +115,9 @@ export function createTrayMenu(windowManager: WindowManager): Menu {
} }
}, },
}, },
{ type: 'separator' }, { type: "separator" },
{ {
label: 'Quit App', label: "Quit App",
click() { click() {
exitApp(); exitApp();
} }

View File

@ -5,12 +5,12 @@ import { migration } from "../migration-wrapper";
export default migration({ export default migration({
version: "2.0.0-beta.2", version: "2.0.0-beta.2",
run(store, log) { run(store) {
for (const value of store) { for (const value of store) {
const contextName = value[0]; const contextName = value[0];
// Looping all the keys gives out the store internal stuff too... // Looping all the keys gives out the store internal stuff too...
if (contextName === "__internal__" || value[1].hasOwnProperty('kubeConfig')) continue; if (contextName === "__internal__" || value[1].hasOwnProperty("kubeConfig")) continue;
store.set(contextName, { kubeConfig: value[1] }); store.set(contextName, { kubeConfig: value[1] });
} }
} }
}); });

View File

@ -3,7 +3,7 @@ import { migration } from "../migration-wrapper";
export default migration({ export default migration({
version: "2.4.1", version: "2.4.1",
run(store, log) { run(store) {
for (const value of store) { for (const value of store) {
const contextName = value[0]; const contextName = value[0];
if (contextName === "__internal__") continue; if (contextName === "__internal__") continue;

View File

@ -3,7 +3,7 @@ import { migration } from "../migration-wrapper";
export default migration({ export default migration({
version: "2.6.0-beta.2", version: "2.6.0-beta.2",
run(store, log) { run(store) {
for (const value of store) { for (const value of store) {
const clusterKey = value[0]; const clusterKey = value[0];
if (clusterKey === "__internal__") continue; if (clusterKey === "__internal__") continue;

View File

@ -10,7 +10,7 @@ export default migration({
const cluster = value[1]; const cluster = value[1];
if (!cluster.kubeConfig) continue; if (!cluster.kubeConfig) continue;
const kubeConfig = yaml.safeLoad(cluster.kubeConfig); const kubeConfig = yaml.safeLoad(cluster.kubeConfig);
if (!kubeConfig.hasOwnProperty('users')) continue; if (!kubeConfig.hasOwnProperty("users")) continue;
const userObj = kubeConfig.users[0]; const userObj = kubeConfig.users[0];
if (userObj) { if (userObj) {
const user = userObj.user; const user = userObj.user;

View File

@ -3,7 +3,7 @@ import { migration } from "../migration-wrapper";
export default migration({ export default migration({
version: "2.7.0-beta.0", version: "2.7.0-beta.0",
run(store, log) { run(store) {
for (const value of store) { for (const value of store) {
const clusterKey = value[0]; const clusterKey = value[0];
if (clusterKey === "__internal__") continue; if (clusterKey === "__internal__") continue;

View File

@ -4,7 +4,7 @@ import { v4 as uuid } from "uuid";
export default migration({ export default migration({
version: "2.7.0-beta.1", version: "2.7.0-beta.1",
run(store, log) { run(store) {
const clusters: any[] = []; const clusters: any[] = [];
for (const value of store) { for (const value of store) {
const clusterKey = value[0]; const clusterKey = value[0];

View File

@ -44,7 +44,7 @@ export default migration({
const iconPath = cluster.preferences.icon.replace("store://", ""); const iconPath = cluster.preferences.icon.replace("store://", "");
const fileData = fse.readFileSync(path.join(userDataPath, iconPath)); const fileData = fse.readFileSync(path.join(userDataPath, iconPath));
cluster.preferences.icon = `data:;base64,${fileData.toString('base64')}`; cluster.preferences.icon = `data:;base64,${fileData.toString("base64")}`;
} else { } else {
delete cluster.preferences?.icon; delete cluster.preferences?.icon;
} }

View File

@ -1,7 +1,7 @@
// Fix embedded kubeconfig paths under snap config // Fix embedded kubeconfig paths under snap config
import { migration } from "../migration-wrapper"; import { migration } from "../migration-wrapper";
import { ClusterModel, ClusterStore } from "../../common/cluster-store"; import { ClusterModel } from "../../common/cluster-store";
import { getAppVersion } from "../../common/utils/app-version"; import { getAppVersion } from "../../common/utils/app-version";
import fs from "fs"; import fs from "fs";

View File

@ -14,7 +14,7 @@ export class ApiManager {
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase); return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase);
} }
return Array.from(this.apis.values()).find(pathOrCallback ?? ((api: KubeApi) => true)); return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true));
} }
registerApi(apiBase: string, api: KubeApi) { registerApi(apiBase: string, api: KubeApi) {

View File

@ -40,7 +40,7 @@ export class DeploymentApi extends KubeApi<Deployment> {
}, },
{ {
headers: { headers: {
'content-type': 'application/strategic-merge-patch+json' "content-type": "application/strategic-merge-patch+json"
} }
}); });
} }

View File

@ -40,7 +40,7 @@ export const metricsApi = {
if (!start && !end) { if (!start && !end) {
const timeNow = Date.now() / 1000; const timeNow = Date.now() / 1000;
const now = moment.unix(timeNow).startOf('minute').unix(); // round date to minutes const now = moment.unix(timeNow).startOf("minute").unix(); // round date to minutes
start = now - range; start = now - range;
end = now; end = now;
} }

View File

@ -7,8 +7,8 @@ import { KubeApi } from "../kube-api";
export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> { export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> {
getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> { getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> {
return metricsApi.getMetrics({ return metricsApi.getMetrics({
diskUsage: { category: 'pvc', pvc: pvcName }, diskUsage: { category: "pvc", pvc: pvcName },
diskCapacity: { category: 'pvc', pvc: pvcName } diskCapacity: { category: "pvc", pvc: pvcName }
}, { }, {
namespace namespace
}); });

View File

@ -34,7 +34,7 @@ export interface JsonApiConfig {
export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> { export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
static reqInitDefault: RequestInit = { static reqInitDefault: RequestInit = {
headers: { headers: {
'content-type': 'application/json' "content-type": "application/json"
} }
}; };
@ -132,9 +132,9 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
protected writeLog(log: JsonApiLog) { protected writeLog(log: JsonApiLog) {
if (!this.config.debug) return; if (!this.config.debug) return;
const { method, reqUrl, ...params } = log; const { method, reqUrl, ...params } = log;
let textStyle = 'font-weight: bold;'; let textStyle = "font-weight: bold;";
if (params.data) textStyle += 'background: green; color: white;'; if (params.data) textStyle += "background: green; color: white;";
if (params.error) textStyle += 'background: red; color: white;'; if (params.error) textStyle += "background: red; color: white;";
console.log(`%c${method} ${reqUrl}`, textStyle, params); console.log(`%c${method} ${reqUrl}`, textStyle, params);
} }
} }

View File

@ -77,7 +77,7 @@ export function parseKubeApi(path: string): IKubeApiParsed {
* 3. otherwise assume apiVersion <- left[0] * 3. otherwise assume apiVersion <- left[0]
* 4. always resource, name <- left[(0 or 1)+1..] * 4. always resource, name <- left[(0 or 1)+1..]
*/ */
if (left[0].includes('.') || left[1].match(/^v[0-9]/)) { if (left[0].includes(".") || left[1].match(/^v[0-9]/)) {
[apiGroup, apiVersion] = left; [apiGroup, apiVersion] = left;
resource = left.slice(2).join("/"); resource = left.slice(2).join("/");
} else { } else {

View File

@ -142,7 +142,7 @@ export class KubeWatchApi {
protected writeLog(...data: any[]) { protected writeLog(...data: any[]) {
if (isDevelopment) { if (isDevelopment) {
console.log('%cKUBE-WATCH-API:', `font-weight: bold`, ...data); console.log("%cKUBE-WATCH-API:", `font-weight: bold`, ...data);
} }
} }

View File

@ -91,7 +91,6 @@ export class TerminalApi extends WebSocketApi {
} }
reconnect() { reconnect() {
const { reconnectDelaySeconds } = this.params;
super.reconnect(); super.reconnect();
} }

View File

@ -88,7 +88,7 @@ export class WebSocketApi {
reconnect() { reconnect() {
const { reconnectDelaySeconds } = this.params; const { reconnectDelaySeconds } = this.params;
if (!reconnectDelaySeconds) return; if (!reconnectDelaySeconds) return;
this.writeLog('reconnect after', reconnectDelaySeconds + "ms"); this.writeLog("reconnect after", reconnectDelaySeconds + "ms");
this.reconnectTimer = setTimeout(() => this.connect(), reconnectDelaySeconds * 1000); this.reconnectTimer = setTimeout(() => this.connect(), reconnectDelaySeconds * 1000);
this.readyState = WebSocketApiState.RECONNECTING; this.readyState = WebSocketApiState.RECONNECTING;
} }
@ -136,17 +136,17 @@ export class WebSocketApi {
this.onOpen.emit(); this.onOpen.emit();
if (this.params.flushOnOpen) this.flush(); if (this.params.flushOnOpen) this.flush();
this.readyState = WebSocketApiState.OPEN; this.readyState = WebSocketApiState.OPEN;
this.writeLog('%cOPEN', 'color:green;font-weight:bold;', evt); this.writeLog("%cOPEN", "color:green;font-weight:bold;", evt);
} }
protected _onMessage(evt: MessageEvent) { protected _onMessage(evt: MessageEvent) {
const data = this.parseMessage(evt.data); const data = this.parseMessage(evt.data);
this.onData.emit(data); this.onData.emit(data);
this.writeLog('%cMESSAGE', 'color:black;font-weight:bold;', data); this.writeLog("%cMESSAGE", "color:black;font-weight:bold;", data);
} }
protected _onError(evt: Event) { protected _onError(evt: Event) {
this.writeLog('%cERROR', 'color:red;font-weight:bold;', evt); this.writeLog("%cERROR", "color:red;font-weight:bold;", evt);
} }
protected _onClose(evt: CloseEvent) { protected _onClose(evt: CloseEvent) {
@ -158,7 +158,7 @@ export class WebSocketApi {
this.readyState = WebSocketApiState.CLOSED; this.readyState = WebSocketApiState.CLOSED;
this.onClose.emit(); this.onClose.emit();
} }
this.writeLog('%cCLOSE', `color:${error ? "red" : "black"};font-weight:bold;`, evt); this.writeLog("%cCLOSE", `color:${error ? "red" : "black"};font-weight:bold;`, evt);
} }
protected writeLog(...data: any[]) { protected writeLog(...data: any[]) {

View File

@ -66,7 +66,7 @@ export class AddCluster extends React.Component {
userStore.kubeConfigPath = filePath; // save to store userStore.kubeConfigPath = filePath; // save to store
} catch (err) { } catch (err) {
Notifications.error( Notifications.error(
<div>Can't setup <code>{filePath}</code> as kubeconfig: {String(err)}</div> <div>Can&apos;t setup <code>{filePath}</code> as kubeconfig: {String(err)}</div>
); );
if (throwError) { if (throwError) {
throw err; throw err;
@ -196,9 +196,9 @@ export class AddCluster extends React.Component {
return ( return (
<p> <p>
Add clusters by clicking the <span className="text-primary">Add Cluster</span> button. Add clusters by clicking the <span className="text-primary">Add Cluster</span> button.
You'll need to obtain a working kubeconfig for the cluster you want to add. You&apos;ll need to obtain a working kubeconfig for the cluster you want to add.
You can either browse it from the file system or paste it as a text from the clipboard. You can either browse it from the file system or paste it as a text from the clipboard.
Read more about adding clusters <a href={`${docsUrl}/latest/clusters/adding-clusters/`} target="_blank">here</a>. Read more about adding clusters <a href={`${docsUrl}/latest/clusters/adding-clusters/`} rel="noreferrer" target="_blank">here</a>.
</p> </p>
); );
} }
@ -300,7 +300,7 @@ export class AddCluster extends React.Component {
); );
} }
onKubeConfigInputBlur = (evt: React.FocusEvent<HTMLInputElement>) => { onKubeConfigInputBlur = () => {
const isChanged = this.kubeConfigPath !== userStore.kubeConfigPath; const isChanged = this.kubeConfigPath !== userStore.kubeConfigPath;
if (isChanged) { if (isChanged) {
this.kubeConfigPath = this.kubeConfigPath.replace("~", os.homedir()); this.kubeConfigPath = this.kubeConfigPath.replace("~", os.homedir());
@ -354,7 +354,7 @@ export class AddCluster extends React.Component {
theme="round-black" theme="round-black"
/> />
<small className="hint"> <small className="hint">
{'A HTTP proxy server URL (format: http://<address>:<port>).'} {"A HTTP proxy server URL (format: http://<address>:<port>)."}
</small> </small>
</div> </div>
)} )}

View File

@ -93,11 +93,11 @@ export class HelmChartDetails extends Component<Props> {
/> />
</DrawerItem> </DrawerItem>
<DrawerItem name={_i18n._(t`Home`)}> <DrawerItem name={_i18n._(t`Home`)}>
<a href={selectedChart.getHome()} target="_blank">{selectedChart.getHome()}</a> <a href={selectedChart.getHome()} target="_blank" rel="noreferrer">{selectedChart.getHome()}</a>
</DrawerItem> </DrawerItem>
<DrawerItem name={_i18n._(t`Maintainers`)} className="maintainers"> <DrawerItem name={_i18n._(t`Maintainers`)} className="maintainers">
{selectedChart.getMaintainers().map(({ name, email, url }) => {selectedChart.getMaintainers().map(({ name, email, url }) =>
<a key={name} href={url || `mailto:${email}`} target="_blank">{name}</a> <a key={name} href={url || `mailto:${email}`} target="_blank" rel="noreferrer">{name}</a>
)} )}
</DrawerItem> </DrawerItem>
{selectedChart.getKeywords().length > 0 && ( {selectedChart.getKeywords().length > 0 && (

View File

@ -84,7 +84,7 @@ export class HelmCharts extends Component<Props> {
]} ]}
renderTableContents={(chart: HelmChart) => [ renderTableContents={(chart: HelmChart) => [
<figure> <figure key="image">
<img <img
src={chart.getIcon() || require("./helm-placeholder.svg")} src={chart.getIcon() || require("./helm-placeholder.svg")}
onLoad={evt => evt.currentTarget.classList.add("visible")} onLoad={evt => evt.currentTarget.classList.add("visible")}

View File

@ -56,8 +56,9 @@ export class HelmReleaseMenu extends React.Component<Props> {
{...menuProps} {...menuProps}
className={cssNames("HelmReleaseMenu", className)} className={cssNames("HelmReleaseMenu", className)}
removeAction={this.remove} removeAction={this.remove}
children={this.renderContent()} >
/> {this.renderContent()}
</MenuActions>
); );
} }
} }

View File

@ -70,7 +70,7 @@ export class HelmReleases extends Component<Props> {
<div> <div>
<Trans>Remove <b>{releaseNames}</b>?</Trans> <Trans>Remove <b>{releaseNames}</b>?</Trans>
<p className="warning"> <p className="warning">
<Trans>Note: StatefulSet Volumes won't be deleted automatically</Trans> <Trans>Note: StatefulSet Volumes won&apos;t be deleted automatically</Trans>
</p> </p>
</div> </div>
); );
@ -146,4 +146,4 @@ export class HelmReleases extends Component<Props> {
</> </>
); );
} }
} }

View File

@ -19,7 +19,7 @@ export class ClusterAccessibleNamespaces extends React.Component<Props> {
return ( return (
<> <>
<SubTitle title="Accessible Namespaces" /> <SubTitle title="Accessible Namespaces" />
<p><Trans>This setting is useful for manually specifying which namespaces you have access to. This is useful when you don't have permissions to list namespaces.</Trans></p> <p><Trans>This setting is useful for manually specifying which namespaces you have access to. This is useful when you don&apos;t have permissions to list namespaces.</Trans></p>
<EditableList <EditableList
placeholder={_i18n._("Add new namespace...")} placeholder={_i18n._("Add new namespace...")}
add={(newNamespace) => { add={(newNamespace) => {

View File

@ -28,7 +28,7 @@ export class ClusterIconSetting extends React.Component<Props> {
try { try {
if (file) { if (file) {
const buf = Buffer.from(await file.arrayBuffer()); const buf = Buffer.from(await file.arrayBuffer());
cluster.preferences.icon = `data:${file.type};base64,${buf.toString('base64')}`; cluster.preferences.icon = `data:${file.type};base64,${buf.toString("base64")}`;
} else { } else {
// this has to be done as a seperate branch (and not always) because `cluster` // this has to be done as a seperate branch (and not always) because `cluster`
// is observable and triggers an update loop. // is observable and triggers an update loop.

View File

@ -78,7 +78,7 @@ export class ClusterPrometheusSetting extends React.Component<Props> {
<SubTitle title="Prometheus"/> <SubTitle title="Prometheus"/>
<p> <p>
Use pre-installed Prometheus service for metrics. Please refer to the{" "} Use pre-installed Prometheus service for metrics. Please refer to the{" "}
<a href="https://github.com/lensapp/lens/blob/master/troubleshooting/custom-prometheus.md" target="_blank">guide</a>{" "} <a href="https://github.com/lensapp/lens/blob/master/troubleshooting/custom-prometheus.md" target="_blank" rel="noreferrer">guide</a>{" "}
for possible configuration changes. for possible configuration changes.
</p> </p>
<p>Prometheus installation method.</p> <p>Prometheus installation method.</p>
@ -103,11 +103,11 @@ export class ClusterPrometheusSetting extends React.Component<Props> {
/> />
<small className="hint"> <small className="hint">
An address to an existing Prometheus installation{" "} An address to an existing Prometheus installation{" "}
({'<namespace>/<service>:<port>'}). Lens tries to auto-detect address if left empty. ({"<namespace>/<service>:<port>"}). Lens tries to auto-detect address if left empty.
</small> </small>
</> </>
)} )}
</> </>
); );
} }
} }

View File

@ -14,16 +14,18 @@ export class Features extends React.Component<Props> {
return ( return (
<div> <div>
<h2>Features</h2> <h2>Features</h2>
{ clusterFeatureRegistry.getItems().map((f) => { {
return ( clusterFeatureRegistry
<InstallFeature cluster={cluster} feature={f.feature}> .getItems()
<> .map((f) => (
<SubTitle title={f.title}/> <InstallFeature key={f.title} cluster={cluster} feature={f.feature}>
<p><f.components.Description /></p> <>
</> <SubTitle title={f.title} />
</InstallFeature> <p><f.components.Description /></p>
); </>
})} </InstallFeature>
))
}
</div> </div>
); );
} }

View File

@ -62,7 +62,7 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
]} ]}
renderTableContents={(hpa: HorizontalPodAutoscaler) => [ renderTableContents={(hpa: HorizontalPodAutoscaler) => [
hpa.getName(), hpa.getName(),
<KubeObjectStatusIcon object={hpa} />, <KubeObjectStatusIcon key="icon" object={hpa} />,
hpa.getNs(), hpa.getNs(),
this.getTargets(hpa), this.getTargets(hpa),
hpa.getMinPods(), hpa.getMinPods(),
@ -85,4 +85,3 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
); );
} }
} }

View File

@ -5,11 +5,9 @@ import { observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router"; import { RouteComponentProps } from "react-router";
import { configMapsStore } from "./config-maps.store"; import { configMapsStore } from "./config-maps.store";
import { ConfigMap, configMapApi } from "../../api/endpoints/configmap.api"; import { ConfigMap } from "../../api/endpoints/configmap.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object";
import { IConfigMapsRouteParams } from "./config-maps.route"; import { IConfigMapsRouteParams } from "./config-maps.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy { enum sortBy {
@ -48,7 +46,7 @@ export class ConfigMaps extends React.Component<Props> {
]} ]}
renderTableContents={(configMap: ConfigMap) => [ renderTableContents={(configMap: ConfigMap) => [
configMap.getName(), configMap.getName(),
<KubeObjectStatusIcon object={configMap}/>, <KubeObjectStatusIcon key="icon" object={configMap}/>,
configMap.getNs(), configMap.getNs(),
configMap.getKeys().join(", "), configMap.getKeys().join(", "),
configMap.getAge(), configMap.getAge(),
@ -57,4 +55,3 @@ export class ConfigMaps extends React.Component<Props> {
); );
} }
} }

View File

@ -19,18 +19,17 @@ export class PodDisruptionBudgetDetails extends React.Component<Props> {
render() { render() {
const { object: pdb } = this.props; const { object: pdb } = this.props;
if (!pdb) return null; if (!pdb) return null;
const { status, spec } = pdb;
const selectors = pdb.getSelectors(); const selectors = pdb.getSelectors();
return ( return (
<div className="PdbDetails"> <div className="PdbDetails">
<KubeObjectMeta object={pdb}/> <KubeObjectMeta object={pdb}/>
{selectors.length > 0 && {selectors.length > 0 &&
<DrawerItem name={<Trans>Selector</Trans>} labelsOnly> <DrawerItem name={<Trans>Selector</Trans>} labelsOnly>
{ {
selectors.map(label => <Badge key={label} label={label}/>) selectors.map(label => <Badge key={label} label={label}/>)
} }
</DrawerItem> </DrawerItem>
} }
<DrawerItem name={<Trans>Min Available</Trans>}> <DrawerItem name={<Trans>Min Available</Trans>}>

View File

@ -3,13 +3,9 @@ import "./pod-disruption-budgets.scss";
import * as React from "react"; import * as React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { podDisruptionBudgetsStore } from "./pod-disruption-budgets.store"; import { podDisruptionBudgetsStore } from "./pod-disruption-budgets.store";
import { PodDisruptionBudget, pdbApi } from "../../api/endpoints/poddisruptionbudget.api"; import { PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectDetailsProps, KubeObjectListLayout } from "../kube-object"; import { KubeObjectDetailsProps, KubeObjectListLayout } from "../kube-object";
import { IPodDisruptionBudgetsRouteParams } from "./pod-disruption-budgets.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy { enum sortBy {
@ -58,7 +54,7 @@ export class PodDisruptionBudgets extends React.Component<Props> {
renderTableContents={(pdb: PodDisruptionBudget) => { renderTableContents={(pdb: PodDisruptionBudget) => {
return [ return [
pdb.getName(), pdb.getName(),
<KubeObjectStatusIcon object={pdb} />, <KubeObjectStatusIcon key="icon" object={pdb} />,
pdb.getNs(), pdb.getNs(),
pdb.getMinAvailable(), pdb.getMinAvailable(),
pdb.getMaxUnavailable(), pdb.getMaxUnavailable(),

View File

@ -63,7 +63,7 @@ export class AddQuotaDialog extends React.Component<Props> {
@computed get quotaEntries() { @computed get quotaEntries() {
return Object.entries(this.quotas) return Object.entries(this.quotas)
.filter(([type, value]) => !!value.trim()); .filter(([, value]) => !!value.trim());
} }
@computed get quotaOptions() { @computed get quotaOptions() {
@ -201,4 +201,4 @@ export class AddQuotaDialog extends React.Component<Props> {
</Dialog> </Dialog>
); );
} }
} }

View File

@ -16,8 +16,6 @@ import { ReplicaSetDetails } from "../+workloads-replicasets";
interface Props extends KubeObjectDetailsProps<ResourceQuota> { interface Props extends KubeObjectDetailsProps<ResourceQuota> {
} }
const onlyNumbers = /$[0-9]*^/g;
function transformUnit(name: string, value: string): number { function transformUnit(name: string, value: string): number {
if (name.includes("memory") || name.includes("storage")) { if (name.includes("memory") || name.includes("storage")) {
return unitsToBytes(value); return unitsToBytes(value);

View File

@ -4,13 +4,11 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router"; import { RouteComponentProps } from "react-router";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object";
import { ResourceQuota, resourceQuotaApi } from "../../api/endpoints/resource-quota.api"; import { ResourceQuota } from "../../api/endpoints/resource-quota.api";
import { AddQuotaDialog } from "./add-quota-dialog"; import { AddQuotaDialog } from "./add-quota-dialog";
import { resourceQuotaStore } from "./resource-quotas.store"; import { resourceQuotaStore } from "./resource-quotas.store";
import { IResourceQuotaRouteParams } from "./resource-quotas.route"; import { IResourceQuotaRouteParams } from "./resource-quotas.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy { enum sortBy {
@ -47,7 +45,7 @@ export class ResourceQuotas extends React.Component<Props> {
]} ]}
renderTableContents={(resourceQuota: ResourceQuota) => [ renderTableContents={(resourceQuota: ResourceQuota) => [
resourceQuota.getName(), resourceQuota.getName(),
<KubeObjectStatusIcon object={resourceQuota}/>, <KubeObjectStatusIcon key="icon" object={resourceQuota}/>,
resourceQuota.getNs(), resourceQuota.getNs(),
resourceQuota.getAge(), resourceQuota.getAge(),
]} ]}

View File

@ -4,14 +4,12 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router"; import { RouteComponentProps } from "react-router";
import { Secret, secretsApi } from "../../api/endpoints"; import { Secret } from "../../api/endpoints";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { AddSecretDialog } from "./add-secret-dialog"; import { AddSecretDialog } from "./add-secret-dialog";
import { ISecretsRouteParams } from "./secrets.route"; import { ISecretsRouteParams } from "./secrets.route";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { secretsStore } from "./secrets.store"; import { secretsStore } from "./secrets.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy { enum sortBy {
@ -57,7 +55,7 @@ export class Secrets extends React.Component<Props> {
]} ]}
renderTableContents={(secret: Secret) => [ renderTableContents={(secret: Secret) => [
secret.getName(), secret.getName(),
<KubeObjectStatusIcon object={secret} />, <KubeObjectStatusIcon key="icon" object={secret} />,
secret.getNs(), secret.getNs(),
secret.getLabels().map(label => <Badge key={label} label={label}/>), secret.getLabels().map(label => <Badge key={label} label={label}/>),
secret.getKeys().join(", "), secret.getKeys().join(", "),

View File

@ -4,8 +4,7 @@ import React from "react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { apiManager } from "../../api/api-manager"; import { CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { AceEditor } from "../ace-editor"; import { AceEditor } from "../ace-editor";
import { Badge } from "../badge"; import { Badge } from "../badge";

View File

@ -90,19 +90,16 @@ export class CrdList extends React.Component {
{ title: <Trans>Scope</Trans>, className: "scope", sortBy: sortBy.scope }, { title: <Trans>Scope</Trans>, className: "scope", sortBy: sortBy.scope },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age }, { title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]} ]}
renderTableContents={(crd: CustomResourceDefinition) => { renderTableContents={(crd: CustomResourceDefinition) => [
return [ <Link key="link" to={crd.getResourceUrl()} onClick={stopPropagation}>
<Link to={crd.getResourceUrl()} onClick={stopPropagation}> {crd.getResourceTitle()}
{crd.getResourceTitle()} </Link>,
</Link>, crd.getGroup(),
crd.getGroup(), crd.getVersion(),
crd.getVersion(), crd.getScope(),
crd.getScope(), crd.getAge(),
crd.getAge(), ]}
];
}}
/> />
); );
} }
} }

View File

@ -102,7 +102,7 @@ export class Events extends React.Component<Props> {
}, },
event.getNs(), event.getNs(),
kind, kind,
<Link to={detailsUrl} title={name} onClick={stopPropagation}>{name}</Link>, <Link key="link" to={detailsUrl} title={name} onClick={stopPropagation}>{name}</Link>,
event.getSource(), event.getSource(),
event.count, event.count,
event.getAge(), event.getAge(),

Some files were not shown because too many files have changed in this diff Show More