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:
parent
10eb082854
commit
7451869c25
83
.eslintrc.js
83
.eslintrc.js
@ -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",
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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'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" target="_blank">here</a>.
|
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>.
|
||||||
</span>
|
</span>
|
||||||
);
|
)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
feature: new MetricsFeature()
|
feature: new MetricsFeature()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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/"));
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -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",
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 [
|
||||||
[
|
[
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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> {
|
||||||
|
|||||||
@ -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>();
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 });
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/main/menu.ts
110
src/main/menu.ts
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = () => {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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[] = [];
|
||||||
|
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,11 @@ 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] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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];
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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";
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -91,7 +91,6 @@ export class TerminalApi extends WebSocketApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reconnect() {
|
reconnect() {
|
||||||
const { reconnectDelaySeconds } = this.params;
|
|
||||||
super.reconnect();
|
super.reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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[]) {
|
||||||
|
|||||||
@ -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'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'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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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 && (
|
||||||
|
|||||||
@ -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")}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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't be deleted automatically</Trans>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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't have permissions to list namespaces.</Trans></p>
|
||||||
<EditableList
|
<EditableList
|
||||||
placeholder={_i18n._("Add new namespace...")}
|
placeholder={_i18n._("Add new namespace...")}
|
||||||
add={(newNamespace) => {
|
add={(newNamespace) => {
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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,7 +103,7 @@ 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>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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) => (
|
||||||
|
<InstallFeature key={f.title} cluster={cluster} feature={f.feature}>
|
||||||
<>
|
<>
|
||||||
<SubTitle title={f.title}/>
|
<SubTitle title={f.title} />
|
||||||
<p><f.components.Description /></p>
|
<p><f.components.Description /></p>
|
||||||
</>
|
</>
|
||||||
</InstallFeature>
|
</InstallFeature>
|
||||||
);
|
))
|
||||||
})}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@ 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">
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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(),
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -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(", "),
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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(),
|
||||||
];
|
]}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
Loading…
Reference in New Issue
Block a user