mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into logs-search
This commit is contained in:
commit
b0c0794200
352 files changed
+28255
-3089
No files matched your search
@@ -149,6 +149,11 @@ jobs:
|
|||||||
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: $(GH_TOKEN)
|
GH_TOKEN: $(GH_TOKEN)
|
||||||
|
- script: make publish-npm
|
||||||
|
displayName: Publish npm package
|
||||||
|
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
||||||
|
env:
|
||||||
|
NPM_TOKEN: $(NPM_TOKEN)
|
||||||
- bash: |
|
- bash: |
|
||||||
mkdir -p "$AZURE_CACHE_FOLDER"
|
mkdir -p "$AZURE_CACHE_FOLDER"
|
||||||
tar -czf "$AZURE_CACHE_FOLDER/yarn-cache.tar.gz" "$YARN_CACHE_FOLDER"
|
tar -czf "$AZURE_CACHE_FOLDER/yarn-cache.tar.gz" "$YARN_CACHE_FOLDER"
|
||||||
|
|||||||
+5
-1
@@ -1,9 +1,11 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
ignorePatterns: ["src/extensions/npm/extensions/api.d.ts"],
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
"src/renderer/**/*.js",
|
"src/renderer/**/*.js",
|
||||||
"build/**/*.js",
|
"build/**/*.js",
|
||||||
|
"extensions/**/*.js"
|
||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
@@ -24,7 +26,9 @@ module.exports = {
|
|||||||
files: [
|
files: [
|
||||||
"build/*.ts",
|
"build/*.ts",
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"integration/**/*.ts"
|
"integration/**/*.ts",
|
||||||
|
"src/extensions/**/*.ts*",
|
||||||
|
"extensions/**/*.ts*"
|
||||||
],
|
],
|
||||||
parser: "@typescript-eslint/parser",
|
parser: "@typescript-eslint/parser",
|
||||||
extends: [
|
extends: [
|
||||||
|
|||||||
+9
-4
@@ -1,12 +1,17 @@
|
|||||||
dist/
|
dist/
|
||||||
out/
|
|
||||||
node_modules/
|
node_modules/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
coverage/
|
coverage/
|
||||||
tmp/
|
tmp/
|
||||||
static/build/**
|
|
||||||
binaries/client/
|
|
||||||
binaries/server/
|
|
||||||
locales/**/**.js
|
locales/**/**.js
|
||||||
lens.log
|
lens.log
|
||||||
|
static/build
|
||||||
|
static/types
|
||||||
|
binaries/client/
|
||||||
|
binaries/server/
|
||||||
|
src/extensions/*/*.js
|
||||||
|
src/extensions/*/*.d.ts
|
||||||
|
types/extension-api.d.ts
|
||||||
|
types/extension-renderer-api.d.ts
|
||||||
|
extensions/*/dist
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020 Mirantis, Inc.
|
Copyright (c) 2020 Mirantis, Inc.
|
||||||
|
|
||||||
All rights reserved.
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
|
* All content residing under the "docs/" directory of this repository, if that
|
||||||
|
directory exists, is licensed under "Creative Commons: CC BY-SA 4.0 license".
|
||||||
|
* All third party components incorporated into the Lens Software are licensed
|
||||||
|
under the original license provided by the owner of the applicable component.
|
||||||
|
* Content outside of the above mentioned directories or restrictions above is
|
||||||
|
available under the "MIT" license as defined below.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
EXTENSIONS_DIR = ./extensions
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
DETECTED_OS := Windows
|
DETECTED_OS := Windows
|
||||||
else
|
else
|
||||||
@@ -46,13 +48,24 @@ integration-win:
|
|||||||
test-app:
|
test-app:
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
build: install-deps download-bins
|
build: install-deps download-bins build-extensions
|
||||||
ifeq "$(DETECTED_OS)" "Windows"
|
ifeq "$(DETECTED_OS)" "Windows"
|
||||||
yarn dist:win
|
yarn dist:win
|
||||||
else
|
else
|
||||||
yarn dist
|
yarn dist
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
build-extensions:
|
||||||
|
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), $(MAKE) -C $(dir) build;)
|
||||||
|
|
||||||
|
build-npm:
|
||||||
|
yarn compile:extension-types
|
||||||
|
yarn npm:fix-package-version
|
||||||
|
|
||||||
|
publish-npm: build-npm
|
||||||
|
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
|
||||||
|
cd src/extensions/npm/extensions && npm publish --access=public
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifeq "$(DETECTED_OS)" "Windows"
|
ifeq "$(DETECTED_OS)" "Windows"
|
||||||
if exist binaries\client del /s /q binaries\client\*.*
|
if exist binaries\client del /s /q binaries\client\*.*
|
||||||
|
|||||||
@@ -40,9 +40,10 @@ brew cask install lens
|
|||||||
|
|
||||||
Allows for faster separate re-runs of some of the more involved processes:
|
Allows for faster separate re-runs of some of the more involved processes:
|
||||||
|
|
||||||
1. `yarn dev:main` compiles electron's main process part and start watching files
|
1. `yarn dev:main` compiles electron's main process app part
|
||||||
1. `yarn dev:renderer` compiles electron's renderer part and start watching files
|
1. `yarn dev:renderer` compiles electron's renderer app part
|
||||||
1. `yarn dev-run` runs app in dev-mode and restarts when electron's main process file has changed
|
1. `yarn dev:extension-types` compile declaration types for `@k8slens/extensions`
|
||||||
|
1. `yarn dev-run` runs app in dev-mode and auto-restart when main process file has changed
|
||||||
|
|
||||||
## Developer's ~~RTFM~~ recommended list:
|
## Developer's ~~RTFM~~ recommended list:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Generate tray icons from SVG to PNG + different sizes and colors (B&W)
|
||||||
|
// Command: `yarn build:tray-icons`
|
||||||
|
import path from "path"
|
||||||
|
import sharp from "sharp";
|
||||||
|
import jsdom from "jsdom"
|
||||||
|
import fs from "fs-extra"
|
||||||
|
|
||||||
|
export async function generateTrayIcon(
|
||||||
|
{
|
||||||
|
outputFilename = "tray_icon", // e.g. output tray_icon_dark@2x.png
|
||||||
|
svgIconPath = path.resolve(__dirname, "../src/renderer/components/icon/logo-lens.svg"),
|
||||||
|
outputFolder = path.resolve(__dirname, "./tray"),
|
||||||
|
dpiSuffix = "2x",
|
||||||
|
pixelSize = 32,
|
||||||
|
shouldUseDarkColors = false, // managed by electron.nativeTheme.shouldUseDarkColors
|
||||||
|
} = {}) {
|
||||||
|
outputFilename += shouldUseDarkColors ? "_dark" : ""
|
||||||
|
dpiSuffix = dpiSuffix !== "1x" ? `@${dpiSuffix}` : ""
|
||||||
|
const pngIconDestPath = path.resolve(outputFolder, `${outputFilename}${dpiSuffix}.png`)
|
||||||
|
try {
|
||||||
|
// Modify .SVG colors
|
||||||
|
const trayIconColor = shouldUseDarkColors ? "white" : "black";
|
||||||
|
const svgDom = await jsdom.JSDOM.fromFile(svgIconPath);
|
||||||
|
const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0];
|
||||||
|
svgRoot.innerHTML += `<style>* {fill: ${trayIconColor} !important;}</style>`
|
||||||
|
const svgIconBuffer = Buffer.from(svgRoot.outerHTML);
|
||||||
|
|
||||||
|
// Resize and convert to .PNG
|
||||||
|
const pngIconBuffer: Buffer = await sharp(svgIconBuffer)
|
||||||
|
.resize({ width: pixelSize, height: pixelSize })
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
// Save icon
|
||||||
|
await fs.writeFile(pngIconDestPath, pngIconBuffer);
|
||||||
|
console.info(`[DONE]: Tray icon saved at "${pngIconDestPath}"`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[ERROR]: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run
|
||||||
|
const iconSizes: Record<string, number> = {
|
||||||
|
"1x": 16,
|
||||||
|
"2x": 32,
|
||||||
|
"3x": 48,
|
||||||
|
};
|
||||||
|
Object.entries(iconSizes).forEach(([dpiSuffix, pixelSize]) => {
|
||||||
|
generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: false });
|
||||||
|
generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: true });
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import * as fs from "fs"
|
||||||
|
import * as path from "path"
|
||||||
|
import packageInfo from "../src/extensions/npm/extensions/package.json"
|
||||||
|
import appInfo from "../package.json"
|
||||||
|
|
||||||
|
const packagePath = path.join(__dirname, "../src/extensions/npm/extensions/package.json")
|
||||||
|
|
||||||
|
packageInfo.version = appInfo.version
|
||||||
|
fs.writeFileSync(packagePath, JSON.stringify(packageInfo, null, 2))
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 448 B |
Binary file not shown.
|
After Width: | Height: | Size: 973 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Lens Example Extension
|
||||||
|
|
||||||
|
*TODO*: add more info
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
`npm run build`
|
||||||
|
|
||||||
|
## Dev
|
||||||
|
|
||||||
|
`npm run dev`
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { LensMainExtension } from "@k8slens/extensions";
|
||||||
|
|
||||||
|
export default class ExampleExtensionMain extends LensMainExtension {
|
||||||
|
onActivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
}
|
||||||
+3468
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "extension-example",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Example extension",
|
||||||
|
"main": "dist/main.js",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"dev": "npm run build --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-open-doodles": "^1.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { LensRendererExtension, Component } from "@k8slens/extensions";
|
||||||
|
import { CoffeeDoodle } from "react-open-doodles";
|
||||||
|
import path from "path";
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export function ExampleIcon(props: Component.IconProps) {
|
||||||
|
return <Component.Icon {...props} material="pages" tooltip={path.basename(__filename)}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExamplePage extends React.Component<{ extension: LensRendererExtension }> {
|
||||||
|
deactivate = () => {
|
||||||
|
const { extension } = this.props;
|
||||||
|
extension.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const doodleStyle = {
|
||||||
|
width: "200px"
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flex column gaps align-flex-start">
|
||||||
|
<div style={doodleStyle}><CoffeeDoodle accent="#3d90ce" /></div>
|
||||||
|
<p>Hello from Example extension!</p>
|
||||||
|
<p>File: <i>{__filename}</i></p>
|
||||||
|
<Component.Button accent label="Deactivate" onClick={this.deactivate}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
|
import { ExampleIcon, ExamplePage } from "./page"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default class ExampleExtension extends LensRendererExtension {
|
||||||
|
clusterPages = [
|
||||||
|
{
|
||||||
|
path: "/extension-example",
|
||||||
|
title: "Example Extension",
|
||||||
|
components: {
|
||||||
|
Page: () => <ExamplePage extension={this}/>,
|
||||||
|
MenuIcon: ExampleIcon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./*.ts",
|
||||||
|
"./*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: './main.ts',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-main",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"mobx": "var global.Mobx",
|
||||||
|
"react": "var global.React"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
filename: 'main.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
+3574
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-metrics-cluster-feature",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens metrics cluster feature",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"dev": "npm run build --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^7.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions"
|
||||||
|
import { MetricsFeature } from "./src/metrics-feature"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default class ClusterMetricsFeatureExtension extends LensRendererExtension {
|
||||||
|
clusterFeatures = [
|
||||||
|
{
|
||||||
|
title: "Metrics Stack",
|
||||||
|
components: {
|
||||||
|
Description: () => {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Enable timeseries data visualization (Prometheus stack) for your cluster.
|
||||||
|
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>.
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
feature: new MetricsFeature()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,96 @@
|
|||||||
|
import { ClusterFeature, Store, K8sApi } from "@k8slens/extensions"
|
||||||
|
import semver from "semver"
|
||||||
|
import * as path from "path"
|
||||||
|
|
||||||
|
export interface MetricsConfiguration {
|
||||||
|
// Placeholder for Metrics config structure
|
||||||
|
persistence: {
|
||||||
|
enabled: boolean;
|
||||||
|
storageClass: string;
|
||||||
|
size: string;
|
||||||
|
};
|
||||||
|
nodeExporter: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
kubeStateMetrics: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
retention: {
|
||||||
|
time: string;
|
||||||
|
size: string;
|
||||||
|
};
|
||||||
|
alertManagers: string[];
|
||||||
|
replicas: number;
|
||||||
|
storageClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetricsFeature extends ClusterFeature.Feature {
|
||||||
|
name = "metrics"
|
||||||
|
latestVersion = "v2.17.2-lens1"
|
||||||
|
|
||||||
|
config: MetricsConfiguration = {
|
||||||
|
persistence: {
|
||||||
|
enabled: false,
|
||||||
|
storageClass: null,
|
||||||
|
size: "20G",
|
||||||
|
},
|
||||||
|
nodeExporter: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
retention: {
|
||||||
|
time: "2d",
|
||||||
|
size: "5GB",
|
||||||
|
},
|
||||||
|
kubeStateMetrics: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
alertManagers: null,
|
||||||
|
replicas: 1,
|
||||||
|
storageClass: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
async install(cluster: Store.Cluster): Promise<void> {
|
||||||
|
// Check if there are storageclasses
|
||||||
|
const storageClassApi = K8sApi.forCluster(cluster, K8sApi.StorageClass)
|
||||||
|
const scs = await storageClassApi.list()
|
||||||
|
this.config.persistence.enabled = scs.some(sc => (
|
||||||
|
sc.metadata?.annotations?.['storageclass.kubernetes.io/is-default-class'] === 'true' ||
|
||||||
|
sc.metadata?.annotations?.['storageclass.beta.kubernetes.io/is-default-class'] === 'true'
|
||||||
|
));
|
||||||
|
|
||||||
|
super.applyResources(cluster, super.renderTemplates(path.join(__dirname, "../resources/")))
|
||||||
|
}
|
||||||
|
|
||||||
|
async upgrade(cluster: Store.Cluster): Promise<void> {
|
||||||
|
return this.install(cluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateStatus(cluster: Store.Cluster): Promise<ClusterFeature.FeatureStatus> {
|
||||||
|
try {
|
||||||
|
const statefulSet = K8sApi.forCluster(cluster, K8sApi.StatefulSet)
|
||||||
|
const prometheus = await statefulSet.get({name: "prometheus", namespace: "lens-metrics"})
|
||||||
|
if (prometheus?.kind) {
|
||||||
|
this.status.installed = true;
|
||||||
|
this.status.currentVersion = prometheus.spec.template.spec.containers[0].image.split(":")[1];
|
||||||
|
this.status.canUpgrade = semver.lt(this.status.currentVersion, this.latestVersion, true);
|
||||||
|
} else {
|
||||||
|
this.status.installed = false
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
if (e?.error?.code === 404) {
|
||||||
|
this.status.installed = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.status
|
||||||
|
}
|
||||||
|
|
||||||
|
async uninstall(cluster: Store.Cluster): Promise<void> {
|
||||||
|
const namespaceApi = K8sApi.forCluster(cluster, K8sApi.Namespace)
|
||||||
|
const clusterRoleBindingApi = K8sApi.forCluster(cluster, K8sApi.ClusterRoleBinding)
|
||||||
|
const clusterRoleApi = K8sApi.forCluster(cluster, K8sApi.ClusterRole)
|
||||||
|
|
||||||
|
await namespaceApi.delete({name: "lens-metrics"})
|
||||||
|
await clusterRoleBindingApi.delete({name: "lens-prometheus"})
|
||||||
|
await clusterRoleApi.delete({name: "lens-prometheus"}) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./*.ts",
|
||||||
|
"./*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
__dirname: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
Generated
+3512
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-node-menu",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens node menu",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"dev": "npm run build --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
|
import React from "react"
|
||||||
|
import { NodeMenu, NodeMenuProps } from "./src/node-menu"
|
||||||
|
|
||||||
|
export default class NodeMenuRendererExtension extends LensRendererExtension {
|
||||||
|
kubeObjectMenuItems = [
|
||||||
|
{
|
||||||
|
kind: "Node",
|
||||||
|
apiVersions: ["v1"],
|
||||||
|
components: {
|
||||||
|
MenuItem: (props: NodeMenuProps) => <NodeMenu {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Component, K8sApi, Navigation} from "@k8slens/extensions"
|
||||||
|
|
||||||
|
export interface NodeMenuProps extends Component.KubeObjectMenuProps<K8sApi.Node> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NodeMenu(props: NodeMenuProps) {
|
||||||
|
const { object: node, toolbar } = props;
|
||||||
|
if (!node) return null;
|
||||||
|
const nodeName = node.getName();
|
||||||
|
|
||||||
|
const sendToTerminal = (command: string) => {
|
||||||
|
Component.terminalStore.sendCommand(command, {
|
||||||
|
enter: true,
|
||||||
|
newTab: true,
|
||||||
|
});
|
||||||
|
Navigation.hideDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
const shell = () => {
|
||||||
|
Component.createTerminalTab({
|
||||||
|
title: `Node: ${nodeName}`,
|
||||||
|
node: nodeName,
|
||||||
|
});
|
||||||
|
Navigation.hideDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cordon = () => {
|
||||||
|
sendToTerminal(`kubectl cordon ${nodeName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unCordon = () => {
|
||||||
|
sendToTerminal(`kubectl uncordon ${nodeName}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const drain = () => {
|
||||||
|
const command = `kubectl drain ${nodeName} --delete-local-data --ignore-daemonsets --force`;
|
||||||
|
Component.ConfirmDialog.open({
|
||||||
|
ok: () => sendToTerminal(command),
|
||||||
|
labelOk: `Drain Node`,
|
||||||
|
message: (
|
||||||
|
<p>
|
||||||
|
Are you sure you want to drain <b>{nodeName}</b>?
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Component.MenuItem onClick={shell}>
|
||||||
|
<Component.Icon svg="ssh" interactive={toolbar} title="Node shell"/>
|
||||||
|
<span className="title">Shell</span>
|
||||||
|
</Component.MenuItem>
|
||||||
|
{!node.isUnschedulable() && (
|
||||||
|
<Component.MenuItem onClick={cordon}>
|
||||||
|
<Component.Icon material="pause_circle_filled" title="Cordon" interactive={toolbar}/>
|
||||||
|
<span className="title">Cordon</span>
|
||||||
|
</Component.MenuItem>
|
||||||
|
)}
|
||||||
|
{node.isUnschedulable() && (
|
||||||
|
<Component.MenuItem onClick={unCordon}>
|
||||||
|
<Component.Icon material="play_circle_filled" title="Uncordon" interactive={toolbar}/>
|
||||||
|
<span className="title">Uncordon</span>
|
||||||
|
</Component.MenuItem>
|
||||||
|
)}
|
||||||
|
<Component.MenuItem onClick={drain}>
|
||||||
|
<Component.Icon material="delete_sweep" title="Drain" interactive={toolbar}/>
|
||||||
|
<span className="title">Drain</span>
|
||||||
|
</Component.MenuItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./*.ts",
|
||||||
|
"./*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
Generated
+3512
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-pod-menu",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens pod menu",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"dev": "npm run build --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
|
import { PodShellMenu, PodShellMenuProps } from "./src/shell-menu"
|
||||||
|
import { PodLogsMenu, PodLogsMenuProps } from "./src/logs-menu"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default class PodMenuRendererExtension extends LensRendererExtension {
|
||||||
|
kubeObjectMenuItems = [
|
||||||
|
{
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersions: ["v1"],
|
||||||
|
components: {
|
||||||
|
MenuItem: (props: PodShellMenuProps) => <PodShellMenu {...props} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersions: ["v1"],
|
||||||
|
components: {
|
||||||
|
MenuItem: (props: PodLogsMenuProps) => <PodLogsMenu {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Component, K8sApi, Util, Navigation } from "@k8slens/extensions";
|
||||||
|
|
||||||
|
export interface PodLogsMenuProps extends Component.KubeObjectMenuProps<K8sApi.Pod> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PodLogsMenu extends React.Component<PodLogsMenuProps> {
|
||||||
|
showLogs(container: K8sApi.IPodContainer) {
|
||||||
|
Navigation.hideDetails();
|
||||||
|
const pod = this.props.object;
|
||||||
|
Component.createPodLogsTab({
|
||||||
|
pod,
|
||||||
|
containers: pod.getContainers(),
|
||||||
|
initContainers: pod.getInitContainers(),
|
||||||
|
selectedContainer: container,
|
||||||
|
showTimestamps: false,
|
||||||
|
previous: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { object: pod, toolbar } = this.props
|
||||||
|
const containers = pod.getAllContainers();
|
||||||
|
const statuses = pod.getContainerStatuses();
|
||||||
|
if (!containers.length) return;
|
||||||
|
return (
|
||||||
|
<Component.MenuItem onClick={Util.prevDefault(() => this.showLogs(containers[0]))}>
|
||||||
|
<Component.Icon material="subject" title="Logs" interactive={toolbar}/>
|
||||||
|
<span className="title">Logs</span>
|
||||||
|
{containers.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Component.Icon className="arrow" material="keyboard_arrow_right"/>
|
||||||
|
<Component.SubMenu>
|
||||||
|
{
|
||||||
|
containers.map(container => {
|
||||||
|
const { name } = container
|
||||||
|
const status = statuses.find(status => status.name === name);
|
||||||
|
const brick = status ? (
|
||||||
|
<Component.StatusBrick
|
||||||
|
className={Util.cssNames(Object.keys(status.state)[0], { ready: status.ready })}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
return (
|
||||||
|
<Component.MenuItem key={name} onClick={Util.prevDefault(() => this.showLogs(container))} className="flex align-center">
|
||||||
|
{brick}
|
||||||
|
{name}
|
||||||
|
</Component.MenuItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Component.SubMenu>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Component.MenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Component, K8sApi, Util, Navigation } from "@k8slens/extensions";
|
||||||
|
|
||||||
|
export interface PodShellMenuProps extends Component.KubeObjectMenuProps<K8sApi.Pod> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PodShellMenu extends React.Component<PodShellMenuProps> {
|
||||||
|
async execShell(container?: string) {
|
||||||
|
Navigation.hideDetails();
|
||||||
|
const { object: pod } = this.props
|
||||||
|
const containerParam = container ? `-c ${container}` : ""
|
||||||
|
let command = `kubectl exec -i -t -n ${pod.getNs()} ${pod.getName()} ${containerParam} "--"`
|
||||||
|
if (window.navigator.platform !== "Win32") {
|
||||||
|
command = `exec ${command}`
|
||||||
|
}
|
||||||
|
if (pod.getSelectedNodeOs() === "windows") {
|
||||||
|
command = `${command} powershell`
|
||||||
|
} else {
|
||||||
|
command = `${command} sh -c "clear; (bash || ash || sh)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const shell = Component.createTerminalTab({
|
||||||
|
title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()})`
|
||||||
|
});
|
||||||
|
|
||||||
|
Component.terminalStore.sendCommand(command, {
|
||||||
|
enter: true,
|
||||||
|
tabId: shell.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { object, toolbar } = this.props
|
||||||
|
const containers = object.getRunningContainers();
|
||||||
|
if (!containers.length) return;
|
||||||
|
return (
|
||||||
|
<Component.MenuItem onClick={Util.prevDefault(() => this.execShell(containers[0].name))}>
|
||||||
|
<Component.Icon svg="ssh" interactive={toolbar} title="Pod shell"/>
|
||||||
|
<span className="title">Shell</span>
|
||||||
|
{containers.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Component.Icon className="arrow" material="keyboard_arrow_right"/>
|
||||||
|
<Component.SubMenu>
|
||||||
|
{
|
||||||
|
containers.map(container => {
|
||||||
|
const { name } = container;
|
||||||
|
return (
|
||||||
|
<Component.MenuItem key={name} onClick={Util.prevDefault(() => this.execShell(name))} className="flex align-center">
|
||||||
|
<Component.StatusBrick/>
|
||||||
|
{name}
|
||||||
|
</Component.MenuItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Component.SubMenu>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Component.MenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./*.ts",
|
||||||
|
"./*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { LensMainExtension, windowManager } from "@k8slens/extensions";
|
||||||
|
import { supportPageURL } from "./src/support.route";
|
||||||
|
|
||||||
|
export default class SupportPageMainExtension extends LensMainExtension {
|
||||||
|
appMenus = [
|
||||||
|
{
|
||||||
|
parentId: "help",
|
||||||
|
label: "Support",
|
||||||
|
click() {
|
||||||
|
windowManager.navigate(supportPageURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+3921
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-support-page",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens support page",
|
||||||
|
"main": "dist/main.js",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack -p",
|
||||||
|
"dev": "webpack --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"@types/node": "^14.11.11",
|
||||||
|
"@types/react": "^16.9.53",
|
||||||
|
"@types/react-router": "^5.1.8",
|
||||||
|
"@types/webpack": "^4.41.17",
|
||||||
|
"css-loader": "^5.0.0",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"sass-loader": "^10.0.4",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Component, LensRendererExtension, Navigation } from "@k8slens/extensions";
|
||||||
|
import { supportPageRoute, supportPageURL } from "./src/support.route";
|
||||||
|
import { Support } from "./src/support";
|
||||||
|
|
||||||
|
export default class SupportPageRendererExtension extends LensRendererExtension {
|
||||||
|
globalPages = [
|
||||||
|
{
|
||||||
|
...supportPageRoute,
|
||||||
|
url: supportPageURL(),
|
||||||
|
hideInMenu: true,
|
||||||
|
components: {
|
||||||
|
Page: Support,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
statusBarItems = [
|
||||||
|
{
|
||||||
|
item: (
|
||||||
|
<div
|
||||||
|
className="flex align-center gaps hover-highlight"
|
||||||
|
onClick={() => Navigation.navigate(supportPageURL())}
|
||||||
|
>
|
||||||
|
<Component.Icon material="help_outline" small/>
|
||||||
|
<span>Support</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { RouteProps } from "react-router";
|
||||||
|
|
||||||
|
export const supportPageRoute: RouteProps = {
|
||||||
|
path: "/support"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const supportPageURL = () => supportPageRoute.path.toString();
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.PageLayout.Support {
|
||||||
|
a[target=_blank] {
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "launch";
|
||||||
|
font: small "Material Icons";
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// TODO: support localization / figure out how to extract / consume i18n strings
|
||||||
|
|
||||||
|
import "./support.scss"
|
||||||
|
import React from "react"
|
||||||
|
import { observer } from "mobx-react"
|
||||||
|
import { App, Component } from "@k8slens/extensions";
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class Support extends React.Component {
|
||||||
|
render() {
|
||||||
|
const { PageLayout } = Component;
|
||||||
|
const { slackUrl, issuesTrackerUrl } = App;
|
||||||
|
return (
|
||||||
|
<PageLayout showOnTop className="Support" header={<h2>Support</h2>}>
|
||||||
|
<h2>Community Slack Channel</h2>
|
||||||
|
<p>
|
||||||
|
Ask a question, see what's being discussed, join the conversation <a href={slackUrl} target="_blank">here</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Report an Issue</h2>
|
||||||
|
<p>
|
||||||
|
Review existing issues or open a new one <a href={issuesTrackerUrl} target="_blank">here</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/*<h2><Trans>Commercial Support</Trans></h2>*/}
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"paths": {
|
||||||
|
"*": [
|
||||||
|
"node_modules/*",
|
||||||
|
"../../types/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"renderer.tsx",
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import path from "path"
|
||||||
|
|
||||||
|
const outputPath = path.resolve(__dirname, 'dist');
|
||||||
|
|
||||||
|
const lensExternals = {
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx",
|
||||||
|
"mobx-react": "var global.MobxReact",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
entry: './main.ts',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-main",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
lensExternals,
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'main.js',
|
||||||
|
path: outputPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.s?css$/,
|
||||||
|
use: [
|
||||||
|
"style-loader",
|
||||||
|
"css-loader",
|
||||||
|
"sass-loader",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
lensExternals,
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: outputPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { LensMainExtension } from "@k8slens/extensions";
|
||||||
|
import { telemetryPreferencesStore } from "./src/telemetry-preferences-store"
|
||||||
|
import { tracker } from "./src/tracker";
|
||||||
|
|
||||||
|
export default class TelemetryMainExtension extends LensMainExtension {
|
||||||
|
|
||||||
|
async onActivate() {
|
||||||
|
console.log("telemetry main extension activated")
|
||||||
|
tracker.start()
|
||||||
|
await telemetryPreferencesStore.loadExtension(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivate() {
|
||||||
|
tracker.stop()
|
||||||
|
console.log("telemetry main extension deactivated")
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+3861
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-telemetry",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens telemetry",
|
||||||
|
"main": "dist/main.js",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack -p",
|
||||||
|
"dev": "webpack --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"node-machine-id": "^1.1.12",
|
||||||
|
"universal-analytics": "^0.4.23"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
|
import { telemetryPreferencesStore } from "./src/telemetry-preferences-store"
|
||||||
|
import { TelemetryPreferenceHint, TelemetryPreferenceInput } from "./src/telemetry-preference"
|
||||||
|
import { tracker } from "./src/tracker"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default class TelemetryRendererExtension extends LensRendererExtension {
|
||||||
|
appPreferences = [
|
||||||
|
{
|
||||||
|
title: "Telemetry & Usage Tracking",
|
||||||
|
components: {
|
||||||
|
Hint: () => <TelemetryPreferenceHint/>,
|
||||||
|
Input: () => <TelemetryPreferenceInput telemetry={telemetryPreferencesStore}/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
async onActivate() {
|
||||||
|
console.log("telemetry extension activated")
|
||||||
|
tracker.start()
|
||||||
|
await telemetryPreferencesStore.loadExtension(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Component } from "@k8slens/extensions"
|
||||||
|
import React from "react"
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { TelemetryPreferencesStore } from "./telemetry-preferences-store"
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class TelemetryPreferenceInput extends React.Component<{telemetry: TelemetryPreferencesStore}, {}> {
|
||||||
|
render() {
|
||||||
|
const { telemetry } = this.props
|
||||||
|
return (
|
||||||
|
<Component.Checkbox
|
||||||
|
label="Allow telemetry & usage tracking"
|
||||||
|
value={telemetry.enabled}
|
||||||
|
onChange={v => { telemetry.enabled = v; }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TelemetryPreferenceHint extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<span>Telemetry & usage data is collected to continuously improve the Lens experience.</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Store } from "@k8slens/extensions";
|
||||||
|
import { toJS } from "mobx"
|
||||||
|
|
||||||
|
export type TelemetryPreferencesModel = {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TelemetryPreferencesStore extends Store.ExtensionStore<TelemetryPreferencesModel> {
|
||||||
|
private constructor() {
|
||||||
|
super({
|
||||||
|
configName: "preferences-store",
|
||||||
|
defaults: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get enabled() {
|
||||||
|
return this.data.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set enabled(v: boolean) {
|
||||||
|
this.data.enabled = v
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): TelemetryPreferencesModel {
|
||||||
|
return toJS({
|
||||||
|
enabled: this.data.enabled
|
||||||
|
}, {
|
||||||
|
recurseEverything: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const telemetryPreferencesStore = TelemetryPreferencesStore.getInstance<TelemetryPreferencesStore>()
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { EventBus, Util, Store, App } from "@k8slens/extensions"
|
||||||
|
import ua from "universal-analytics"
|
||||||
|
import { machineIdSync } from "node-machine-id"
|
||||||
|
import { telemetryPreferencesStore } from "./telemetry-preferences-store"
|
||||||
|
|
||||||
|
export class Tracker extends Util.Singleton {
|
||||||
|
static readonly GA_ID = "UA-159377374-1"
|
||||||
|
|
||||||
|
protected eventHandlers: Array<(ev: EventBus.AppEvent ) => void> = []
|
||||||
|
protected started = false
|
||||||
|
protected visitor: ua.Visitor
|
||||||
|
protected machineId: string = null;
|
||||||
|
protected ip: string = null;
|
||||||
|
protected appVersion: string;
|
||||||
|
protected locale: string;
|
||||||
|
protected electronUA: string;
|
||||||
|
|
||||||
|
protected reportInterval: NodeJS.Timeout
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
super();
|
||||||
|
try {
|
||||||
|
this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false })
|
||||||
|
} catch (error) {
|
||||||
|
this.visitor = ua(Tracker.GA_ID)
|
||||||
|
}
|
||||||
|
this.visitor.set("dl", "https://telemetry.k8slens.dev")
|
||||||
|
this.visitor.set("ua", `Lens ${App.version} (${this.getOS()})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (this.started === true) { return }
|
||||||
|
|
||||||
|
this.started = true
|
||||||
|
|
||||||
|
const handler = (ev: EventBus.AppEvent) => {
|
||||||
|
this.event(ev.name, ev.action, ev.params)
|
||||||
|
}
|
||||||
|
this.eventHandlers.push(handler)
|
||||||
|
EventBus.appEventBus.addListener(handler)
|
||||||
|
this.reportInterval = setInterval(() => {
|
||||||
|
this.reportData()
|
||||||
|
}, 60 * 60 * 1000) // report every 1h
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (!this.started) { return }
|
||||||
|
|
||||||
|
this.started = false
|
||||||
|
|
||||||
|
for (const handler of this.eventHandlers) {
|
||||||
|
EventBus.appEventBus.removeListener(handler)
|
||||||
|
}
|
||||||
|
if (this.reportInterval) {
|
||||||
|
clearInterval(this.reportInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async isTelemetryAllowed(): Promise<boolean> {
|
||||||
|
return telemetryPreferencesStore.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
protected reportData() {
|
||||||
|
const clustersList = Store.clusterStore.clustersList
|
||||||
|
|
||||||
|
this.event("generic-data", "report", {
|
||||||
|
appVersion: App.version,
|
||||||
|
clustersCount: clustersList.length,
|
||||||
|
workspacesCount: Store.workspaceStore.workspacesList.length
|
||||||
|
})
|
||||||
|
|
||||||
|
clustersList.forEach((cluster) => {
|
||||||
|
if (!cluster?.metadata.lastSeen) { return }
|
||||||
|
this.reportClusterData(cluster)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected reportClusterData(cluster: Store.ClusterModel) {
|
||||||
|
this.event("cluster-data", "report", {
|
||||||
|
id: cluster.metadata.id,
|
||||||
|
kubernetesVersion: cluster.metadata.version,
|
||||||
|
distribution: cluster.metadata.distribution,
|
||||||
|
nodesCount: cluster.metadata.nodes,
|
||||||
|
lastSeen: cluster.metadata.lastSeen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getOS() {
|
||||||
|
let os = ""
|
||||||
|
if (App.isMac) {
|
||||||
|
os = "MacOS"
|
||||||
|
} else if(App.isWindows) {
|
||||||
|
os = "Windows"
|
||||||
|
} else if (App.isLinux) {
|
||||||
|
os = "Linux"
|
||||||
|
if (App.isSnap) {
|
||||||
|
os += "; Snap"
|
||||||
|
} else {
|
||||||
|
os += "; AppImage"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
os = "Unknown"
|
||||||
|
}
|
||||||
|
return os
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async event(eventCategory: string, eventAction: string, otherParams = {}) {
|
||||||
|
try {
|
||||||
|
const allowed = await this.isTelemetryAllowed();
|
||||||
|
if (!allowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.visitor.event({
|
||||||
|
ec: eventCategory,
|
||||||
|
ea: eventAction,
|
||||||
|
...otherParams,
|
||||||
|
}).send()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to track "${eventCategory}:${eventAction}"`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tracker = Tracker.getInstance<Tracker>();
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"paths": {
|
||||||
|
"*": [
|
||||||
|
"node_modules/*",
|
||||||
|
"../../types/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"renderer.ts",
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: './main.ts',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-main",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'main.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: './renderer.tsx',
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx",
|
||||||
|
"mobx-react": "var global.MobxReact"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: 'renderer.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -166,8 +166,8 @@ describe("Lens integration tests", () => {
|
|||||||
pages: [{
|
pages: [{
|
||||||
name: "Cluster",
|
name: "Cluster",
|
||||||
href: "cluster",
|
href: "cluster",
|
||||||
expectedSelector: "div.ClusterNoMetrics p",
|
expectedSelector: "div.Cluster div.label",
|
||||||
expectedText: "Metrics are not available due"
|
expectedText: "Master"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -389,13 +389,13 @@ describe("Lens integration tests", () => {
|
|||||||
it(`shows ${drawer} drawer`, async () => {
|
it(`shows ${drawer} drawer`, async () => {
|
||||||
expect(clusterAdded).toBe(true)
|
expect(clusterAdded).toBe(true)
|
||||||
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
|
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
|
||||||
await app.client.waitUntilTextExists(`a[href="/${pages[0].href}"]`, pages[0].name)
|
await app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pages.forEach(({ name, href, expectedSelector, expectedText }) => {
|
pages.forEach(({ name, href, expectedSelector, expectedText }) => {
|
||||||
it(`shows ${drawer}->${name} page`, async () => {
|
it(`shows ${drawer}->${name} page`, async () => {
|
||||||
expect(clusterAdded).toBe(true)
|
expect(clusterAdded).toBe(true)
|
||||||
await app.client.click(`a[href="/${href}"]`)
|
await app.client.click(`a[href^="/${href}"]`)
|
||||||
await app.client.waitUntilTextExists(expectedSelector, expectedText)
|
await app.client.waitUntilTextExists(expectedSelector, expectedText)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -404,7 +404,7 @@ describe("Lens integration tests", () => {
|
|||||||
it(`hides ${drawer} drawer`, async () => {
|
it(`hides ${drawer} drawer`, async () => {
|
||||||
expect(clusterAdded).toBe(true)
|
expect(clusterAdded).toBe(true)
|
||||||
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
|
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
|
||||||
await expect(app.client.waitUntilTextExists(`a[href="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow()
|
await expect(app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -440,8 +440,8 @@ describe("Lens integration tests", () => {
|
|||||||
it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => {
|
it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => {
|
||||||
expect(clusterAdded).toBe(true)
|
expect(clusterAdded).toBe(true)
|
||||||
await app.client.click(".sidebar-nav #workloads span.link-text")
|
await app.client.click(".sidebar-nav #workloads span.link-text")
|
||||||
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")
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export function setup(): Application {
|
|||||||
path: AppPaths[process.platform],
|
path: AppPaths[process.platform],
|
||||||
startTimeout: 30000,
|
startTimeout: 30000,
|
||||||
waitTimeout: 60000,
|
waitTimeout: 60000,
|
||||||
chromeDriverArgs: ['remote-debugging-port=9222'],
|
|
||||||
env: {
|
env: {
|
||||||
CICD: "true"
|
CICD: "true"
|
||||||
}
|
}
|
||||||
@@ -27,6 +26,6 @@ export async function tearDown(app: Application) {
|
|||||||
try {
|
try {
|
||||||
process.kill(pid, "SIGKILL");
|
process.kill(pid, "SIGKILL");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+26
-18
@@ -88,7 +88,7 @@ msgid "Active"
|
|||||||
msgstr "Active"
|
msgstr "Active"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:127
|
||||||
msgid "Add Cluster"
|
msgid "Add Cluster"
|
||||||
msgstr "Add Cluster"
|
msgstr "Add Cluster"
|
||||||
|
|
||||||
@@ -219,11 +219,11 @@ msgstr "Allocatable"
|
|||||||
msgid "Allow Privilege Escalation"
|
msgid "Allow Privilege Escalation"
|
||||||
msgstr "Allow Privilege Escalation"
|
msgstr "Allow Privilege Escalation"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:169
|
#: src/renderer/components/+preferences/preferences.tsx:172
|
||||||
msgid "Allow telemetry & usage tracking"
|
msgid "Allow telemetry & usage tracking"
|
||||||
msgstr "Allow telemetry & usage tracking"
|
msgstr "Allow telemetry & usage tracking"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:161
|
#: src/renderer/components/+preferences/preferences.tsx:164
|
||||||
msgid "Allow untrusted Certificate Authorities"
|
msgid "Allow untrusted Certificate Authorities"
|
||||||
msgstr "Allow untrusted Certificate Authorities"
|
msgstr "Allow untrusted Certificate Authorities"
|
||||||
|
|
||||||
@@ -301,6 +301,14 @@ msgstr "Associate clusters and choose the ones you want to access via quick laun
|
|||||||
msgid "Auth App Role"
|
msgid "Auth App Role"
|
||||||
msgstr "Auth App Role"
|
msgstr "Auth App Role"
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:160
|
||||||
|
msgid "Auto start-up"
|
||||||
|
msgstr "Auto start-up"
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:161
|
||||||
|
msgid "Automatically start Lens on login"
|
||||||
|
msgstr "Automatically start Lens on login"
|
||||||
|
|
||||||
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
||||||
#: src/renderer/components/wizard/wizard.tsx:130
|
#: src/renderer/components/wizard/wizard.tsx:130
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
@@ -422,7 +430,7 @@ msgstr "Cancel"
|
|||||||
msgid "Capacity"
|
msgid "Capacity"
|
||||||
msgstr "Capacity"
|
msgstr "Capacity"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:160
|
#: src/renderer/components/+preferences/preferences.tsx:163
|
||||||
msgid "Certificate Trust"
|
msgid "Certificate Trust"
|
||||||
msgstr "Certificate Trust"
|
msgstr "Certificate Trust"
|
||||||
|
|
||||||
@@ -817,7 +825,7 @@ msgstr "Desired Healthy"
|
|||||||
msgid "Desired number of replicas"
|
msgid "Desired number of replicas"
|
||||||
msgstr "Desired number of replicas"
|
msgstr "Desired number of replicas"
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:62
|
||||||
msgid "Disconnect"
|
msgid "Disconnect"
|
||||||
msgstr "Disconnect"
|
msgstr "Disconnect"
|
||||||
|
|
||||||
@@ -831,7 +839,7 @@ msgstr "Disk"
|
|||||||
msgid "Disk:"
|
msgid "Disk:"
|
||||||
msgstr "Disk:"
|
msgstr "Disk:"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:165
|
#: src/renderer/components/+preferences/preferences.tsx:168
|
||||||
msgid "Does not affect cluster communications!"
|
msgid "Does not affect cluster communications!"
|
||||||
msgstr "Does not affect cluster communications!"
|
msgstr "Does not affect cluster communications!"
|
||||||
|
|
||||||
@@ -927,8 +935,8 @@ msgstr "Environment"
|
|||||||
msgid "Error stack"
|
msgid "Error stack"
|
||||||
msgstr "Error stack"
|
msgstr "Error stack"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:89
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:88
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:130
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:129
|
||||||
msgid "Error while adding cluster(s): {0}"
|
msgid "Error while adding cluster(s): {0}"
|
||||||
msgstr "Error while adding cluster(s): {0}"
|
msgstr "Error while adding cluster(s): {0}"
|
||||||
|
|
||||||
@@ -1581,7 +1589,7 @@ msgstr "Namespaces"
|
|||||||
msgid "Namespaces: {0}"
|
msgid "Namespaces: {0}"
|
||||||
msgstr "Namespaces: {0}"
|
msgstr "Namespaces: {0}"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:164
|
#: src/renderer/components/+preferences/preferences.tsx:167
|
||||||
msgid "Needed with some corporate proxies that do certificate re-writing."
|
msgid "Needed with some corporate proxies that do certificate re-writing."
|
||||||
msgstr "Needed with some corporate proxies that do certificate re-writing."
|
msgstr "Needed with some corporate proxies that do certificate re-writing."
|
||||||
|
|
||||||
@@ -1798,7 +1806,7 @@ msgstr "Persistent Volume Claims"
|
|||||||
msgid "Persistent Volumes"
|
msgid "Persistent Volumes"
|
||||||
msgstr "Persistent Volumes"
|
msgstr "Persistent Volumes"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:75
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:74
|
||||||
msgid "Please select at least one cluster context"
|
msgid "Please select at least one cluster context"
|
||||||
msgstr "Please select at least one cluster context"
|
msgstr "Please select at least one cluster context"
|
||||||
|
|
||||||
@@ -2025,8 +2033,8 @@ msgstr "Releases"
|
|||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:152
|
#: src/renderer/components/+preferences/preferences.tsx:152
|
||||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:73
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:79
|
||||||
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:49
|
#: src/renderer/components/menu/menu-actions.tsx:49
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:85
|
#: src/renderer/components/menu/menu-actions.tsx:85
|
||||||
@@ -2470,7 +2478,7 @@ msgstr "Set"
|
|||||||
msgid "Set quota"
|
msgid "Set quota"
|
||||||
msgstr "Set quota"
|
msgstr "Set quota"
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:53
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:51
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Settings"
|
msgstr "Settings"
|
||||||
|
|
||||||
@@ -2613,7 +2621,7 @@ msgstr "Submitting.."
|
|||||||
msgid "Subsets"
|
msgid "Subsets"
|
||||||
msgstr "Subsets"
|
msgstr "Subsets"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:122
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:121
|
||||||
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
||||||
msgstr "Successfully imported <0>{0}</0> cluster(s)"
|
msgstr "Successfully imported <0>{0}</0> cluster(s)"
|
||||||
|
|
||||||
@@ -2635,11 +2643,11 @@ msgstr "TLS"
|
|||||||
msgid "Taints"
|
msgid "Taints"
|
||||||
msgstr "Taints"
|
msgstr "Taints"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:168
|
#: src/renderer/components/+preferences/preferences.tsx:171
|
||||||
msgid "Telemetry & Usage Tracking"
|
msgid "Telemetry & Usage Tracking"
|
||||||
msgstr "Telemetry & Usage Tracking"
|
msgstr "Telemetry & Usage Tracking"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:171
|
#: src/renderer/components/+preferences/preferences.tsx:174
|
||||||
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
||||||
msgstr "Telemetry & usage data is collected to continuously improve the Lens experience."
|
msgstr "Telemetry & usage data is collected to continuously improve the Lens experience."
|
||||||
|
|
||||||
@@ -2675,7 +2683,7 @@ msgstr "This field must be a valid path"
|
|||||||
msgid "This is the quick launch menu."
|
msgid "This is the quick launch menu."
|
||||||
msgstr "This is the quick launch menu."
|
msgstr "This is the quick launch menu."
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:163
|
#: src/renderer/components/+preferences/preferences.tsx:166
|
||||||
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
||||||
msgstr "This will make Lens to trust ANY certificate authority without any validations."
|
msgstr "This will make Lens to trust ANY certificate authority without any validations."
|
||||||
|
|
||||||
@@ -2953,7 +2961,7 @@ msgstr "listKind"
|
|||||||
msgid "never"
|
msgid "never"
|
||||||
msgstr "never"
|
msgstr "never"
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
||||||
msgid "new"
|
msgid "new"
|
||||||
msgstr "new"
|
msgstr "new"
|
||||||
|
|
||||||
|
|||||||
+26
-18
@@ -88,7 +88,7 @@ msgid "Active"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:127
|
||||||
msgid "Add Cluster"
|
msgid "Add Cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -219,11 +219,11 @@ msgstr ""
|
|||||||
msgid "Allow Privilege Escalation"
|
msgid "Allow Privilege Escalation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:169
|
#: src/renderer/components/+preferences/preferences.tsx:172
|
||||||
msgid "Allow telemetry & usage tracking"
|
msgid "Allow telemetry & usage tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:161
|
#: src/renderer/components/+preferences/preferences.tsx:164
|
||||||
msgid "Allow untrusted Certificate Authorities"
|
msgid "Allow untrusted Certificate Authorities"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -301,6 +301,14 @@ msgstr ""
|
|||||||
msgid "Auth App Role"
|
msgid "Auth App Role"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:160
|
||||||
|
msgid "Auto start-up"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:161
|
||||||
|
msgid "Automatically start Lens on login"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
||||||
#: src/renderer/components/wizard/wizard.tsx:130
|
#: src/renderer/components/wizard/wizard.tsx:130
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
@@ -422,7 +430,7 @@ msgstr ""
|
|||||||
msgid "Capacity"
|
msgid "Capacity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:160
|
#: src/renderer/components/+preferences/preferences.tsx:163
|
||||||
msgid "Certificate Trust"
|
msgid "Certificate Trust"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -813,7 +821,7 @@ msgstr ""
|
|||||||
msgid "Desired number of replicas"
|
msgid "Desired number of replicas"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:62
|
||||||
msgid "Disconnect"
|
msgid "Disconnect"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -827,7 +835,7 @@ msgstr ""
|
|||||||
msgid "Disk:"
|
msgid "Disk:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:165
|
#: src/renderer/components/+preferences/preferences.tsx:168
|
||||||
msgid "Does not affect cluster communications!"
|
msgid "Does not affect cluster communications!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -923,8 +931,8 @@ msgstr ""
|
|||||||
msgid "Error stack"
|
msgid "Error stack"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:89
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:88
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:130
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:129
|
||||||
msgid "Error while adding cluster(s): {0}"
|
msgid "Error while adding cluster(s): {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1572,7 +1580,7 @@ msgstr ""
|
|||||||
msgid "Namespaces: {0}"
|
msgid "Namespaces: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:164
|
#: src/renderer/components/+preferences/preferences.tsx:167
|
||||||
msgid "Needed with some corporate proxies that do certificate re-writing."
|
msgid "Needed with some corporate proxies that do certificate re-writing."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1781,7 +1789,7 @@ msgstr ""
|
|||||||
msgid "Persistent Volumes"
|
msgid "Persistent Volumes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:75
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:74
|
||||||
msgid "Please select at least one cluster context"
|
msgid "Please select at least one cluster context"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2008,8 +2016,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:152
|
#: src/renderer/components/+preferences/preferences.tsx:152
|
||||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:73
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:79
|
||||||
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:49
|
#: src/renderer/components/menu/menu-actions.tsx:49
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:85
|
#: src/renderer/components/menu/menu-actions.tsx:85
|
||||||
@@ -2453,7 +2461,7 @@ msgstr ""
|
|||||||
msgid "Set quota"
|
msgid "Set quota"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:53
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:51
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2596,7 +2604,7 @@ msgstr ""
|
|||||||
msgid "Subsets"
|
msgid "Subsets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:122
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:121
|
||||||
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2618,11 +2626,11 @@ msgstr ""
|
|||||||
msgid "Taints"
|
msgid "Taints"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:168
|
#: src/renderer/components/+preferences/preferences.tsx:171
|
||||||
msgid "Telemetry & Usage Tracking"
|
msgid "Telemetry & Usage Tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:171
|
#: src/renderer/components/+preferences/preferences.tsx:174
|
||||||
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2658,7 +2666,7 @@ msgstr ""
|
|||||||
msgid "This is the quick launch menu."
|
msgid "This is the quick launch menu."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:163
|
#: src/renderer/components/+preferences/preferences.tsx:166
|
||||||
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2936,7 +2944,7 @@ msgstr ""
|
|||||||
msgid "never"
|
msgid "never"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
||||||
msgid "new"
|
msgid "new"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+26
-18
@@ -89,7 +89,7 @@ msgid "Active"
|
|||||||
msgstr "Активный"
|
msgstr "Активный"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:310
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:127
|
||||||
msgid "Add Cluster"
|
msgid "Add Cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -220,11 +220,11 @@ msgstr ""
|
|||||||
msgid "Allow Privilege Escalation"
|
msgid "Allow Privilege Escalation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:169
|
#: src/renderer/components/+preferences/preferences.tsx:172
|
||||||
msgid "Allow telemetry & usage tracking"
|
msgid "Allow telemetry & usage tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:161
|
#: src/renderer/components/+preferences/preferences.tsx:164
|
||||||
msgid "Allow untrusted Certificate Authorities"
|
msgid "Allow untrusted Certificate Authorities"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -302,6 +302,14 @@ msgstr ""
|
|||||||
msgid "Auth App Role"
|
msgid "Auth App Role"
|
||||||
msgstr "Auth App Role"
|
msgstr "Auth App Role"
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:160
|
||||||
|
msgid "Auto start-up"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/renderer/components/+preferences/preferences.tsx:161
|
||||||
|
msgid "Automatically start Lens on login"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
#: src/renderer/components/error-boundary/error-boundary.tsx:53
|
||||||
#: src/renderer/components/wizard/wizard.tsx:130
|
#: src/renderer/components/wizard/wizard.tsx:130
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
@@ -423,7 +431,7 @@ msgstr "Отмена"
|
|||||||
msgid "Capacity"
|
msgid "Capacity"
|
||||||
msgstr "Емкость"
|
msgstr "Емкость"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:160
|
#: src/renderer/components/+preferences/preferences.tsx:163
|
||||||
msgid "Certificate Trust"
|
msgid "Certificate Trust"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -818,7 +826,7 @@ msgstr ""
|
|||||||
msgid "Desired number of replicas"
|
msgid "Desired number of replicas"
|
||||||
msgstr "Нужный уровень реплик"
|
msgstr "Нужный уровень реплик"
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:62
|
||||||
msgid "Disconnect"
|
msgid "Disconnect"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -832,7 +840,7 @@ msgstr "Диск"
|
|||||||
msgid "Disk:"
|
msgid "Disk:"
|
||||||
msgstr "Диск:"
|
msgstr "Диск:"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:165
|
#: src/renderer/components/+preferences/preferences.tsx:168
|
||||||
msgid "Does not affect cluster communications!"
|
msgid "Does not affect cluster communications!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -928,8 +936,8 @@ msgstr "Среда"
|
|||||||
msgid "Error stack"
|
msgid "Error stack"
|
||||||
msgstr "Стэк ошибки"
|
msgstr "Стэк ошибки"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:89
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:88
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:130
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:129
|
||||||
msgid "Error while adding cluster(s): {0}"
|
msgid "Error while adding cluster(s): {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1582,7 +1590,7 @@ msgstr "Namespaces"
|
|||||||
msgid "Namespaces: {0}"
|
msgid "Namespaces: {0}"
|
||||||
msgstr "Namespaces: {0}"
|
msgstr "Namespaces: {0}"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:164
|
#: src/renderer/components/+preferences/preferences.tsx:167
|
||||||
msgid "Needed with some corporate proxies that do certificate re-writing."
|
msgid "Needed with some corporate proxies that do certificate re-writing."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1799,7 +1807,7 @@ msgstr "Persistent Volume Claims"
|
|||||||
msgid "Persistent Volumes"
|
msgid "Persistent Volumes"
|
||||||
msgstr "Persistent Volumes"
|
msgstr "Persistent Volumes"
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:75
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:74
|
||||||
msgid "Please select at least one cluster context"
|
msgid "Please select at least one cluster context"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2026,8 +2034,8 @@ msgstr "Релизы"
|
|||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:152
|
#: src/renderer/components/+preferences/preferences.tsx:152
|
||||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:73
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:79
|
||||||
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:49
|
#: src/renderer/components/menu/menu-actions.tsx:49
|
||||||
#: src/renderer/components/menu/menu-actions.tsx:85
|
#: src/renderer/components/menu/menu-actions.tsx:85
|
||||||
@@ -2471,7 +2479,7 @@ msgstr "Установлено"
|
|||||||
msgid "Set quota"
|
msgid "Set quota"
|
||||||
msgstr "Установить квоту"
|
msgstr "Установить квоту"
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:53
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:51
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2614,7 +2622,7 @@ msgstr "Применение.."
|
|||||||
msgid "Subsets"
|
msgid "Subsets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:122
|
#: src/renderer/components/+add-cluster/add-cluster.tsx:121
|
||||||
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
msgid "Successfully imported <0>{0}</0> cluster(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2636,11 +2644,11 @@ msgstr "TLS"
|
|||||||
msgid "Taints"
|
msgid "Taints"
|
||||||
msgstr "Метки блокировки"
|
msgstr "Метки блокировки"
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:168
|
#: src/renderer/components/+preferences/preferences.tsx:171
|
||||||
msgid "Telemetry & Usage Tracking"
|
msgid "Telemetry & Usage Tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:171
|
#: src/renderer/components/+preferences/preferences.tsx:174
|
||||||
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2676,7 +2684,7 @@ msgstr ""
|
|||||||
msgid "This is the quick launch menu."
|
msgid "This is the quick launch menu."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/+preferences/preferences.tsx:163
|
#: src/renderer/components/+preferences/preferences.tsx:166
|
||||||
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
msgid "This will make Lens to trust ANY certificate authority without any validations."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2954,7 +2962,7 @@ msgstr ""
|
|||||||
msgid "never"
|
msgid "never"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
|
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
|
||||||
msgid "new"
|
msgid "new"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+65
-13
@@ -2,7 +2,7 @@
|
|||||||
"name": "kontena-lens",
|
"name": "kontena-lens",
|
||||||
"productName": "Lens",
|
"productName": "Lens",
|
||||||
"description": "Lens - The Kubernetes IDE",
|
"description": "Lens - The Kubernetes IDE",
|
||||||
"version": "3.6.6",
|
"version": "4.0.0-alpha.2",
|
||||||
"main": "static/build/main.js",
|
"main": "static/build/main.js",
|
||||||
"copyright": "© 2020, Mirantis, Inc.",
|
"copyright": "© 2020, Mirantis, Inc.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -11,14 +11,18 @@
|
|||||||
"email": "info@k8slens.dev"
|
"email": "info@k8slens.dev"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -k \"yarn dev-run -C\" \"yarn dev:main\" \"yarn dev:renderer\"",
|
"dev": "concurrently -k \"yarn dev-run -C\" yarn:dev:*",
|
||||||
|
"dev-build": "concurrently yarn:compile:*",
|
||||||
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
||||||
"dev:main": "yarn compile:main --watch",
|
"dev:main": "yarn compile:main --watch",
|
||||||
"dev:renderer": "yarn compile:renderer --watch",
|
"dev:renderer": "yarn compile:renderer --watch",
|
||||||
|
"dev:extension-types": "yarn compile:extension-types --watch",
|
||||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||||
"compile:main": "webpack --config webpack.main.ts",
|
"compile:main": "webpack --config webpack.main.ts",
|
||||||
"compile:renderer": "webpack --config webpack.renderer.ts",
|
"compile:renderer": "webpack --config webpack.renderer.ts",
|
||||||
"compile:i18n": "lingui compile",
|
"compile:i18n": "lingui compile",
|
||||||
|
"compile:extension-types": "rollup --config src/extensions/rollup.config.js",
|
||||||
|
"npm:fix-package-version": "ts-node build/set_npm_version.ts",
|
||||||
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
||||||
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
||||||
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
||||||
@@ -32,8 +36,8 @@
|
|||||||
"download-bins": "concurrently yarn:download:*",
|
"download-bins": "concurrently yarn:download:*",
|
||||||
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
||||||
"download:helm": "yarn run ts-node build/download_helm.ts",
|
"download:helm": "yarn run ts-node build/download_helm.ts",
|
||||||
"lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/",
|
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
|
||||||
"rebuild-pty": "yarn run electron-rebuild -f -w node-pty"
|
"lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"bundledKubectlVersion": "1.17.11",
|
"bundledKubectlVersion": "1.17.11",
|
||||||
@@ -68,9 +72,15 @@
|
|||||||
},
|
},
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts"
|
"\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts"
|
||||||
}
|
},
|
||||||
|
"modulePathIgnorePatterns": [
|
||||||
|
"<rootDir>/dist"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
|
"files": [
|
||||||
|
"static/build/main.js"
|
||||||
|
],
|
||||||
"afterSign": "build/notarize.js",
|
"afterSign": "build/notarize.js",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
@@ -88,6 +98,19 @@
|
|||||||
"to": "static/",
|
"to": "static/",
|
||||||
"filter": "!**/main.js"
|
"filter": "!**/main.js"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "build/tray",
|
||||||
|
"to": "static/icons",
|
||||||
|
"filter": "*.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "extensions/",
|
||||||
|
"to": "./extensions/",
|
||||||
|
"filter": [
|
||||||
|
"**/*.js*",
|
||||||
|
"!**/node_modules"
|
||||||
|
]
|
||||||
|
},
|
||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"linux": {
|
"linux": {
|
||||||
@@ -156,6 +179,15 @@
|
|||||||
"confinement": "classic"
|
"confinement": "classic"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lens": {
|
||||||
|
"extensions": [
|
||||||
|
"telemetry",
|
||||||
|
"pod-menu",
|
||||||
|
"node-menu",
|
||||||
|
"metrics-cluster-feature",
|
||||||
|
"support-page"
|
||||||
|
]
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hapi/call": "^8.0.0",
|
"@hapi/call": "^8.0.0",
|
||||||
"@hapi/subtext": "^7.0.3",
|
"@hapi/subtext": "^7.0.3",
|
||||||
@@ -165,6 +197,7 @@
|
|||||||
"@types/fs-extra": "^9.0.1",
|
"@types/fs-extra": "^9.0.1",
|
||||||
"@types/http-proxy": "^1.17.4",
|
"@types/http-proxy": "^1.17.4",
|
||||||
"@types/js-yaml": "^3.12.4",
|
"@types/js-yaml": "^3.12.4",
|
||||||
|
"@types/jsdom": "^16.2.4",
|
||||||
"@types/jsonpath": "^0.2.0",
|
"@types/jsonpath": "^0.2.0",
|
||||||
"@types/lodash": "^4.14.155",
|
"@types/lodash": "^4.14.155",
|
||||||
"@types/marked": "^0.7.4",
|
"@types/marked": "^0.7.4",
|
||||||
@@ -185,8 +218,8 @@
|
|||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"handlebars": "^4.7.6",
|
"handlebars": "^4.7.6",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"immer": "^7.0.5",
|
|
||||||
"js-yaml": "^3.14.0",
|
"js-yaml": "^3.14.0",
|
||||||
|
"jsdom": "^16.4.0",
|
||||||
"jsonpath": "^1.0.2",
|
"jsonpath": "^1.0.2",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"mac-ca": "^1.0.4",
|
"mac-ca": "^1.0.4",
|
||||||
@@ -195,13 +228,11 @@
|
|||||||
"mobx": "^5.15.5",
|
"mobx": "^5.15.5",
|
||||||
"mobx-observable-history": "^1.0.3",
|
"mobx-observable-history": "^1.0.3",
|
||||||
"mock-fs": "^4.12.0",
|
"mock-fs": "^4.12.0",
|
||||||
"node-machine-id": "^1.1.12",
|
|
||||||
"node-pty": "^0.9.0",
|
"node-pty": "^0.9.0",
|
||||||
|
"npm": "^6.14.8",
|
||||||
"openid-client": "^3.15.2",
|
"openid-client": "^3.15.2",
|
||||||
"path-to-regexp": "^6.1.0",
|
"path-to-regexp": "^6.1.0",
|
||||||
"proper-lockfile": "^4.1.1",
|
"proper-lockfile": "^4.1.1",
|
||||||
"react-beautiful-dnd": "^13.0.0",
|
|
||||||
"react-router": "^5.2.0",
|
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
@@ -211,7 +242,6 @@
|
|||||||
"tar": "^6.0.2",
|
"tar": "^6.0.2",
|
||||||
"tcp-port-used": "^1.0.1",
|
"tcp-port-used": "^1.0.1",
|
||||||
"tempy": "^0.5.0",
|
"tempy": "^0.5.0",
|
||||||
"universal-analytics": "^0.4.20",
|
|
||||||
"uuid": "^8.1.0",
|
"uuid": "^8.1.0",
|
||||||
"win-ca": "^3.2.0",
|
"win-ca": "^3.2.0",
|
||||||
"winston": "^3.2.1",
|
"winston": "^3.2.1",
|
||||||
@@ -232,27 +262,44 @@
|
|||||||
"@lingui/macro": "^3.0.0-13",
|
"@lingui/macro": "^3.0.0-13",
|
||||||
"@lingui/react": "^3.0.0-13",
|
"@lingui/react": "^3.0.0-13",
|
||||||
"@material-ui/core": "^4.10.1",
|
"@material-ui/core": "^4.10.1",
|
||||||
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@types/chart.js": "^2.9.21",
|
"@types/chart.js": "^2.9.21",
|
||||||
"@types/circular-dependency-plugin": "^5.0.1",
|
"@types/circular-dependency-plugin": "^5.0.1",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
|
"@types/crypto-js": "^3.1.47",
|
||||||
"@types/dompurify": "^2.0.2",
|
"@types/dompurify": "^2.0.2",
|
||||||
|
"@types/electron-window-state": "^2.0.34",
|
||||||
|
"@types/fs-extra": "^9.0.1",
|
||||||
"@types/hapi": "^18.0.3",
|
"@types/hapi": "^18.0.3",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
"@types/html-webpack-plugin": "^3.2.3",
|
"@types/html-webpack-plugin": "^3.2.3",
|
||||||
|
"@types/http-proxy": "^1.17.4",
|
||||||
"@types/jest": "^25.2.3",
|
"@types/jest": "^25.2.3",
|
||||||
|
"@types/js-yaml": "^3.12.4",
|
||||||
|
"@types/jsonpath": "^0.2.0",
|
||||||
|
"@types/lodash": "^4.14.155",
|
||||||
|
"@types/marked": "^0.7.4",
|
||||||
"@types/material-ui": "^0.21.7",
|
"@types/material-ui": "^0.21.7",
|
||||||
"@types/md5-file": "^4.0.2",
|
"@types/md5-file": "^4.0.2",
|
||||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||||
|
"@types/mock-fs": "^4.10.0",
|
||||||
|
"@types/module-alias": "^2.0.0",
|
||||||
|
"@types/node": "^12.12.45",
|
||||||
|
"@types/npm": "^2.0.31",
|
||||||
"@types/progress-bar-webpack-plugin": "^2.1.0",
|
"@types/progress-bar-webpack-plugin": "^2.1.0",
|
||||||
|
"@types/proper-lockfile": "^4.1.1",
|
||||||
"@types/react": "^16.9.35",
|
"@types/react": "^16.9.35",
|
||||||
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
"@types/react-select": "^3.0.13",
|
"@types/react-select": "^3.0.13",
|
||||||
"@types/react-window": "^1.8.2",
|
"@types/react-window": "^1.8.2",
|
||||||
"@types/request": "^2.48.5",
|
"@types/request": "^2.48.5",
|
||||||
"@types/request-promise-native": "^1.0.17",
|
"@types/request-promise-native": "^1.0.17",
|
||||||
"@types/semver": "^7.2.0",
|
"@types/semver": "^7.2.0",
|
||||||
|
"@types/sharp": "^0.26.0",
|
||||||
"@types/shelljs": "^0.8.8",
|
"@types/shelljs": "^0.8.8",
|
||||||
"@types/spdy": "^3.4.4",
|
"@types/spdy": "^3.4.4",
|
||||||
|
"@types/tar": "^4.0.3",
|
||||||
"@types/tcp-port-used": "^1.0.0",
|
"@types/tcp-port-used": "^1.0.0",
|
||||||
"@types/tempy": "^0.3.0",
|
"@types/tempy": "^0.3.0",
|
||||||
"@types/terser-webpack-plugin": "^3.0.0",
|
"@types/terser-webpack-plugin": "^3.0.0",
|
||||||
@@ -280,7 +327,6 @@
|
|||||||
"electron": "^9.1.2",
|
"electron": "^9.1.2",
|
||||||
"electron-builder": "^22.7.0",
|
"electron-builder": "^22.7.0",
|
||||||
"electron-notarize": "^0.3.0",
|
"electron-notarize": "^0.3.0",
|
||||||
"electron-rebuild": "^1.11.0",
|
|
||||||
"eslint": "^7.7.0",
|
"eslint": "^7.7.0",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"flex.box": "^3.4.4",
|
"flex.box": "^3.4.4",
|
||||||
@@ -292,7 +338,6 @@
|
|||||||
"jest": "^26.0.1",
|
"jest": "^26.0.1",
|
||||||
"jest-mock-extended": "^1.0.10",
|
"jest-mock-extended": "^1.0.10",
|
||||||
"make-plural": "^6.2.1",
|
"make-plural": "^6.2.1",
|
||||||
"material-design-icons": "^3.0.1",
|
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"mobx-react": "^6.2.2",
|
"mobx-react": "^6.2.2",
|
||||||
"moment": "^2.26.0",
|
"moment": "^2.26.0",
|
||||||
@@ -303,12 +348,19 @@
|
|||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"progress-bar-webpack-plugin": "^2.1.0",
|
"progress-bar-webpack-plugin": "^2.1.0",
|
||||||
"raw-loader": "^4.0.1",
|
"raw-loader": "^4.0.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.14.0",
|
||||||
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-select": "^3.1.0",
|
"react-select": "^3.1.0",
|
||||||
"react-window": "^1.8.5",
|
"react-window": "^1.8.5",
|
||||||
|
"rollup": "^2.28.2",
|
||||||
|
"rollup-plugin-dts": "^1.4.13",
|
||||||
|
"rollup-plugin-ignore-import": "^1.3.2",
|
||||||
|
"rollup-pluginutils": "^2.8.2",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
|
"sharp": "^0.26.1",
|
||||||
"spectron": "11.0.0",
|
"spectron": "11.0.0",
|
||||||
"style-loader": "^1.2.1",
|
"style-loader": "^1.2.1",
|
||||||
"terser-webpack-plugin": "^3.0.3",
|
"terser-webpack-plugin": "^3.0.3",
|
||||||
|
|||||||
@@ -64,13 +64,13 @@ describe("empty config", () => {
|
|||||||
|
|
||||||
it("sets active cluster", () => {
|
it("sets active cluster", () => {
|
||||||
clusterStore.setActive("foo");
|
clusterStore.setActive("foo");
|
||||||
expect(clusterStore.activeCluster.id).toBe("foo");
|
expect(clusterStore.active.id).toBe("foo");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("with prod and dev clusters added", () => {
|
describe("with prod and dev clusters added", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
clusterStore.addCluster(
|
clusterStore.addClusters(
|
||||||
new Cluster({
|
new Cluster({
|
||||||
id: "prod",
|
id: "prod",
|
||||||
contextName: "prod",
|
contextName: "prod",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { appEventBus, AppEvent } from "../event-bus"
|
||||||
|
|
||||||
|
describe("event bus tests", () => {
|
||||||
|
describe("emit", () => {
|
||||||
|
it("emits an event", () => {
|
||||||
|
let event: AppEvent = null
|
||||||
|
appEventBus.addListener((data) => {
|
||||||
|
event = data
|
||||||
|
})
|
||||||
|
|
||||||
|
appEventBus.emit({name: "foo", action: "bar"})
|
||||||
|
expect(event.name).toBe("foo")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -10,7 +10,7 @@ jest.mock("electron", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
import { WorkspaceStore } from "../workspace-store"
|
import { Workspace, WorkspaceStore } from "../workspace-store"
|
||||||
|
|
||||||
describe("workspace store tests", () => {
|
describe("workspace store tests", () => {
|
||||||
describe("for an empty config", () => {
|
describe("for an empty config", () => {
|
||||||
@@ -35,16 +35,16 @@ describe("workspace store tests", () => {
|
|||||||
it("cannot remove the default workspace", () => {
|
it("cannot remove the default workspace", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
expect(() => ws.removeWorkspace(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
|
expect(() => ws.removeWorkspaceById(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
|
||||||
})
|
})
|
||||||
|
|
||||||
it("can update default workspace name", () => {
|
it("can update default workspace name", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: WorkspaceStore.defaultId,
|
id: WorkspaceStore.defaultId,
|
||||||
name: "foobar",
|
name: "foobar",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.currentWorkspace.name).toBe("foobar");
|
expect(ws.currentWorkspace.name).toBe("foobar");
|
||||||
})
|
})
|
||||||
@@ -52,10 +52,10 @@ describe("workspace store tests", () => {
|
|||||||
it("can add workspaces", () => {
|
it("can add workspaces", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "123",
|
id: "123",
|
||||||
name: "foobar",
|
name: "foobar",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.getById("123").name).toBe("foobar");
|
expect(ws.getById("123").name).toBe("foobar");
|
||||||
})
|
})
|
||||||
@@ -69,10 +69,10 @@ describe("workspace store tests", () => {
|
|||||||
it("can set a existent workspace to be active", () => {
|
it("can set a existent workspace to be active", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "abc",
|
id: "abc",
|
||||||
name: "foobar",
|
name: "foobar",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(() => ws.setActive("abc")).not.toThrowError();
|
expect(() => ws.setActive("abc")).not.toThrowError();
|
||||||
})
|
})
|
||||||
@@ -80,15 +80,15 @@ describe("workspace store tests", () => {
|
|||||||
it("can remove a workspace", () => {
|
it("can remove a workspace", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "123",
|
id: "123",
|
||||||
name: "foobar",
|
name: "foobar",
|
||||||
});
|
}));
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "1234",
|
id: "1234",
|
||||||
name: "foobar 1",
|
name: "foobar 1",
|
||||||
});
|
}));
|
||||||
ws.removeWorkspace("123");
|
ws.removeWorkspaceById("123");
|
||||||
|
|
||||||
expect(ws.workspaces.size).toBe(2);
|
expect(ws.workspaces.size).toBe(2);
|
||||||
})
|
})
|
||||||
@@ -96,10 +96,10 @@ describe("workspace store tests", () => {
|
|||||||
it("cannot create workspace with existent name", () => {
|
it("cannot create workspace with existent name", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "someid",
|
id: "someid",
|
||||||
name: "default",
|
name: "default",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
||||||
})
|
})
|
||||||
@@ -107,10 +107,10 @@ describe("workspace store tests", () => {
|
|||||||
it("cannot create workspace with empty name", () => {
|
it("cannot create workspace with empty name", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "random",
|
id: "random",
|
||||||
name: "",
|
name: "",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
||||||
})
|
})
|
||||||
@@ -118,10 +118,10 @@ describe("workspace store tests", () => {
|
|||||||
it("cannot create workspace with ' ' name", () => {
|
it("cannot create workspace with ' ' name", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "random",
|
id: "random",
|
||||||
name: " ",
|
name: " ",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
||||||
})
|
})
|
||||||
@@ -129,10 +129,10 @@ describe("workspace store tests", () => {
|
|||||||
it("trim workspace name", () => {
|
it("trim workspace name", () => {
|
||||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||||
|
|
||||||
ws.saveWorkspace({
|
ws.addWorkspace(new Workspace({
|
||||||
id: "random",
|
id: "random",
|
||||||
name: "default ",
|
name: "default ",
|
||||||
});
|
}));
|
||||||
|
|
||||||
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
expect(ws.workspacesList.length).toBe(1); // default workspace only
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -56,13 +56,17 @@ export class BaseStore<T = any> extends Singleton {
|
|||||||
...confOptions,
|
...confOptions,
|
||||||
projectName: "lens",
|
projectName: "lens",
|
||||||
projectVersion: getAppVersion(),
|
projectVersion: getAppVersion(),
|
||||||
cwd: (app || remote.app).getPath("userData"),
|
cwd: this.storePath(),
|
||||||
});
|
});
|
||||||
logger.info(`[STORE]: LOADED from ${this.storeConfig.path}`);
|
logger.info(`[STORE]: LOADED from ${this.storeConfig.path}`);
|
||||||
this.fromStore(this.storeConfig.store);
|
this.fromStore(this.storeConfig.store);
|
||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected storePath() {
|
||||||
|
return (app || remote.app).getPath("userData")
|
||||||
|
}
|
||||||
|
|
||||||
protected async saveToFile(model: T) {
|
protected async saveToFile(model: T) {
|
||||||
logger.info(`[STORE]: SAVING ${this.name}`);
|
logger.info(`[STORE]: SAVING ${this.name}`);
|
||||||
// todo: update when fixed https://github.com/sindresorhus/conf/issues/114
|
// todo: update when fixed https://github.com/sindresorhus/conf/issues/114
|
||||||
@@ -86,13 +90,19 @@ export class BaseStore<T = any> extends Singleton {
|
|||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
const callback = (event: IpcRendererEvent, model: T) => {
|
const callback = (event: IpcRendererEvent, model: T) => {
|
||||||
logger.silly(`[STORE]: SYNC ${this.name} from main`, { model });
|
logger.silly(`[STORE]: SYNC ${this.name} from main`, { model });
|
||||||
this.onSync(model);
|
this.onSyncFromMain(model);
|
||||||
};
|
};
|
||||||
ipcRenderer.on(this.syncChannel, callback);
|
ipcRenderer.on(this.syncChannel, callback);
|
||||||
this.syncDisposers.push(() => ipcRenderer.off(this.syncChannel, callback));
|
this.syncDisposers.push(() => ipcRenderer.off(this.syncChannel, callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onSyncFromMain(model: T) {
|
||||||
|
this.applyWithoutSync(() => {
|
||||||
|
this.onSync(model)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
unregisterIpcListener() {
|
unregisterIpcListener() {
|
||||||
ipcRenderer.removeAllListeners(this.syncChannel)
|
ipcRenderer.removeAllListeners(this.syncChannel)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-24
@@ -1,6 +1,8 @@
|
|||||||
import { createIpcChannel } from "./ipc";
|
import { createIpcChannel } from "./ipc";
|
||||||
import { ClusterId, clusterStore } from "./cluster-store";
|
import { ClusterId, clusterStore } from "./cluster-store";
|
||||||
import { tracker } from "./tracker";
|
import { extensionLoader } from "../extensions/extension-loader"
|
||||||
|
import { appEventBus } from "./event-bus"
|
||||||
|
import { ResourceApplier } from "../main/resource-applier";
|
||||||
|
|
||||||
export const clusterIpc = {
|
export const clusterIpc = {
|
||||||
activate: createIpcChannel({
|
activate: createIpcChannel({
|
||||||
@@ -19,6 +21,7 @@ export const clusterIpc = {
|
|||||||
const cluster = clusterStore.getById(clusterId);
|
const cluster = clusterStore.getById(clusterId);
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
|
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
|
||||||
|
extensionLoader.broadcastExtensions(frameId)
|
||||||
return cluster.pushState();
|
return cluster.pushState();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -28,44 +31,29 @@ export const clusterIpc = {
|
|||||||
channel: "cluster:refresh",
|
channel: "cluster:refresh",
|
||||||
handle: (clusterId: ClusterId) => {
|
handle: (clusterId: ClusterId) => {
|
||||||
const cluster = clusterStore.getById(clusterId);
|
const cluster = clusterStore.getById(clusterId);
|
||||||
if (cluster) return cluster.refresh();
|
if (cluster) return cluster.refresh({ refreshMetadata: true })
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
disconnect: createIpcChannel({
|
disconnect: createIpcChannel({
|
||||||
channel: "cluster:disconnect",
|
channel: "cluster:disconnect",
|
||||||
handle: (clusterId: ClusterId) => {
|
handle: (clusterId: ClusterId) => {
|
||||||
tracker.event("cluster", "stop");
|
appEventBus.emit({name: "cluster", action: "stop"});
|
||||||
return clusterStore.getById(clusterId)?.disconnect();
|
return clusterStore.getById(clusterId)?.disconnect();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
installFeature: createIpcChannel({
|
kubectlApplyAll: createIpcChannel({
|
||||||
channel: "cluster:install-feature",
|
channel: "cluster:kubectl-apply-all",
|
||||||
handle: async (clusterId: ClusterId, feature: string, config?: any) => {
|
handle: (clusterId: ClusterId, resources: string[]) => {
|
||||||
tracker.event("cluster", "install", feature);
|
appEventBus.emit({name: "cluster", action: "kubectl-apply-all"})
|
||||||
const cluster = clusterStore.getById(clusterId);
|
const cluster = clusterStore.getById(clusterId);
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
await cluster.installFeature(feature, config)
|
const applier = new ResourceApplier(cluster)
|
||||||
|
applier.kubectlApplyAll(resources)
|
||||||
} else {
|
} else {
|
||||||
throw `${clusterId} is not a valid cluster id`;
|
throw `${clusterId} is not a valid cluster id`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
uninstallFeature: createIpcChannel({
|
|
||||||
channel: "cluster:uninstall-feature",
|
|
||||||
handle: (clusterId: ClusterId, feature: string) => {
|
|
||||||
tracker.event("cluster", "uninstall", feature);
|
|
||||||
return clusterStore.getById(clusterId)?.uninstallFeature(feature)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
upgradeFeature: createIpcChannel({
|
|
||||||
channel: "cluster:upgrade-feature",
|
|
||||||
handle: (clusterId: ClusterId, feature: string, config?: any) => {
|
|
||||||
tracker.event("cluster", "upgrade", feature);
|
|
||||||
return clusterStore.getById(clusterId)?.upgradeFeature(feature, config)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
+73
-25
@@ -1,18 +1,18 @@
|
|||||||
|
import type { WorkspaceId } from "./workspace-store";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { app, ipcRenderer, remote, webFrame, webContents } from "electron";
|
import { app, ipcRenderer, remote, webFrame } from "electron";
|
||||||
import { unlink } from "fs-extra";
|
import { unlink } from "fs-extra";
|
||||||
import { action, computed, observable, toJS } from "mobx";
|
import { action, computed, observable, toJS } from "mobx";
|
||||||
import { BaseStore } from "./base-store";
|
import { BaseStore } from "./base-store";
|
||||||
import { Cluster, ClusterState } from "../main/cluster";
|
import { Cluster, ClusterState } from "../main/cluster";
|
||||||
import migrations from "../migrations/cluster-store"
|
import migrations from "../migrations/cluster-store"
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { tracker } from "./tracker";
|
import { appEventBus } from "./event-bus"
|
||||||
import { dumpConfigYaml } from "./kube-helpers";
|
import { dumpConfigYaml } from "./kube-helpers";
|
||||||
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||||
import { KubeConfig } from "@kubernetes/client-node";
|
import { KubeConfig } from "@kubernetes/client-node";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import move from "array-move";
|
import move from "array-move";
|
||||||
import type { WorkspaceId } from "./workspace-store";
|
|
||||||
|
|
||||||
export interface ClusterIconUpload {
|
export interface ClusterIconUpload {
|
||||||
clusterId: string;
|
clusterId: string;
|
||||||
@@ -20,6 +20,10 @@ export interface ClusterIconUpload {
|
|||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClusterMetadata {
|
||||||
|
[key: string]: string | number | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ClusterStoreModel {
|
export interface ClusterStoreModel {
|
||||||
activeCluster?: ClusterId; // last opened cluster
|
activeCluster?: ClusterId; // last opened cluster
|
||||||
clusters?: ClusterModel[]
|
clusters?: ClusterModel[]
|
||||||
@@ -29,10 +33,12 @@ export type ClusterId = string;
|
|||||||
|
|
||||||
export interface ClusterModel {
|
export interface ClusterModel {
|
||||||
id: ClusterId;
|
id: ClusterId;
|
||||||
|
kubeConfigPath: string;
|
||||||
workspace?: WorkspaceId;
|
workspace?: WorkspaceId;
|
||||||
contextName?: string;
|
contextName?: string;
|
||||||
preferences?: ClusterPreferences;
|
preferences?: ClusterPreferences;
|
||||||
kubeConfigPath: string;
|
metadata?: ClusterMetadata;
|
||||||
|
ownerRef?: string;
|
||||||
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
kubeConfig?: string; // yaml
|
kubeConfig?: string; // yaml
|
||||||
@@ -67,25 +73,34 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@observable activeCluster: ClusterId;
|
||||||
|
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
||||||
|
@observable clusters = observable.map<ClusterId, Cluster>();
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
super({
|
super({
|
||||||
configName: "lens-cluster-store",
|
configName: "lens-cluster-store",
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.pushStateToViewsPeriodically()
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable activeClusterId: ClusterId;
|
protected pushStateToViewsPeriodically() {
|
||||||
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
if (!ipcRenderer) {
|
||||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
// This is a bit of a hack, we need to do this because we might loose messages that are sent before a view is ready
|
||||||
|
setInterval(() => {
|
||||||
|
this.pushState()
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registerIpcListener() {
|
registerIpcListener() {
|
||||||
logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`)
|
logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`)
|
||||||
ipcRenderer.on("cluster:state", (event, model: ClusterState) => {
|
ipcRenderer.on("cluster:state", (event, clusterId: string, state: ClusterState) => {
|
||||||
this.applyWithoutSync(() => {
|
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, clusterId, state);
|
||||||
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, model);
|
this.getById(clusterId)?.setState(state)
|
||||||
this.getById(model.id)?.updateModel(model);
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +109,35 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
ipcRenderer.removeAllListeners("cluster:state")
|
ipcRenderer.removeAllListeners("cluster:state")
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get activeCluster(): Cluster | null {
|
pushState() {
|
||||||
return this.getById(this.activeClusterId);
|
this.clusters.forEach((c) => {
|
||||||
|
c.pushState()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeClusterId() {
|
||||||
|
return this.activeCluster
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get clustersList(): Cluster[] {
|
@computed get clustersList(): Cluster[] {
|
||||||
return Array.from(this.clusters.values());
|
return Array.from(this.clusters.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed get enabledClustersList(): Cluster[] {
|
||||||
|
return this.clustersList.filter((c) => c.enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get active(): Cluster | null {
|
||||||
|
return this.getById(this.activeCluster);
|
||||||
|
}
|
||||||
|
|
||||||
isActive(id: ClusterId) {
|
isActive(id: ClusterId) {
|
||||||
return this.activeClusterId === id;
|
return this.activeCluster === id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setActive(id: ClusterId) {
|
setActive(id: ClusterId) {
|
||||||
this.activeClusterId = id;
|
this.activeCluster = this.clusters.has(id) ? id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@@ -140,22 +169,38 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
addCluster(...models: ClusterModel[]) {
|
addClusters(...models: ClusterModel[]): Cluster[] {
|
||||||
|
const clusters: Cluster[] = []
|
||||||
models.forEach(model => {
|
models.forEach(model => {
|
||||||
tracker.event("cluster", "add");
|
clusters.push(this.addCluster(model))
|
||||||
const cluster = new Cluster(model);
|
|
||||||
this.clusters.set(model.id, cluster);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return clusters
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
addCluster(model: ClusterModel | Cluster ): Cluster {
|
||||||
|
appEventBus.emit({name: "cluster", action: "add"})
|
||||||
|
let cluster = model as Cluster;
|
||||||
|
if (!(model instanceof Cluster)) {
|
||||||
|
cluster = new Cluster(model)
|
||||||
|
}
|
||||||
|
this.clusters.set(model.id, cluster);
|
||||||
|
return cluster
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeCluster(model: ClusterModel) {
|
||||||
|
await this.removeById(model.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async removeById(clusterId: ClusterId) {
|
async removeById(clusterId: ClusterId) {
|
||||||
tracker.event("cluster", "remove");
|
appEventBus.emit({name: "cluster", action: "remove"})
|
||||||
const cluster = this.getById(clusterId);
|
const cluster = this.getById(clusterId);
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
this.clusters.delete(clusterId);
|
this.clusters.delete(clusterId);
|
||||||
if (this.activeClusterId === clusterId) {
|
if (this.activeCluster === clusterId) {
|
||||||
this.activeClusterId = null;
|
this.setActive(null);
|
||||||
}
|
}
|
||||||
// remove only custom kubeconfigs (pasted as text)
|
// remove only custom kubeconfigs (pasted as text)
|
||||||
if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) {
|
if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) {
|
||||||
@@ -184,6 +229,9 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
cluster.updateModel(clusterModel);
|
cluster.updateModel(clusterModel);
|
||||||
} else {
|
} else {
|
||||||
cluster = new Cluster(clusterModel);
|
cluster = new Cluster(clusterModel);
|
||||||
|
if (!cluster.isManaged) {
|
||||||
|
cluster.enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
newClusters.set(clusterModel.id, cluster);
|
newClusters.set(clusterModel.id, cluster);
|
||||||
}
|
}
|
||||||
@@ -195,14 +243,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activeClusterId = newClusters.has(activeCluster) ? activeCluster : null;
|
this.activeCluster = newClusters.has(activeCluster) ? activeCluster : null;
|
||||||
this.clusters.replace(newClusters);
|
this.clusters.replace(newClusters);
|
||||||
this.removedClusters.replace(removedClusters);
|
this.removedClusters.replace(removedClusters);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON(): ClusterStoreModel {
|
toJSON(): ClusterStoreModel {
|
||||||
return toJS({
|
return toJS({
|
||||||
activeCluster: this.activeClusterId,
|
activeCluster: this.activeCluster,
|
||||||
clusters: this.clustersList.map(cluster => cluster.toJSON()),
|
clusters: this.clustersList.map(cluster => cluster.toJSON()),
|
||||||
}, {
|
}, {
|
||||||
recurseEverything: true
|
recurseEverything: true
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { EventEmitter } from "./event-emitter"
|
||||||
|
|
||||||
|
export type AppEvent = {
|
||||||
|
name: string;
|
||||||
|
action: string;
|
||||||
|
params?: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appEventBus = new EventEmitter<[AppEvent]>()
|
||||||
File renamed without changes.
@@ -1,49 +0,0 @@
|
|||||||
import { app, App, remote } from "electron"
|
|
||||||
import ua from "universal-analytics"
|
|
||||||
import { machineIdSync } from "node-machine-id"
|
|
||||||
import Singleton from "./utils/singleton";
|
|
||||||
import { userStore } from "./user-store"
|
|
||||||
import logger from "../main/logger";
|
|
||||||
|
|
||||||
export class Tracker extends Singleton {
|
|
||||||
static readonly GA_ID = "UA-159377374-1"
|
|
||||||
|
|
||||||
protected visitor: ua.Visitor
|
|
||||||
protected machineId: string = null;
|
|
||||||
protected ip: string = null;
|
|
||||||
protected appVersion: string;
|
|
||||||
protected locale: string;
|
|
||||||
protected electronUA: string;
|
|
||||||
|
|
||||||
private constructor(app: App) {
|
|
||||||
super();
|
|
||||||
try {
|
|
||||||
this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false })
|
|
||||||
} catch (error) {
|
|
||||||
this.visitor = ua(Tracker.GA_ID)
|
|
||||||
}
|
|
||||||
this.visitor.set("dl", "https://telemetry.k8slens.dev")
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async isTelemetryAllowed(): Promise<boolean> {
|
|
||||||
return userStore.preferences.allowTelemetry;
|
|
||||||
}
|
|
||||||
|
|
||||||
async event(eventCategory: string, eventAction: string, otherParams = {}) {
|
|
||||||
try {
|
|
||||||
const allowed = await this.isTelemetryAllowed();
|
|
||||||
if (!allowed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.visitor.event({
|
|
||||||
ec: eventCategory,
|
|
||||||
ea: eventAction,
|
|
||||||
...otherParams,
|
|
||||||
}).send()
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(`Failed to track "${eventCategory}:${eventAction}"`, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const tracker = Tracker.getInstance<Tracker>(app || remote.app);
|
|
||||||
+27
-10
@@ -7,7 +7,7 @@ import { BaseStore } from "./base-store";
|
|||||||
import migrations from "../migrations/user-store"
|
import migrations from "../migrations/user-store"
|
||||||
import { getAppVersion } from "./utils/app-version";
|
import { getAppVersion } from "./utils/app-version";
|
||||||
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
||||||
import { tracker } from "./tracker";
|
import { appEventBus } from "./event-bus"
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ export interface UserPreferences {
|
|||||||
downloadKubectlBinaries?: boolean;
|
downloadKubectlBinaries?: boolean;
|
||||||
downloadBinariesPath?: string;
|
downloadBinariesPath?: string;
|
||||||
kubectlBinariesPath?: string;
|
kubectlBinariesPath?: string;
|
||||||
|
openAtLogin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> {
|
export class UserStore extends BaseStore<UserStoreModel> {
|
||||||
@@ -38,14 +39,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
});
|
});
|
||||||
|
|
||||||
// track telemetry availability
|
this.handleOnLoad();
|
||||||
reaction(() => this.preferences.allowTelemetry, allowed => {
|
|
||||||
tracker.event("telemetry", allowed ? "enabled" : "disabled");
|
|
||||||
});
|
|
||||||
|
|
||||||
// refresh new contexts
|
|
||||||
this.whenLoaded.then(this.refreshNewContexts);
|
|
||||||
reaction(() => this.kubeConfigPath, this.refreshNewContexts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable lastSeenAppVersion = "0.0.0"
|
@observable lastSeenAppVersion = "0.0.0"
|
||||||
@@ -59,8 +53,31 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
colorTheme: UserStore.defaultTheme,
|
colorTheme: UserStore.defaultTheme,
|
||||||
downloadMirror: "default",
|
downloadMirror: "default",
|
||||||
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
||||||
|
openAtLogin: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected async handleOnLoad() {
|
||||||
|
await this.whenLoaded;
|
||||||
|
|
||||||
|
// refresh new contexts
|
||||||
|
this.refreshNewContexts();
|
||||||
|
reaction(() => this.kubeConfigPath, this.refreshNewContexts);
|
||||||
|
|
||||||
|
if (app) {
|
||||||
|
// track telemetry availability
|
||||||
|
reaction(() => this.preferences.allowTelemetry, allowed => {
|
||||||
|
appEventBus.emit({name: "telemetry", action: allowed ? "enabled" : "disabled"})
|
||||||
|
});
|
||||||
|
|
||||||
|
// open at system start-up
|
||||||
|
reaction(() => this.preferences.openAtLogin, open => {
|
||||||
|
app.setLoginItemSettings({ openAtLogin: open });
|
||||||
|
}, {
|
||||||
|
fireImmediately: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get isNewVersion() {
|
get isNewVersion() {
|
||||||
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
||||||
}
|
}
|
||||||
@@ -77,7 +94,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
saveLastSeenAppVersion() {
|
saveLastSeenAppVersion() {
|
||||||
tracker.event("app", "whats-new-seen")
|
appEventBus.emit({name: "app", action: "whats-new-seen"})
|
||||||
this.lastSeenAppVersion = getAppVersion();
|
this.lastSeenAppVersion = getAppVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,3 +7,7 @@ export function getAppVersion(): string {
|
|||||||
export function getBundledKubectlVersion(): string {
|
export function getBundledKubectlVersion(): string {
|
||||||
return packageInfo.config.bundledKubectlVersion;
|
return packageInfo.config.bundledKubectlVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getBundledExtensions(): string[] {
|
||||||
|
return packageInfo.lens?.extensions || []
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { compile } from "path-to-regexp"
|
||||||
|
|
||||||
|
export interface IURLParams<P extends object = {}, Q extends object = {}> {
|
||||||
|
params?: P;
|
||||||
|
query?: Q;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildURL<P extends object = {}, Q extends object = {}>(path: string | any) {
|
||||||
|
const pathBuilder = compile(String(path));
|
||||||
|
return function ({ params, query }: IURLParams<P, Q> = {}) {
|
||||||
|
const queryParams = query ? new URLSearchParams(Object.entries(query)).toString() : ""
|
||||||
|
return pathBuilder(params) + (queryParams ? `?${queryParams}` : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ import { defineGlobal } from "./utils/defineGlobal";
|
|||||||
|
|
||||||
export const isMac = process.platform === "darwin"
|
export const isMac = process.platform === "darwin"
|
||||||
export const isWindows = process.platform === "win32"
|
export const isWindows = process.platform === "win32"
|
||||||
|
export const isLinux = process.platform === "linux"
|
||||||
export const isDebugging = process.env.DEBUG === "true";
|
export const isDebugging = process.env.DEBUG === "true";
|
||||||
|
export const isSnap = !!process.env["SNAP"]
|
||||||
export const isProduction = process.env.NODE_ENV === "production"
|
export const isProduction = process.env.NODE_ENV === "production"
|
||||||
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
||||||
export const isDevelopment = !isTestEnv && !isProduction;
|
export const isDevelopment = !isTestEnv && !isProduction;
|
||||||
|
|||||||
Loaded 100 of 352 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user