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
@ -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"
|
||||||
|
|||||||
@ -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: [
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@ -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
|
||||||
|
|||||||
11
LICENSE
11
LICENSE
@ -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
|
||||||
|
|||||||
17
Makefile
17
Makefile
@ -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\*.*
|
||||||
@ -62,4 +75,4 @@ else
|
|||||||
rm -rf binaries/client/*
|
rm -rf binaries/client/*
|
||||||
rm -rf dist/*
|
rm -rf dist/*
|
||||||
rm -rf static/build/*
|
rm -rf static/build/*
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
51
build/build_tray_icon.ts
Normal file
51
build/build_tray_icon.ts
Normal file
@ -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 });
|
||||||
|
});
|
||||||
9
build/set_npm_version.ts
Normal file
9
build/set_npm_version.ts
Normal file
@ -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))
|
||||||
BIN
build/tray/tray_icon.png
Normal file
BIN
build/tray/tray_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 448 B |
BIN
build/tray/tray_icon@2x.png
Normal file
BIN
build/tray/tray_icon@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 973 B |
BIN
build/tray/tray_icon@3x.png
Normal file
BIN
build/tray/tray_icon@3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
build/tray/tray_icon_dark.png
Normal file
BIN
build/tray/tray_icon_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
BIN
build/tray/tray_icon_dark@2x.png
Normal file
BIN
build/tray/tray_icon_dark@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
build/tray/tray_icon_dark@3x.png
Normal file
BIN
build/tray/tray_icon_dark@3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
2
extensions/example-extension/.gitignore
vendored
Normal file
2
extensions/example-extension/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
5
extensions/example-extension/Makefile
Normal file
5
extensions/example-extension/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
11
extensions/example-extension/README.md
Normal file
11
extensions/example-extension/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Lens Example Extension
|
||||||
|
|
||||||
|
*TODO*: add more info
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
`npm run build`
|
||||||
|
|
||||||
|
## Dev
|
||||||
|
|
||||||
|
`npm run dev`
|
||||||
11
extensions/example-extension/main.ts
Normal file
11
extensions/example-extension/main.ts
Normal file
@ -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
extensions/example-extension/package-lock.json
generated
Normal file
3468
extensions/example-extension/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
extensions/example-extension/package.json
Normal file
24
extensions/example-extension/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/example-extension/page.tsx
Normal file
29
extensions/example-extension/page.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
16
extensions/example-extension/renderer.tsx
Normal file
16
extensions/example-extension/renderer.tsx
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
26
extensions/example-extension/tsconfig.json
Normal file
26
extensions/example-extension/tsconfig.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
65
extensions/example-extension/webpack.config.js
Normal file
65
extensions/example-extension/webpack.config.js
Normal file
@ -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'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
5
extensions/metrics-cluster-feature/Makefile
Normal file
5
extensions/metrics-cluster-feature/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
3574
extensions/metrics-cluster-feature/package-lock.json
generated
Normal file
3574
extensions/metrics-cluster-feature/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
extensions/metrics-cluster-feature/package.json
Normal file
25
extensions/metrics-cluster-feature/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
extensions/metrics-cluster-feature/renderer.tsx
Normal file
23
extensions/metrics-cluster-feature/renderer.tsx
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
96
extensions/metrics-cluster-feature/src/metrics-feature.ts
Normal file
96
extensions/metrics-cluster-feature/src/metrics-feature.ts
Normal file
@ -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"}) }
|
||||||
|
}
|
||||||
26
extensions/metrics-cluster-feature/tsconfig.json
Normal file
26
extensions/metrics-cluster-feature/tsconfig.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
38
extensions/metrics-cluster-feature/webpack.config.js
Normal file
38
extensions/metrics-cluster-feature/webpack.config.js
Normal file
@ -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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
5
extensions/node-menu/Makefile
Normal file
5
extensions/node-menu/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
3512
extensions/node-menu/package-lock.json
generated
Normal file
3512
extensions/node-menu/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
extensions/node-menu/package.json
Normal file
23
extensions/node-menu/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
extensions/node-menu/renderer.tsx
Normal file
15
extensions/node-menu/renderer.tsx
Normal file
@ -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} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
73
extensions/node-menu/src/node-menu.tsx
Normal file
73
extensions/node-menu/src/node-menu.tsx
Normal file
@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
extensions/node-menu/tsconfig.json
Normal file
26
extensions/node-menu/tsconfig.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
35
extensions/node-menu/webpack.config.js
Normal file
35
extensions/node-menu/webpack.config.js
Normal file
@ -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'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
5
extensions/pod-menu/Makefile
Normal file
5
extensions/pod-menu/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
3512
extensions/pod-menu/package-lock.json
generated
Normal file
3512
extensions/pod-menu/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
extensions/pod-menu/package.json
Normal file
23
extensions/pod-menu/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
extensions/pod-menu/renderer.tsx
Normal file
23
extensions/pod-menu/renderer.tsx
Normal file
@ -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} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
57
extensions/pod-menu/src/logs-menu.tsx
Normal file
57
extensions/pod-menu/src/logs-menu.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
extensions/pod-menu/src/shell-menu.tsx
Normal file
63
extensions/pod-menu/src/shell-menu.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
extensions/pod-menu/tsconfig.json
Normal file
26
extensions/pod-menu/tsconfig.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
35
extensions/pod-menu/webpack.config.js
Normal file
35
extensions/pod-menu/webpack.config.js
Normal file
@ -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'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
5
extensions/support-page/Makefile
Normal file
5
extensions/support-page/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
14
extensions/support-page/main.ts
Normal file
14
extensions/support-page/main.ts
Normal file
@ -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
extensions/support-page/package-lock.json
generated
Normal file
3921
extensions/support-page/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
extensions/support-page/package.json
Normal file
28
extensions/support-page/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
extensions/support-page/renderer.tsx
Normal file
31
extensions/support-page/renderer.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
extensions/support-page/src/support.route.ts
Normal file
7
extensions/support-page/src/support.route.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { RouteProps } from "react-router";
|
||||||
|
|
||||||
|
export const supportPageRoute: RouteProps = {
|
||||||
|
path: "/support"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const supportPageURL = () => supportPageRoute.path.toString();
|
||||||
13
extensions/support-page/src/support.scss
Normal file
13
extensions/support-page/src/support.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/support-page/src/support.tsx
Normal file
29
extensions/support-page/src/support.tsx
Normal file
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/support-page/tsconfig.json
Normal file
29
extensions/support-page/tsconfig.json
Normal file
@ -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/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
75
extensions/support-page/webpack.config.ts
Normal file
75
extensions/support-page/webpack.config.ts
Normal file
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
5
extensions/telemetry/Makefile
Normal file
5
extensions/telemetry/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install-deps:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
build: install-deps
|
||||||
|
npm run build
|
||||||
17
extensions/telemetry/main.ts
Normal file
17
extensions/telemetry/main.ts
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
3861
extensions/telemetry/package-lock.json
generated
Normal file
3861
extensions/telemetry/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
extensions/telemetry/package.json
Normal file
26
extensions/telemetry/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
extensions/telemetry/renderer.tsx
Normal file
23
extensions/telemetry/renderer.tsx
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
extensions/telemetry/src/telemetry-preference.tsx
Normal file
26
extensions/telemetry/src/telemetry-preference.tsx
Normal file
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
extensions/telemetry/src/telemetry-preferences-store.ts
Normal file
35
extensions/telemetry/src/telemetry-preferences-store.ts
Normal file
@ -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>()
|
||||||
124
extensions/telemetry/src/tracker.ts
Normal file
124
extensions/telemetry/src/tracker.ts
Normal file
@ -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>();
|
||||||
29
extensions/telemetry/tsconfig.json
Normal file
29
extensions/telemetry/tsconfig.json
Normal file
@ -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/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
67
extensions/telemetry/webpack.config.js
Normal file
67
extensions/telemetry/webpack.config.js
Normal file
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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 ""
|
||||||
|
|
||||||
|
|||||||
@ -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 ""
|
||||||
|
|
||||||
|
|||||||
78
package.json
78
package.json
@ -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",
|
||||||
|
|||||||
15
src/common/__tests__/event-bus.test.ts
Normal file
15
src/common/__tests__/event-bus.test.ts
Normal file
@ -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
|
||||||
})
|
})
|
||||||
@ -169,4 +169,4 @@ describe("workspace store tests", () => {
|
|||||||
expect(ws.currentWorkspaceId).toBe("abc");
|
expect(ws.currentWorkspaceId).toBe("abc");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
9
src/common/event-bus.ts
Normal file
9
src/common/event-bus.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { EventEmitter } from "./event-emitter"
|
||||||
|
|
||||||
|
export type AppEvent = {
|
||||||
|
name: string;
|
||||||
|
action: string;
|
||||||
|
params?: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appEventBus = new EventEmitter<[AppEvent]>()
|
||||||
@ -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);
|
|
||||||
@ -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 || []
|
||||||
|
}
|
||||||
|
|||||||
14
src/common/utils/buildUrl.ts
Normal file
14
src/common/utils/buildUrl.ts
Normal file
@ -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;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user