From f1b03990ea64ebde5dad70acef4a61e6cd1d12a9 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 9 Sep 2020 13:00:25 +0300 Subject: [PATCH 001/102] Extensions loading (#795) Signed-off-by: Roman Co-authored-by: Alex Andreev Co-authored-by: Lauri Nevala --- .gitignore | 6 +- package.json | 23 ++- src/common/cluster-store.ts | 5 +- src/common/vars.ts | 8 + src/extensions/example-extension/README.md | 3 + .../example-extension/example-extension.ts | 17 ++ src/extensions/example-extension/package.json | 11 ++ .../example-extension/tsconfig.json | 13 ++ src/extensions/extension-api.ts | 22 +++ src/extensions/extension-store.ts | 174 ++++++++++++++++++ src/extensions/extension.ts | 89 +++++++++ src/extensions/lens-runtime.ts | 19 ++ src/extensions/tsconfig.json | 13 ++ src/main/menu.ts | 10 +- src/renderer/bootstrap.tsx | 2 + .../+extensions/extensions.route.ts | 11 ++ .../components/+extensions/extensions.scss | 4 + .../components/+extensions/extensions.tsx | 31 ++++ src/renderer/components/+extensions/index.ts | 2 + .../components/cluster-icon/cluster-icon.scss | 1 - .../cluster-manager/cluster-manager.tsx | 2 + .../cluster-manager/clusters-menu.scss | 16 +- .../cluster-manager/clusters-menu.tsx | 9 +- .../cluster-manager/register-page.ts | 28 +++ src/renderer/lens-app.tsx | 6 + static/RELEASE_NOTES.md | 19 +- tsconfig.json | 1 - types/mocks.d.ts | 1 + webpack.dll.ts | 34 ---- webpack.renderer.ts | 31 +++- yarn.lock | 19 +- 31 files changed, 564 insertions(+), 66 deletions(-) create mode 100644 src/extensions/example-extension/README.md create mode 100644 src/extensions/example-extension/example-extension.ts create mode 100644 src/extensions/example-extension/package.json create mode 100644 src/extensions/example-extension/tsconfig.json create mode 100644 src/extensions/extension-api.ts create mode 100644 src/extensions/extension-store.ts create mode 100644 src/extensions/extension.ts create mode 100644 src/extensions/lens-runtime.ts create mode 100644 src/extensions/tsconfig.json create mode 100644 src/renderer/components/+extensions/extensions.route.ts create mode 100644 src/renderer/components/+extensions/extensions.scss create mode 100644 src/renderer/components/+extensions/extensions.tsx create mode 100644 src/renderer/components/+extensions/index.ts create mode 100644 src/renderer/components/cluster-manager/register-page.ts delete mode 100755 webpack.dll.ts diff --git a/.gitignore b/.gitignore index d6efc880e7..6ddf3f489c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,14 @@ dist/ -out/ node_modules/ .DS_Store yarn-error.log coverage/ tmp/ -static/build/** +static/build +static/types binaries/client/ binaries/server/ +src/extensions/*/*.js +src/extensions/*/*.d.ts locales/**/**.js lens.log diff --git a/package.json b/package.json index 9a1b57733e..920d90afb5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "3.6.0-beta.2", + "version": "3.6.0-rc.1", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", @@ -11,14 +11,16 @@ "email": "info@k8slens.dev" }, "scripts": { - "dev": "concurrently -k \"yarn dev-run -C\" \"yarn dev:main\" \"yarn dev:renderer\"", - "dev-run": "cross-env DEBUG=true nodemon --watch static/build/main.js --exec \"electron --inspect .\"", - "dev:main": "cross-env DEBUG=true yarn compile:main --watch", - "dev:renderer": "cross-env DEBUG=true yarn compile:renderer --watch", + "dev": "concurrently -k \"yarn dev-run -C\" yarn:dev:*", + "dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"", + "dev:main": "yarn compile:main --watch", + "dev:renderer": "yarn compile:renderer --watch", + "dev:extensions": "tsc --project src/extensions/example-extension --watch", "compile": "env NODE_ENV=production concurrently yarn:compile:*", "compile:main": "webpack --config webpack.main.ts", "compile:renderer": "webpack --config webpack.renderer.ts", "compile:i18n": "lingui compile", + "compile:extension-api.d.ts": "tsc --project src/extensions", "build:linux": "yarn compile && electron-builder --linux --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", @@ -69,7 +71,8 @@ }, "moduleNameMapper": { "\\.(css|scss)$": "/__mocks__/styleMock.ts" - } + }, + "modulePathIgnorePatterns": ["/dist"] }, "build": { "afterSign": "build/notarize.js", @@ -89,6 +92,11 @@ "to": "static/", "filter": "!**/main.js" }, + { + "from": "src/extensions/", + "to": "./extensions/", + "filter": "**/*.js*" + }, "LICENSE" ], "linux": { @@ -167,6 +175,7 @@ "@types/lodash": "^4.14.155", "@types/marked": "^0.7.4", "@types/mock-fs": "^4.10.0", + "@types/module-alias": "^2.0.0", "@types/node": "^12.12.45", "@types/proper-lockfile": "^4.1.1", "@types/react-beautiful-dnd": "^13.0.0", @@ -193,6 +202,7 @@ "mobx": "^5.15.5", "mobx-observable-history": "^1.0.3", "mock-fs": "^4.12.0", + "module-alias": "^2.2.2", "node-machine-id": "^1.1.12", "node-pty": "^0.9.0", "openid-client": "^3.15.2", @@ -269,7 +279,6 @@ "circular-dependency-plugin": "^5.2.0", "color": "^3.1.2", "concurrently": "^5.2.0", - "cross-env": "^7.0.2", "css-element-queries": "^1.2.3", "css-loader": "^3.5.3", "dompurify": "^2.0.11", diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 45bcad4d6f..a30c4d7e94 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -1,4 +1,4 @@ -import { WorkspaceId, workspaceStore } from "./workspace-store"; +import type { WorkspaceId } from "./workspace-store"; import path from "path"; import { app, ipcRenderer, remote } from "electron"; import { unlink } from "fs-extra"; @@ -13,7 +13,6 @@ import { saveToAppFiles } from "./utils/saveToAppFiles"; import { KubeConfig } from "@kubernetes/client-node"; import _ from "lodash"; import move from "array-move"; -import { is } from "immer/dist/internal"; export interface ClusterIconUpload { clusterId: string; @@ -64,7 +63,7 @@ export class ClusterStore extends BaseStore { static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string { const filePath = ClusterStore.getCustomKubeConfigPath(clusterId); const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig); - saveToAppFiles(filePath, fileContents, { mode: 0o600}); + saveToAppFiles(filePath, fileContents, { mode: 0o600 }); return filePath; } diff --git a/src/common/vars.ts b/src/common/vars.ts index ca28a2f99a..c17c54e118 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -2,6 +2,7 @@ import path from "path"; import packageInfo from "../../package.json" import { defineGlobal } from "./utils/defineGlobal"; +import { addAlias } from "module-alias"; export const isMac = process.platform === "darwin" export const isWindows = process.platform === "win32" @@ -21,6 +22,13 @@ export const rendererDir = path.join(contextDir, "src/renderer"); export const htmlTemplate = path.resolve(rendererDir, "template.html"); export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss"); +// Extensions +export const extensionsLibName = `${appName}-extensions.api` +export const extensionsDir = path.join(contextDir, "src/extensions"); + +// Special dynamic module aliases +addAlias("@lens/extensions", path.resolve(buildDir, `${extensionsLibName}.js`)); // fixme: provide path in prod + // Special runtime paths defineGlobal("__static", { get() { diff --git a/src/extensions/example-extension/README.md b/src/extensions/example-extension/README.md new file mode 100644 index 0000000000..eb8d01ac38 --- /dev/null +++ b/src/extensions/example-extension/README.md @@ -0,0 +1,3 @@ +# Lens Example Extension + +*TODO*: add more info \ No newline at end of file diff --git a/src/extensions/example-extension/example-extension.ts b/src/extensions/example-extension/example-extension.ts new file mode 100644 index 0000000000..28b4da5825 --- /dev/null +++ b/src/extensions/example-extension/example-extension.ts @@ -0,0 +1,17 @@ +import { LensExtension, Icon, LensRuntimeRendererEnv } from "@lens/extensions"; // fixme: map to generated types from "extension-api.d.ts" + +// todo: register custom icon in cluster-menu +// todo: register custom view by clicking the item + +export default class ExampleExtension extends LensExtension { + async enable(runtime: /*LensRuntimeRendererEnv*/ any): Promise { + try { + super.enable(runtime); + runtime.logger.info('EXAMPLE EXTENSION: ENABLE() override'); + } catch (err){ + console.error(err) + } + } +} + +// console.log("done")}/> \ No newline at end of file diff --git a/src/extensions/example-extension/package.json b/src/extensions/example-extension/package.json new file mode 100644 index 0000000000..d57a78aab5 --- /dev/null +++ b/src/extensions/example-extension/package.json @@ -0,0 +1,11 @@ +{ + "name": "extension-example", + "version": "1.0.0", + "description": "Example extension", + "main": "example-extension.ts", + "lens": { + "metadata": {} + }, + "dependencies": { + } +} diff --git a/src/extensions/example-extension/tsconfig.json b/src/extensions/example-extension/tsconfig.json new file mode 100644 index 0000000000..16081f6055 --- /dev/null +++ b/src/extensions/example-extension/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": ".", + "module": "CommonJS", + "sourceMap": false, + "declaration": false + }, + "include": [ + "../../../types", + "./example-extension.ts" + ] +} diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts new file mode 100644 index 0000000000..3dff72d00c --- /dev/null +++ b/src/extensions/extension-api.ts @@ -0,0 +1,22 @@ +// Lens-extensions api developer's kit +export type { LensRuntimeRendererEnv } from "./lens-runtime"; + +// APIs +export * from "./extension" + +// Common UI components +export * from "../renderer/components/icon" +export * from "../renderer/components/badge" +export * from "../renderer/components/tooltip" +export * from "../renderer/components/button" +export * from "../renderer/components/input" +export * from "../renderer/components/select" +export * from "../renderer/components/checkbox" +export * from "../renderer/components/radio" +export * from "../renderer/components/slider" +export * from "../renderer/components/spinner" +export * from "../renderer/components/tabs" +export * from "../renderer/components/line-progress" + +// Utils +export * from "../main/logger"; diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts new file mode 100644 index 0000000000..655c4d6ca7 --- /dev/null +++ b/src/extensions/extension-store.ts @@ -0,0 +1,174 @@ +import type { LensRuntimeRendererEnv } from "./lens-runtime"; +import path from "path"; +import fs from "fs-extra"; +import { action, observable, reaction, toJS, } from "mobx"; +import { BaseStore } from "../common/base-store"; +import { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./extension"; +import { isDevelopment, isProduction, isTestEnv } from "../common/vars"; +import logger from "../main/logger"; + +export interface ExtensionStoreModel { + version: ExtensionVersion; + extensions: Record +} + +export interface ExtensionModel { + id?: ExtensionId; // available in lens-extension instance + version: ExtensionVersion; + name: string; + manifestPath: string; + description?: string; + enabled?: boolean; + updateUrl?: string; +} + +export interface InstalledExtension { + manifestPath: string; + manifest: ExtensionManifest; + extensionModule: { + [name: string]: any; + default: new (model: ExtensionModel, manifest?: ExtensionManifest) => LensExtension + } +} + +export class ExtensionStore extends BaseStore { + private constructor() { + super({ + configName: "lens-extension-store", + syncEnabled: false, + }); + } + + @observable version: ExtensionVersion = "0.0.0"; + @observable extensions = observable.map(); + @observable removed = observable.map(); + @observable installed = observable.map([], { deep: false }); + + get folderPath(): string { + if (isDevelopment) { + return path.resolve(__static, "../src/extensions"); + } + return path.resolve(__static, "../extensions"); //todo figure out prod + } + + async load() { + await this.loadExtensions(); + return super.load(); + } + + autoEnableOnLoad(getLensRuntimeEnv: () => LensRuntimeRendererEnv, { delay = 0 } = {}) { + logger.info('[EXTENSIONS-STORE]: auto-activation loaded extensions: ON'); + return reaction(() => this.installed.toJS(), installedExtensions => { + installedExtensions.forEach(({ extensionModule, manifest, manifestPath }) => { + let instance = this.getById(manifestPath); + if (!instance) { + const LensExtensionClass = extensionModule.default; + instance = new LensExtensionClass({ ...manifest, manifestPath, id: manifestPath }, manifest); + instance.enable(getLensRuntimeEnv()); + this.extensions.set(manifestPath, instance); // save + } + }) + }, { + fireImmediately: true, + delay: delay, + }) + } + + getExtensionByManifest(manifestPath: string): InstalledExtension { + let manifestJson: ExtensionManifest; + let mainJs: string; + try { + manifestJson = __non_webpack_require__(manifestPath); // "__non_webpack_require__" converts to native node's require()-call + mainJs = path.resolve(path.dirname(manifestPath), manifestJson.main); + mainJs = mainJs.replace(/\.ts$/i, ".js"); // todo: compile *.ts on the fly? + const extensionModule = __non_webpack_require__(mainJs); + return { + manifestPath: manifestPath, + manifest: manifestJson, + extensionModule: extensionModule, + } + } catch (err) { + console.error(`[EXTENSION-STORE]: can't load extension at ${manifestPath}: ${err}`, { manifestJson, mainJs }); + } + } + + @action + async loadExtensions() { + const extensions = await this.loadFromFolder(this.folderPath); + const extManifestMap = new Map(extensions.map(ext => [ext.manifestPath, ext])); + this.installed.replace(extManifestMap); + } + + async loadFromFolder(folderPath: string): Promise { + const paths = await fs.readdir(folderPath); + const manifestsLoading = paths.map(fileName => { + const absPath = path.resolve(folderPath, fileName); + const manifestPath = path.resolve(absPath, "package.json"); + return fs.access(manifestPath, fs.constants.F_OK) + .then(() => this.getExtensionByManifest(manifestPath)) + .catch(() => null) + }); + let extensions = await Promise.all(manifestsLoading); + extensions = extensions.filter(v => !!v); // filter out files and invalid folders (without manifest.json) + console.info(`[EXTENSION-STORE]: ${extensions.length} extensions loaded`, { folderPath, extensions }); + return extensions; + } + + getById(id: ExtensionId): LensExtension { + return this.extensions.get(id); + } + + async removeById(id: ExtensionId) { + const extension = this.getById(id); + if (extension) { + await extension.uninstall(); + this.extensions.delete(id); + } + } + + @action + protected fromStore({ extensions, version }: ExtensionStoreModel) { + if (version) { + this.version = version; + } + if (extensions) { + const currentExtensions = new Map(Object.entries(extensions)); + this.extensions.forEach(extension => { + if (!currentExtensions.has(extension.id)) { + this.removed.set(extension.id, extension); + } + }) + currentExtensions.forEach(model => { + const extensionId = model.id || model.manifestPath; + const manifest = this.installed.get(extensionId); + if (!manifest) { + console.error(`[EXTENSION-STORE]: can't load extension manifest at ${model.manifestPath}`, { model }) + return; + } + const extensionInstance = this.getById(extensionId) + if (!extensionInstance) { + try { + const { manifest: manifestJson, extensionModule } = manifest; + const LensExtensionClass = extensionModule.default; + this.extensions.set(model.id, new LensExtensionClass(model, manifestJson)); + } catch (err) { + console.error(`[EXTENSION-STORE]: init extension failed: ${err}`, { model, manifest }) + } + } else { + extensionInstance.importModel(model); + } + }) + } + } + + toJSON(): ExtensionStoreModel { + return toJS({ + version: this.version, + extensions: this.extensions.toJSON(), + }, { + recurseEverything: true, + }) + } +} + +export const extensionStore = ExtensionStore.getInstance() diff --git a/src/extensions/extension.ts b/src/extensions/extension.ts new file mode 100644 index 0000000000..698422fb54 --- /dev/null +++ b/src/extensions/extension.ts @@ -0,0 +1,89 @@ +import type { ExtensionModel } from "./extension-store"; +import type { LensRuntimeRendererEnv } from "./lens-runtime"; +import { readJsonSync } from "fs-extra"; +import { action, observable, toJS } from "mobx"; +import extensionManifest from "./example-extension/package.json" +import logger from "../main/logger"; + +export type ExtensionId = string; // instance-id or abs path to "%lens-extension/manifest.json" +export type ExtensionVersion = string | number; +export type ExtensionManifest = typeof extensionManifest & ExtensionModel; + +export class LensExtension implements ExtensionModel { + public id: ExtensionId; + public updateUrl: string; + + @observable name = ""; + @observable description = ""; + @observable version: ExtensionVersion = "0.0.0"; + @observable manifest: ExtensionManifest; + @observable manifestPath: string; + @observable isEnabled = false; + @observable.ref runtime: Partial = {}; + + constructor(model: ExtensionModel, manifest: ExtensionManifest) { + this.importModel(model, manifest); + } + + @action + async importModel({ enabled, manifestPath, ...model }: ExtensionModel, manifest?: ExtensionManifest) { + try { + this.manifest = manifest || await readJsonSync(manifestPath, { throws: true }) + this.manifestPath = manifestPath; + Object.assign(this, model); + } catch (err) { + logger.error(`[EXTENSION]: cannot read manifest at ${manifestPath}`, { ...model, err: String(err) }) + this.disable(); + } + } + + async enable(runtime: LensRuntimeRendererEnv) { + this.isEnabled = true; + this.runtime = runtime; + console.log(`[EXTENSION]: enabled ${this.name}@${this.version}`, this.getMeta()); + } + + async disable() { + this.isEnabled = false; + this.runtime = {}; + console.log(`[EXTENSION]: disabled ${this.name}@${this.version}`, this.getMeta()); + } + + // todo + async install(downloadUrl?: string) { + return; + } + + // todo + async uninstall() { + return; + } + + async hasNewVersion(): Promise> { + return; + } + + getMeta() { + return toJS({ + id: this.id, + manifest: this.manifest, + manifestPath: this.manifestPath, + enabled: this.isEnabled, + runtime: this.runtime, + }, { + recurseEverything: true + }) + } + + toJSON(): ExtensionModel { + return { + id: this.id, + name: this.name, + version: this.version, + description: this.description, + manifestPath: this.manifestPath, + enabled: this.isEnabled, + updateUrl: this.updateUrl, + } + } +} diff --git a/src/extensions/lens-runtime.ts b/src/extensions/lens-runtime.ts new file mode 100644 index 0000000000..e456bb435f --- /dev/null +++ b/src/extensions/lens-runtime.ts @@ -0,0 +1,19 @@ +// Lens runtime for injecting to extension on activation +import { apiManager } from "../renderer/api/api-manager"; +import logger from "../main/logger"; +import { dynamicPages } from "../renderer/components/cluster-manager/register-page"; + +export interface LensRuntimeRendererEnv { + apiManager: typeof apiManager; + logger: typeof logger; + dynamicPages: typeof dynamicPages +} + +// todo: expose more public runtime apis: stores, managers, etc. +export function getLensRuntime(): LensRuntimeRendererEnv { + return { + apiManager, + logger, + dynamicPages, + } +} diff --git a/src/extensions/tsconfig.json b/src/extensions/tsconfig.json new file mode 100644 index 0000000000..bc688c7ebe --- /dev/null +++ b/src/extensions/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "AMD", + "declaration": true, + "emitDeclarationOnly": true, + "outFile": "./../../static/types/extension-api.d.ts" + }, + "include": [ + "../../types", + "./extension-api.ts" + ] +} diff --git a/src/main/menu.ts b/src/main/menu.ts index 1b0f3434d7..cbe8645e42 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -6,6 +6,7 @@ import { addClusterURL } from "../renderer/components/+add-cluster/add-cluster.r import { preferencesURL } from "../renderer/components/+preferences/preferences.route"; import { whatsNewURL } from "../renderer/components/+whats-new/whats-new.route"; import { clusterSettingsURL } from "../renderer/components/+cluster-settings/cluster-settings.route"; +import { extensionsURL } from "../renderer/components/+extensions/extensions.route"; import logger from "./logger"; export function initMenu(windowManager: WindowManager) { @@ -67,11 +68,18 @@ export function buildMenu(windowManager: WindowManager) { { type: 'separator' }, { label: 'Preferences', - accelerator: 'Cmd+,', + accelerator: 'CmdOrCtrl+,', click() { navigate(preferencesURL()) } }, + { + label: 'Extensions', + accelerator: 'CmdOrCtrl+E', + click() { + navigate(extensionsURL()) + } + }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 0fcb216cd9..e4b24c3035 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -4,6 +4,7 @@ import { render } from "react-dom"; import { isMac } from "../common/vars"; import { userStore } from "../common/user-store"; import { workspaceStore } from "../common/workspace-store"; +import { extensionStore } from "../extensions/extension-store"; import { clusterStore, getHostedClusterId } from "../common/cluster-store"; import { i18nStore } from "./i18n"; import { themeStore } from "./theme.store"; @@ -23,6 +24,7 @@ export async function bootstrap(App: AppComponent) { userStore.load(), workspaceStore.load(), clusterStore.load(), + extensionStore.load(), i18nStore.init(), themeStore.init(), ]); diff --git a/src/renderer/components/+extensions/extensions.route.ts b/src/renderer/components/+extensions/extensions.route.ts new file mode 100644 index 0000000000..5744aabb0e --- /dev/null +++ b/src/renderer/components/+extensions/extensions.route.ts @@ -0,0 +1,11 @@ +import { RouteProps } from "react-router"; +import { buildURL } from "../../navigation"; + +export const extensionsRoute: RouteProps = { + path: "/extensions" +} + +export interface IExtensionsRouteParams { +} + +export const extensionsURL = buildURL(extensionsRoute.path); diff --git a/src/renderer/components/+extensions/extensions.scss b/src/renderer/components/+extensions/extensions.scss new file mode 100644 index 0000000000..496b03c161 --- /dev/null +++ b/src/renderer/components/+extensions/extensions.scss @@ -0,0 +1,4 @@ +.Extensions { + $spacing: $padding * 2; + padding: $spacing; +} \ No newline at end of file diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx new file mode 100644 index 0000000000..18e1b2aeec --- /dev/null +++ b/src/renderer/components/+extensions/extensions.tsx @@ -0,0 +1,31 @@ +import "./extensions.scss" +import React from "react"; +import { observer } from "mobx-react"; +import { extensionStore } from "../../../extensions/extension-store"; +import { WizardLayout } from "../layout/wizard-layout"; +import { Icon } from "../icon"; + +@observer +export class Extensions extends React.Component { + // todo: add input-select to customize extensions loading folder(s) + renderInfoPanel() { + return ( +
+ +

Extensions available to install

+
+ ); + } + + render() { + const { installed: installedExtensions } = extensionStore; + return ( + +

Extensions

+
+          {JSON.stringify(installedExtensions.toJSON(), null, 2)}
+        
+
+ ); + } +} diff --git a/src/renderer/components/+extensions/index.ts b/src/renderer/components/+extensions/index.ts new file mode 100644 index 0000000000..8946a5f6fe --- /dev/null +++ b/src/renderer/components/+extensions/index.ts @@ -0,0 +1,2 @@ +export * from "./extensions.route" +export * from "./extensions" diff --git a/src/renderer/components/cluster-icon/cluster-icon.scss b/src/renderer/components/cluster-icon/cluster-icon.scss index c64e6e07ab..540cecf9eb 100644 --- a/src/renderer/components/cluster-icon/cluster-icon.scss +++ b/src/renderer/components/cluster-icon/cluster-icon.scss @@ -4,7 +4,6 @@ position: relative; border-radius: $radius; padding: $radius; - margin-bottom: $padding * 2; user-select: none; cursor: pointer; diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index 6011549e8c..987f22658d 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -11,6 +11,7 @@ import { Workspaces, workspacesRoute } from "../+workspaces"; import { AddCluster, addClusterRoute } from "../+add-cluster"; import { ClusterView } from "./cluster-view"; import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings"; +import { Extensions, extensionsRoute } from "../+extensions"; import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route"; import { clusterStore } from "../../../common/cluster-store"; import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views"; @@ -60,6 +61,7 @@ export class ClusterManager extends React.Component { + diff --git a/src/renderer/components/cluster-manager/clusters-menu.scss b/src/renderer/components/cluster-manager/clusters-menu.scss index db1a182b38..bc4a8ef75c 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.scss +++ b/src/renderer/components/cluster-manager/clusters-menu.scss @@ -16,10 +16,10 @@ .clusters { @include hidden-scrollbar; padding: 0 $spacing; // extra spacing for cluster-icon's badge - margin-bottom: $spacing; + margin-bottom: $margin; - > :last-child { - margin-bottom: $margin; + .ClusterIcon { + margin-bottom: $margin * 1.5; } &:empty { @@ -67,4 +67,14 @@ pointer-events: none; } } + + > .dynamic-pages { + &:not(:empty) { + padding-top: $spacing; + } + + .Icon { + --size: 40px; + } + } } \ No newline at end of file diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 07780830ff..495ce2176a 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -21,8 +21,7 @@ import { ConfirmDialog } from "../confirm-dialog"; import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route"; import { DragDropContext, Droppable, Draggable, DropResult, DroppableProvided, DraggableProvided } from "react-beautiful-dnd"; - -// fixme: allow to rearrange clusters with drag&drop +import { dynamicPages } from "./register-page"; interface Props { className?: IClassName; @@ -149,6 +148,12 @@ export class ClustersMenu extends React.Component { new} /> )} +
+ {Array.from(dynamicPages.all).map(([path, { MenuIcon }]) => { + if (!MenuIcon) return; + return navigate(path)}/> + })} +
); } diff --git a/src/renderer/components/cluster-manager/register-page.ts b/src/renderer/components/cluster-manager/register-page.ts new file mode 100644 index 0000000000..71a6345cae --- /dev/null +++ b/src/renderer/components/cluster-manager/register-page.ts @@ -0,0 +1,28 @@ +// Dynamic pages + +import React from "react"; +import { observable } from "mobx"; +import type { IconProps } from "../icon"; + +export interface PageComponents { + Main: React.ComponentType; + MenuIcon: React.ComponentType; +} + +export class PagesStore { + all = observable.map(); + + getComponents(path: string): PageComponents | null { + return this.all.get(path); + } + + register(path: string, components: PageComponents) { + this.all.set(path, components); + } + + unregister(path: string) { + this.all.delete(path); + } +} + +export const dynamicPages = new PagesStore(); diff --git a/src/renderer/lens-app.tsx b/src/renderer/lens-app.tsx index 9b4fffc6a1..7c1f228fd3 100644 --- a/src/renderer/lens-app.tsx +++ b/src/renderer/lens-app.tsx @@ -11,9 +11,15 @@ import { ErrorBoundary } from "./components/error-boundary"; import { WhatsNew, whatsNewRoute } from "./components/+whats-new"; import { Notifications } from "./components/notifications"; import { ConfirmDialog } from "./components/confirm-dialog"; +import { extensionStore } from "../extensions/extension-store"; +import { getLensRuntime } from "../extensions/lens-runtime"; @observer export class LensApp extends React.Component { + componentDidMount() { + extensionStore.autoEnableOnLoad(getLensRuntime); + } + render() { return ( diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index 6e53eb990d..d0553e542a 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -2,7 +2,24 @@ Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights! -## 3.6.0-beta.2 (current version) +## 3.6.0-rc.1 (current version) +- Allow user to configure directory where Kubectl binaries are downloaded +- Allow user to configure path to Kubectl binary, instead of using bundled Kubectl +- Log application logs also to log file +- Restrict file permissions to only the user for pasted kubeconfigs +- Close Preferences and Cluster Setting on Esc keypress +- Update Kubectl versions used with Lens +- Update Helm binary version +- Fix: Update CRD api to use preferred version and implement v1 differences +- Fix: Allow to drag and drop cluster icons +- Fix: Wider version select box for Helm chart installation +- Fix: Reload only active dashboard view, not the whole app window +- Fix cluster icon margins +- Fix: Reconnect non-accessible clusters on reconnect +- Fix: Bundle Kubectl and Helm binaries +- Fix: Remove double copyright + +## 3.6.0-beta.2 - Fix: too narrow sidebar without clusters - Fix app crash when iterating Events without 'kind' property defined - Detect non-functional bundled kubectl diff --git a/tsconfig.json b/tsconfig.json index 95f8861c59..5a61e2ca3e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { "baseUrl": ".", - "outDir": "./out", "jsx": "react", "target": "ES2017", "module": "ESNext", diff --git a/types/mocks.d.ts b/types/mocks.d.ts index 7ddd25267b..9c8bf60047 100644 --- a/types/mocks.d.ts +++ b/types/mocks.d.ts @@ -3,6 +3,7 @@ declare module "mac-ca" declare module "win-ca" declare module "@hapi/call" declare module "@hapi/subtext" +declare module "@lens/extensions" // fixme: provide generated types from "extension-api.ts" // Global path to static assets declare const __static: string; diff --git a/webpack.dll.ts b/webpack.dll.ts deleted file mode 100755 index 000ba0af21..0000000000 --- a/webpack.dll.ts +++ /dev/null @@ -1,34 +0,0 @@ -import path from "path"; -import webpack, { LibraryTarget } from "webpack"; -import { isDevelopment, buildDir } from "./src/common/vars"; - -export const library = "dll" -export const libraryTarget: LibraryTarget = "commonjs2" -export const manifestPath = path.resolve(buildDir, `${library}.manifest.json`); - -export const packages = [ - "react", "react-dom", - "ace-builds", "xterm", - "moment", -]; - -export default function (): webpack.Configuration { - return { - context: path.dirname(manifestPath), - mode: isDevelopment ? "development" : "production", - cache: isDevelopment, - entry: { - [library]: packages, - }, - output: { - library, - libraryTarget, - }, - plugins: [ - new webpack.DllPlugin({ - name: library, - path: manifestPath, - }) - ], - } -} diff --git a/webpack.renderer.ts b/webpack.renderer.ts index b6c35535ce..1d60e2afc4 100755 --- a/webpack.renderer.ts +++ b/webpack.renderer.ts @@ -1,4 +1,4 @@ -import { appName, htmlTemplate, isDevelopment, isProduction, buildDir, rendererDir, sassCommonVars, publicPath } from "./src/common/vars"; +import { appName, buildDir, extensionsDir, extensionsLibName, htmlTemplate, isDevelopment, isProduction, publicPath, rendererDir, sassCommonVars } from "./src/common/vars"; import path from "path"; import webpack from "webpack"; import HtmlWebpackPlugin from "html-webpack-plugin"; @@ -6,12 +6,32 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin"; import TerserPlugin from "terser-webpack-plugin"; import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin" -export default function (): webpack.Configuration { - console.info('WEBPACK:renderer', require("./src/common/vars")) +export default [ + webpackLensRenderer, + webpackExtensionsApi, +] + +// todo: use common chunks/externals for "react", "react-dom", etc. +export function webpackExtensionsApi(): webpack.Configuration { + const config = webpackLensRenderer({ showVars: false }); + config.name = "extensions-api" + config.entry = { + [extensionsLibName]: path.resolve(extensionsDir, "extension-api.ts") + }; + config.output.libraryTarget = "commonjs2" + delete config.devtool; + return config; +} + +export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration { + if (showVars) { + console.info('WEBPACK:renderer', require("./src/common/vars")); + } return { context: __dirname, target: "electron-renderer", devtool: "source-map", // todo: optimize in dev-mode with webpack.SourceMapDevToolPlugin + name: "lens-app", mode: isProduction ? "production" : "development", cache: isDevelopment, entry: { @@ -23,6 +43,11 @@ export default function (): webpack.Configuration { filename: '[name].js', chunkFilename: 'chunks/[name].js', }, + stats: { + warningsFilter: [ + /Critical dependency: the request of a dependency is an expression/ + ] + }, resolve: { extensions: [ '.js', '.jsx', '.json', diff --git a/yarn.lock b/yarn.lock index 44fc03bfd8..8e84da93d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1966,6 +1966,11 @@ dependencies: "@types/node" "*" +"@types/module-alias@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.0.tgz#882668f8b8cdbda44812c3b592c590909e18849e" + integrity sha512-e3sW4oEH0qS1QxSfX7PT6xIi5qk/YSMsrB9Lq8EtkhQBZB+bKyfkP+jpLJRySanvBhAQPSv2PEBe81M8Iy/7yg== + "@types/node@*": version "14.0.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.11.tgz#61d4886e2424da73b7b25547f59fdcb534c165a3" @@ -4086,13 +4091,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-env@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" - integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== - dependencies: - cross-spawn "^7.0.1" - cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -4112,7 +4110,7 @@ cross-spawn@^3.0.0: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -8181,6 +8179,11 @@ mock-fs@^4.12.0: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.12.0.tgz#a5d50b12d2d75e5bec9dac3b67ffe3c41d31ade4" integrity sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ== +module-alias@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" + integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== + moment@^2.10.2, moment@^2.26.0: version "2.26.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" From 5daf53e6cbeeea97608066f4fc3077fc58132db3 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 9 Sep 2020 16:19:02 +0300 Subject: [PATCH 002/102] Extensions-api: initial hello-world example (#817) Signed-off-by: Roman Co-authored-by: Alex Andreev --- .eslintrc.js | 3 +- .gitignore | 1 + .../example-extension/example-extension.ts | 17 ------- .../example-extension/example-extension.tsx | 47 +++++++++++++++++++ src/extensions/example-extension/package.json | 5 +- .../example-extension/tsconfig.json | 2 +- src/extensions/extension-api.ts | 3 +- src/extensions/extension-store.ts | 16 +++---- .../{extension.ts => lens-extension.ts} | 34 ++++++++++++-- src/extensions/lens-runtime.ts | 31 +++++++----- src/extensions/register-page.tsx | 46 ++++++++++++++++++ src/renderer/bootstrap.tsx | 2 + src/renderer/components/app.tsx | 4 ++ .../cluster-manager/cluster-manager.tsx | 4 ++ .../cluster-manager/clusters-menu.tsx | 15 +++--- .../cluster-manager/register-page.ts | 28 ----------- .../components/layout/main-layout.tsx | 4 +- src/renderer/components/layout/sidebar.tsx | 13 +++++ src/renderer/lens-app.tsx | 6 --- 19 files changed, 190 insertions(+), 91 deletions(-) delete mode 100644 src/extensions/example-extension/example-extension.ts create mode 100644 src/extensions/example-extension/example-extension.tsx rename src/extensions/{extension.ts => lens-extension.ts} (74%) create mode 100644 src/extensions/register-page.tsx delete mode 100644 src/renderer/components/cluster-manager/register-page.ts diff --git a/.eslintrc.js b/.eslintrc.js index 516029cdd4..3de849de34 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,7 +24,8 @@ module.exports = { files: [ "build/*.ts", "src/**/*.ts", - "integration/**/*.ts" + "integration/**/*.ts", + "src/extensions/**/*.ts*" ], parser: "@typescript-eslint/parser", extends: [ diff --git a/.gitignore b/.gitignore index 6ddf3f489c..11cc6298ab 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ binaries/client/ binaries/server/ src/extensions/*/*.js src/extensions/*/*.d.ts +src/extensions/example-extension/src/** locales/**/**.js lens.log diff --git a/src/extensions/example-extension/example-extension.ts b/src/extensions/example-extension/example-extension.ts deleted file mode 100644 index 28b4da5825..0000000000 --- a/src/extensions/example-extension/example-extension.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { LensExtension, Icon, LensRuntimeRendererEnv } from "@lens/extensions"; // fixme: map to generated types from "extension-api.d.ts" - -// todo: register custom icon in cluster-menu -// todo: register custom view by clicking the item - -export default class ExampleExtension extends LensExtension { - async enable(runtime: /*LensRuntimeRendererEnv*/ any): Promise { - try { - super.enable(runtime); - runtime.logger.info('EXAMPLE EXTENSION: ENABLE() override'); - } catch (err){ - console.error(err) - } - } -} - -// console.log("done")}/> \ No newline at end of file diff --git a/src/extensions/example-extension/example-extension.tsx b/src/extensions/example-extension/example-extension.tsx new file mode 100644 index 0000000000..52a169a2c9 --- /dev/null +++ b/src/extensions/example-extension/example-extension.tsx @@ -0,0 +1,47 @@ +import { Button, DynamicPageType, Icon, LensExtension } from "@lens/extensions"; // fixme: map to generated types from "extension-api.ts" +import React from "react"; +import path from "path"; + +export default class ExampleExtension extends LensExtension { + onActivate() { + console.log('EXAMPLE EXTENSION: ACTIVATED', this.getMeta()); + this.registerPage({ + type: DynamicPageType.CLUSTER, + path: "/extension-example", + menuTitle: "Example Extension", + components: { + Page: () => , + MenuIcon: ExtensionIcon, + } + }) + } + + onDeactivate() { + console.log('EXAMPLE EXTENSION: DEACTIVATED', this.getMeta()); + } +} + +export function ExtensionIcon(props: {} /*IconProps |*/) { + return +} + +export class ExtensionPage extends React.Component<{ extension: ExampleExtension }> { + deactivate = () => { + const { extension } = this.props; + extension.runtime.navigate("/") + extension.disable(); + } + + render() { + const { MainLayout } = this.props.extension.runtime.components; + return ( + +
+

Hello from extensions-api!

+

File: {__filename}

+
+
+ ) + } +} diff --git a/src/extensions/example-extension/package.json b/src/extensions/example-extension/package.json index d57a78aab5..663d5d432a 100644 --- a/src/extensions/example-extension/package.json +++ b/src/extensions/example-extension/package.json @@ -2,9 +2,10 @@ "name": "extension-example", "version": "1.0.0", "description": "Example extension", - "main": "example-extension.ts", + "main": "example-extension.js", "lens": { - "metadata": {} + "metadata": {}, + "styles": [] }, "dependencies": { } diff --git a/src/extensions/example-extension/tsconfig.json b/src/extensions/example-extension/tsconfig.json index 16081f6055..993992112a 100644 --- a/src/extensions/example-extension/tsconfig.json +++ b/src/extensions/example-extension/tsconfig.json @@ -8,6 +8,6 @@ }, "include": [ "../../../types", - "./example-extension.ts" + "./example-extension.tsx" ] } diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts index 3dff72d00c..8e5951a12e 100644 --- a/src/extensions/extension-api.ts +++ b/src/extensions/extension-api.ts @@ -2,7 +2,8 @@ export type { LensRuntimeRendererEnv } from "./lens-runtime"; // APIs -export * from "./extension" +export * from "./lens-extension" +export { DynamicPageType } from "./register-page"; // Common UI components export * from "../renderer/components/icon" diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index 655c4d6ca7..a7eb75db88 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -3,17 +3,17 @@ import path from "path"; import fs from "fs-extra"; import { action, observable, reaction, toJS, } from "mobx"; import { BaseStore } from "../common/base-store"; -import { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./extension"; -import { isDevelopment, isProduction, isTestEnv } from "../common/vars"; +import { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./lens-extension"; +import { isDevelopment } from "../common/vars"; import logger from "../main/logger"; export interface ExtensionStoreModel { version: ExtensionVersion; - extensions: Record + extensions: [ExtensionId, ExtensionModel][] } export interface ExtensionModel { - id?: ExtensionId; // available in lens-extension instance + id: ExtensionId; version: ExtensionVersion; name: string; manifestPath: string; @@ -35,7 +35,6 @@ export class ExtensionStore extends BaseStore { private constructor() { super({ configName: "lens-extension-store", - syncEnabled: false, }); } @@ -48,7 +47,7 @@ export class ExtensionStore extends BaseStore { if (isDevelopment) { return path.resolve(__static, "../src/extensions"); } - return path.resolve(__static, "../extensions"); //todo figure out prod + return path.resolve(__static, "../extensions"); } async load() { @@ -80,7 +79,6 @@ export class ExtensionStore extends BaseStore { try { manifestJson = __non_webpack_require__(manifestPath); // "__non_webpack_require__" converts to native node's require()-call mainJs = path.resolve(path.dirname(manifestPath), manifestJson.main); - mainJs = mainJs.replace(/\.ts$/i, ".js"); // todo: compile *.ts on the fly? const extensionModule = __non_webpack_require__(mainJs); return { manifestPath: manifestPath, @@ -132,7 +130,7 @@ export class ExtensionStore extends BaseStore { this.version = version; } if (extensions) { - const currentExtensions = new Map(Object.entries(extensions)); + const currentExtensions = new Map(extensions); this.extensions.forEach(extension => { if (!currentExtensions.has(extension.id)) { this.removed.set(extension.id, extension); @@ -164,7 +162,7 @@ export class ExtensionStore extends BaseStore { toJSON(): ExtensionStoreModel { return toJS({ version: this.version, - extensions: this.extensions.toJSON(), + extensions: Array.from(this.extensions).map(([id, instance]) => [id, instance.toJSON()]), }, { recurseEverything: true, }) diff --git a/src/extensions/extension.ts b/src/extensions/lens-extension.ts similarity index 74% rename from src/extensions/extension.ts rename to src/extensions/lens-extension.ts index 698422fb54..a285cf095e 100644 --- a/src/extensions/extension.ts +++ b/src/extensions/lens-extension.ts @@ -1,17 +1,20 @@ import type { ExtensionModel } from "./extension-store"; import type { LensRuntimeRendererEnv } from "./lens-runtime"; +import type { PageRegistration } from "./register-page"; import { readJsonSync } from "fs-extra"; import { action, observable, toJS } from "mobx"; import extensionManifest from "./example-extension/package.json" import logger from "../main/logger"; -export type ExtensionId = string; // instance-id or abs path to "%lens-extension/manifest.json" +export type ExtensionId = string | ExtensionPackageJsonPath; +export type ExtensionPackageJsonPath = string; export type ExtensionVersion = string | number; export type ExtensionManifest = typeof extensionManifest & ExtensionModel; export class LensExtension implements ExtensionModel { public id: ExtensionId; public updateUrl: string; + protected disposers: Function[] = []; @observable name = ""; @observable description = ""; @@ -19,7 +22,7 @@ export class LensExtension implements ExtensionModel { @observable manifest: ExtensionManifest; @observable manifestPath: string; @observable isEnabled = false; - @observable.ref runtime: Partial = {}; + @observable.ref runtime: LensRuntimeRendererEnv; constructor(model: ExtensionModel, manifest: ExtensionManifest) { this.importModel(model, manifest); @@ -41,14 +44,27 @@ export class LensExtension implements ExtensionModel { this.isEnabled = true; this.runtime = runtime; console.log(`[EXTENSION]: enabled ${this.name}@${this.version}`, this.getMeta()); + this.onActivate(); } async disable() { + this.onDeactivate(); this.isEnabled = false; - this.runtime = {}; + this.runtime = null; + this.disposers.forEach(cleanUp => cleanUp()); + this.disposers.length = 0; console.log(`[EXTENSION]: disabled ${this.name}@${this.version}`, this.getMeta()); } + // todo: add more hooks + protected onActivate() { + // mock + } + + protected onDeactivate() { + // mock + } + // todo async install(downloadUrl?: string) { return; @@ -76,7 +92,7 @@ export class LensExtension implements ExtensionModel { } toJSON(): ExtensionModel { - return { + return toJS({ id: this.id, name: this.name, version: this.version, @@ -84,6 +100,16 @@ export class LensExtension implements ExtensionModel { manifestPath: this.manifestPath, enabled: this.isEnabled, updateUrl: this.updateUrl, + }, { + recurseEverything: true, + }) + } + + // Runtime helpers + protected registerPage(params: PageRegistration, autoDisable = true) { + const dispose = this.runtime.dynamicPages.register(params); + if (autoDisable) { + this.disposers.push(dispose); } } } diff --git a/src/extensions/lens-runtime.ts b/src/extensions/lens-runtime.ts index e456bb435f..5fdc4c35eb 100644 --- a/src/extensions/lens-runtime.ts +++ b/src/extensions/lens-runtime.ts @@ -1,19 +1,26 @@ -// Lens runtime for injecting to extension on activation -import { apiManager } from "../renderer/api/api-manager"; +// Lens renderer runtime params available to extensions after activation + import logger from "../main/logger"; -import { dynamicPages } from "../renderer/components/cluster-manager/register-page"; +import { dynamicPages } from "./register-page"; +import { MainLayout } from "../renderer/components/layout/main-layout"; +import { navigate } from "../renderer/navigation"; export interface LensRuntimeRendererEnv { - apiManager: typeof apiManager; + navigate: typeof navigate; logger: typeof logger; dynamicPages: typeof dynamicPages -} - -// todo: expose more public runtime apis: stores, managers, etc. -export function getLensRuntime(): LensRuntimeRendererEnv { - return { - apiManager, - logger, - dynamicPages, + components: { + MainLayout: typeof MainLayout + } +} + +export function getLensRuntime(): LensRuntimeRendererEnv { + return { + logger, + navigate, + dynamicPages, + components: { + MainLayout // fixme: refactor, import as pure component from "@lens/extensions" + } } } diff --git a/src/extensions/register-page.tsx b/src/extensions/register-page.tsx new file mode 100644 index 0000000000..34317c1d93 --- /dev/null +++ b/src/extensions/register-page.tsx @@ -0,0 +1,46 @@ +// Extensions-api -> Dynamic pages + +import { computed, observable } from "mobx"; +import React from "react"; +import type { IconProps } from "../renderer/components/icon"; + +export enum DynamicPageType { + GLOBAL = "lens-scope", + CLUSTER = "cluster-view-scope", +} + +export interface PageRegistration { + path: string; // route-path + menuTitle: string; + type: DynamicPageType; + components: PageComponents; +} + +export interface PageComponents { + Page: React.ComponentType; + MenuIcon: React.ComponentType; +} + +export class PagesStore { + protected pages = observable.array([], { deep: false }); + + @computed get globalPages() { + return this.pages.filter(page => page.type === DynamicPageType.GLOBAL); + } + + @computed get clusterPages() { + return this.pages.filter(page => page.type === DynamicPageType.CLUSTER); + } + + // todo: verify paths to avoid collision with existing pages + register(params: PageRegistration) { + this.pages.push(params); + return () => { + this.pages.replace( + this.pages.filter(page => page.components !== params.components) + ) + }; + } +} + +export const dynamicPages = new PagesStore(); diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index e4b24c3035..ee4dd6dedf 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -10,6 +10,7 @@ import { i18nStore } from "./i18n"; import { themeStore } from "./theme.store"; import { App } from "./components/app"; import { LensApp } from "./lens-app"; +import { getLensRuntime } from "../extensions/lens-runtime"; type AppComponent = React.ComponentType & { init?(): void; @@ -32,6 +33,7 @@ export async function bootstrap(App: AppComponent) { // init app's dependencies if any if (App.init) { await App.init(); + extensionStore.autoEnableOnLoad(getLensRuntime); } render(, rootElem); } diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 23e71e2be9..a878fd47b7 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -35,6 +35,7 @@ import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store import logger from "../../main/logger"; import { clusterIpc } from "../../common/cluster-ipc"; import { webFrame } from "electron"; +import { dynamicPages } from "../../extensions/register-page"; @observer export class App extends React.Component { @@ -71,6 +72,9 @@ export class App extends React.Component { + {dynamicPages.clusterPages.map(({ path, components: { Page } }) => { + return + })} diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index 987f22658d..10ee59f467 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -15,6 +15,7 @@ import { Extensions, extensionsRoute } from "../+extensions"; import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route"; import { clusterStore } from "../../../common/cluster-store"; import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views"; +import { dynamicPages } from "../../../extensions/register-page"; @observer export class ClusterManager extends React.Component { @@ -62,6 +63,9 @@ export class ClusterManager extends React.Component { + {dynamicPages.globalPages.map(({ path, components: { Page } }) => { + return + })} diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 495ce2176a..30edfc22c7 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -10,7 +10,7 @@ import { ClusterId, clusterStore } from "../../../common/cluster-store"; import { workspaceStore } from "../../../common/workspace-store"; import { ClusterIcon } from "../cluster-icon"; import { Icon } from "../icon"; -import { cssNames, IClassName, autobind } from "../../utils"; +import { autobind, cssNames, IClassName } from "../../utils"; import { Badge } from "../badge"; import { navigate } from "../../navigation"; import { addClusterURL } from "../+add-cluster"; @@ -20,8 +20,8 @@ import { Tooltip } from "../tooltip"; import { ConfirmDialog } from "../confirm-dialog"; import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route"; -import { DragDropContext, Droppable, Draggable, DropResult, DroppableProvided, DraggableProvided } from "react-beautiful-dnd"; -import { dynamicPages } from "./register-page"; +import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd"; +import { dynamicPages } from "../../../extensions/register-page"; interface Props { className?: IClassName; @@ -143,15 +143,14 @@ export class ClustersMenu extends React.Component { Add Cluster - + {newContexts.size > 0 && ( - new} /> + new}/> )}
- {Array.from(dynamicPages.all).map(([path, { MenuIcon }]) => { - if (!MenuIcon) return; - return navigate(path)}/> + {dynamicPages.globalPages.map(({ path, components: { MenuIcon } }) => { + return navigate(path)}/> })}
diff --git a/src/renderer/components/cluster-manager/register-page.ts b/src/renderer/components/cluster-manager/register-page.ts deleted file mode 100644 index 71a6345cae..0000000000 --- a/src/renderer/components/cluster-manager/register-page.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Dynamic pages - -import React from "react"; -import { observable } from "mobx"; -import type { IconProps } from "../icon"; - -export interface PageComponents { - Main: React.ComponentType; - MenuIcon: React.ComponentType; -} - -export class PagesStore { - all = observable.map(); - - getComponents(path: string): PageComponents | null { - return this.all.get(path); - } - - register(path: string, components: PageComponents) { - this.all.set(path, components); - } - - unregister(path: string) { - this.all.delete(path); - } -} - -export const dynamicPages = new PagesStore(); diff --git a/src/renderer/components/layout/main-layout.tsx b/src/renderer/components/layout/main-layout.tsx index def55961fa..8672e4ba23 100755 --- a/src/renderer/components/layout/main-layout.tsx +++ b/src/renderer/components/layout/main-layout.tsx @@ -17,7 +17,7 @@ export interface TabRoute extends RouteProps { url: string; } -interface Props { +export interface MainLayoutProps { className?: any; tabs?: TabRoute[]; footer?: React.ReactNode; @@ -27,7 +27,7 @@ interface Props { } @observer -export class MainLayout extends React.Component { +export class MainLayout extends React.Component { public storage = createStorage("main_layout", { pinnedSidebar: true }); @observable isPinned = this.storage.get().pinnedSidebar; diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index d46a1548cf..91cd4940a6 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -28,6 +28,7 @@ import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resourc import { CustomResources } from "../+custom-resources/custom-resources"; import { navigation } from "../../navigation"; import { isAllowedResource } from "../../../common/rbac" +import { dynamicPages } from "../../../extensions/register-page"; const SidebarContext = React.createContext({ pinned: false }); type SidebarContextValue = { @@ -183,6 +184,18 @@ export class Sidebar extends React.Component { > {this.renderCustomResources()} + {dynamicPages.clusterPages.map(({ path, menuTitle, components: { MenuIcon } }) => { + return ( + } + /> + ) + })} diff --git a/src/renderer/lens-app.tsx b/src/renderer/lens-app.tsx index 7c1f228fd3..9b4fffc6a1 100644 --- a/src/renderer/lens-app.tsx +++ b/src/renderer/lens-app.tsx @@ -11,15 +11,9 @@ import { ErrorBoundary } from "./components/error-boundary"; import { WhatsNew, whatsNewRoute } from "./components/+whats-new"; import { Notifications } from "./components/notifications"; import { ConfirmDialog } from "./components/confirm-dialog"; -import { extensionStore } from "../extensions/extension-store"; -import { getLensRuntime } from "../extensions/lens-runtime"; @observer export class LensApp extends React.Component { - componentDidMount() { - extensionStore.autoEnableOnLoad(getLensRuntime); - } - render() { return ( From 9a402eab3debd8be1a3c9a7ef6ebbd6f72632c93 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Sep 2020 12:10:02 +0300 Subject: [PATCH 003/102] fix: example-extension broken after merging master fix: removed devtools warnings from extension-api bundle Signed-off-by: Roman --- src/extensions/example-extension/example-extension.tsx | 6 +++--- src/extensions/lens-runtime.ts | 6 +++--- webpack.renderer.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/extensions/example-extension/example-extension.tsx b/src/extensions/example-extension/example-extension.tsx index 52a169a2c9..f44c6936a4 100644 --- a/src/extensions/example-extension/example-extension.tsx +++ b/src/extensions/example-extension/example-extension.tsx @@ -33,15 +33,15 @@ export class ExtensionPage extends React.Component<{ extension: ExampleExtension } render() { - const { MainLayout } = this.props.extension.runtime.components; + const { TabLayout } = this.props.extension.runtime.components; return ( - +

Hello from extensions-api!

File: {__filename}

-
+ ) } } diff --git a/src/extensions/lens-runtime.ts b/src/extensions/lens-runtime.ts index 5fdc4c35eb..1518dbc9de 100644 --- a/src/extensions/lens-runtime.ts +++ b/src/extensions/lens-runtime.ts @@ -2,7 +2,7 @@ import logger from "../main/logger"; import { dynamicPages } from "./register-page"; -import { MainLayout } from "../renderer/components/layout/main-layout"; +import { TabLayout } from "../renderer/components/layout/tab-layout"; import { navigate } from "../renderer/navigation"; export interface LensRuntimeRendererEnv { @@ -10,7 +10,7 @@ export interface LensRuntimeRendererEnv { logger: typeof logger; dynamicPages: typeof dynamicPages components: { - MainLayout: typeof MainLayout + TabLayout: typeof TabLayout } } @@ -20,7 +20,7 @@ export function getLensRuntime(): LensRuntimeRendererEnv { navigate, dynamicPages, components: { - MainLayout // fixme: refactor, import as pure component from "@lens/extensions" + TabLayout // fixme: refactor, import as pure component from "@lens/extensions" } } } diff --git a/webpack.renderer.ts b/webpack.renderer.ts index ca8e07438a..37ed000384 100755 --- a/webpack.renderer.ts +++ b/webpack.renderer.ts @@ -20,7 +20,7 @@ export function webpackExtensionsApi(): webpack.Configuration { [extensionsLibName]: path.resolve(extensionsDir, "extension-api.ts") }; config.output.libraryTarget = "commonjs2" - delete config.devtool; + config.devtool = "nosources-source-map"; return config; } From bc228c9e365867a16590bc19091ded91ca3d2b81 Mon Sep 17 00:00:00 2001 From: Yangjun Wang Date: Mon, 28 Sep 2020 17:24:54 +0300 Subject: [PATCH 004/102] display last-applied-configuration annotation in detail-view, but filter it in search fields still (#943) Signed-off-by: Yangjun Wang Co-authored-by: Yangjun Wang --- src/renderer/api/kube-object.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts index abb92dfe58..5e712df9f2 100644 --- a/src/renderer/api/kube-object.ts +++ b/src/renderer/api/kube-object.ts @@ -115,12 +115,12 @@ export class KubeObject implements ItemObject { return KubeObject.stringifyLabels(this.metadata.labels); } - getAnnotations(): string[] { + getAnnotations(filter = false): string[] { const labels = KubeObject.stringifyLabels(this.metadata.annotations); - return labels.filter(label => { + return filter ? labels.filter(label => { const skip = resourceApplierApi.annotations.some(key => label.startsWith(key)); return !skip; - }) + }) : labels; } getOwnerRefs() { @@ -138,7 +138,7 @@ export class KubeObject implements ItemObject { getNs(), getId(), ...getLabels(), - ...getAnnotations(), + ...getAnnotations(true), ] } From 34e141e517c474f074c93a300cb43e8ba392dd1f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 28 Sep 2020 10:30:23 -0400 Subject: [PATCH 005/102] make namespace filter multi select and change onChange (#987) Signed-off-by: Sebastian Malton --- src/renderer/components/+namespaces/namespace-select.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/+namespaces/namespace-select.tsx b/src/renderer/components/+namespaces/namespace-select.tsx index 4d4dcc22da..7657a25345 100644 --- a/src/renderer/components/+namespaces/namespace-select.tsx +++ b/src/renderer/components/+namespaces/namespace-select.tsx @@ -58,7 +58,7 @@ export class NamespaceSelect extends React.Component { const { value, label } = option; return label || ( <> - {showIcons && } + {showIcons && } {value} ); @@ -91,14 +91,15 @@ export class NamespaceSelectFilter extends React.Component { closeMenuOnSelect={false} isOptionSelected={() => false} controlShouldRenderValue={false} - onChange={({ value: namespace }: SelectOption) => toggleContext(namespace)} + isMulti + onChange={([{ value }]: SelectOption[]) => toggleContext(value)} formatOptionLabel={({ value: namespace }: SelectOption) => { const isSelected = hasContext(namespace); return (
- + {namespace} - {isSelected && } + {isSelected && }
) }} From 5ec6751ab41b11a21f67d486d98fc9b69516d5ea Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 29 Sep 2020 07:37:25 +0300 Subject: [PATCH 006/102] Extensions api types (#985) Signed-off-by: Roman --- .gitignore | 5 +- package.json | 15 +- .../example-extension/example-extension.tsx | 4 +- src/extensions/extension-api.ts | 14 +- src/extensions/extension-store.ts | 2 +- src/extensions/lens-extension.ts | 7 +- src/extensions/rollup.config.js | 5 + src/extensions/rollup.config.ts | 59 +++++ src/extensions/tsconfig.json | 13 - types/mocks.d.ts | 1 - yarn.lock | 248 ++++++------------ 11 files changed, 174 insertions(+), 199 deletions(-) create mode 100644 src/extensions/rollup.config.js create mode 100644 src/extensions/rollup.config.ts delete mode 100644 src/extensions/tsconfig.json diff --git a/.gitignore b/.gitignore index 11cc6298ab..68c773c895 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ node_modules/ yarn-error.log coverage/ tmp/ +locales/**/**.js +lens.log static/build static/types binaries/client/ @@ -11,5 +13,4 @@ binaries/server/ src/extensions/*/*.js src/extensions/*/*.d.ts src/extensions/example-extension/src/** -locales/**/**.js -lens.log +types/extension-api.d.ts diff --git a/package.json b/package.json index 934095b84e..7d52e4588d 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,11 @@ "dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"", "dev:main": "yarn compile:main --watch", "dev:renderer": "yarn compile:renderer --watch", - "dev:extensions": "tsc --project src/extensions/example-extension --watch", + "dev:extensions": "rollup --config src/extensions/rollup.config.js --watch", "compile": "env NODE_ENV=production concurrently yarn:compile:*", "compile:main": "webpack --config webpack.main.ts", "compile:renderer": "webpack --config webpack.renderer.ts", "compile:i18n": "lingui compile", - "compile:extension-api.d.ts": "tsc --project src/extensions", "build:linux": "yarn compile && electron-builder --linux --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", @@ -35,7 +34,7 @@ "download:kubectl": "yarn run ts-node build/download_kubectl.ts", "download:helm": "yarn run ts-node build/download_helm.ts", "lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/", - "rebuild-pty": "yarn run electron-rebuild -f -w node-pty" + "extensions:example": "tsc --project src/extensions/example-extension/tsconfig.json --watch" }, "config": { "bundledKubectlVersion": "1.17.11", @@ -72,7 +71,9 @@ "moduleNameMapper": { "\\.(css|scss)$": "/__mocks__/styleMock.ts" }, - "modulePathIgnorePatterns": ["/dist"] + "modulePathIgnorePatterns": [ + "/dist" + ] }, "build": { "afterSign": "build/notarize.js", @@ -242,6 +243,7 @@ "@lingui/macro": "^3.0.0-13", "@lingui/react": "^3.0.0-13", "@material-ui/core": "^4.10.1", + "@rollup/plugin-json": "^4.1.0", "@types/chart.js": "^2.9.21", "@types/circular-dependency-plugin": "^5.0.1", "@types/color": "^3.0.1", @@ -290,7 +292,6 @@ "electron": "^9.1.2", "electron-builder": "^22.7.0", "electron-notarize": "^0.3.0", - "electron-rebuild": "^1.11.0", "eslint": "^7.7.0", "file-loader": "^6.0.0", "flex.box": "^3.4.4", @@ -317,6 +318,10 @@ "react-router-dom": "^5.2.0", "react-select": "^3.1.0", "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", "spectron": "11.0.0", "style-loader": "^1.2.1", diff --git a/src/extensions/example-extension/example-extension.tsx b/src/extensions/example-extension/example-extension.tsx index f44c6936a4..1967ce0d60 100644 --- a/src/extensions/example-extension/example-extension.tsx +++ b/src/extensions/example-extension/example-extension.tsx @@ -1,4 +1,4 @@ -import { Button, DynamicPageType, Icon, LensExtension } from "@lens/extensions"; // fixme: map to generated types from "extension-api.ts" +import { Button, DynamicPageType, Icon, IconProps, LensExtension } from "@lens/extensions"; import React from "react"; import path from "path"; @@ -21,7 +21,7 @@ export default class ExampleExtension extends LensExtension { } } -export function ExtensionIcon(props: {} /*IconProps |*/) { +export function ExtensionIcon(props: IconProps) { return } diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts index 8e5951a12e..5b9fce7d02 100644 --- a/src/extensions/extension-api.ts +++ b/src/extensions/extension-api.ts @@ -5,19 +5,9 @@ export type { LensRuntimeRendererEnv } from "./lens-runtime"; export * from "./lens-extension" export { DynamicPageType } from "./register-page"; -// Common UI components +// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps) export * from "../renderer/components/icon" -export * from "../renderer/components/badge" export * from "../renderer/components/tooltip" export * from "../renderer/components/button" -export * from "../renderer/components/input" -export * from "../renderer/components/select" -export * from "../renderer/components/checkbox" -export * from "../renderer/components/radio" -export * from "../renderer/components/slider" -export * from "../renderer/components/spinner" export * from "../renderer/components/tabs" -export * from "../renderer/components/line-progress" - -// Utils -export * from "../main/logger"; +export * from "../renderer/components/badge" diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index a7eb75db88..13be1eb927 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -1,9 +1,9 @@ +import type { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./lens-extension"; import type { LensRuntimeRendererEnv } from "./lens-runtime"; import path from "path"; import fs from "fs-extra"; import { action, observable, reaction, toJS, } from "mobx"; import { BaseStore } from "../common/base-store"; -import { ExtensionId, ExtensionManifest, ExtensionVersion, LensExtension } from "./lens-extension"; import { isDevelopment } from "../common/vars"; import logger from "../main/logger"; diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index a285cf095e..9f33858f00 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -3,13 +3,16 @@ import type { LensRuntimeRendererEnv } from "./lens-runtime"; import type { PageRegistration } from "./register-page"; import { readJsonSync } from "fs-extra"; import { action, observable, toJS } from "mobx"; -import extensionManifest from "./example-extension/package.json" import logger from "../main/logger"; export type ExtensionId = string | ExtensionPackageJsonPath; export type ExtensionPackageJsonPath = string; export type ExtensionVersion = string | number; -export type ExtensionManifest = typeof extensionManifest & ExtensionModel; + +export interface ExtensionManifest extends ExtensionModel { + main: string; + description?: string; // todo: add more fields similar to package.json + some extra +} export class LensExtension implements ExtensionModel { public id: ExtensionId; diff --git a/src/extensions/rollup.config.js b/src/extensions/rollup.config.js new file mode 100644 index 0000000000..beb74ba319 --- /dev/null +++ b/src/extensions/rollup.config.js @@ -0,0 +1,5 @@ +// Workaround for using Typescript in Rollup configutation +// https://stackoverflow.com/questions/54711437/does-rollup-support-typescript-in-rollup-config-file + +require('ts-node').register(); +module.exports = require('./rollup.config.ts'); diff --git a/src/extensions/rollup.config.ts b/src/extensions/rollup.config.ts new file mode 100644 index 0000000000..8d802ef495 --- /dev/null +++ b/src/extensions/rollup.config.ts @@ -0,0 +1,59 @@ +// Generating declaration types for extensions-api +// Rollup: https://rollupjs.org/guide/en/ +// Plugin docs: https://github.com/Swatinem/rollup-plugin-dts + +import { OutputChunk, Plugin, RollupOptions } from 'rollup'; +import json from '@rollup/plugin-json'; +import dts from "rollup-plugin-dts"; +import ignoreImport from 'rollup-plugin-ignore-import' + +// todo: generate extension-api.js bundle also with Rollup (?) + +const config: RollupOptions = { + input: "src/extensions/extension-api.ts", + output: [ + { file: "types/extension-api.d.ts", format: "es", } + ], + plugins: [ + dts(), + dtsModuleWrap({ name: "@lens/extensions" }), + ignoreImport({ extensions: ['.scss'] }), + json(), + ], +}; + +function dtsModuleWrap({ name }: { name: string }): Plugin { + return { + name, + generateBundle: (options, bundle) => { + const apiTypes = Object.values(bundle)[0] as OutputChunk; // extension-api.d.ts + const typeRefs: string[] = [] + const declarations: string[] = [] + const apiLines = apiTypes.code.split("\n") + let outputCode = "" + + apiLines.forEach(line => { + if (line.startsWith("///")) { + typeRefs.push(line) + } else { + declarations.push(line) + } + }) + + // print external @types refs first + if (typeRefs.length) { + outputCode += typeRefs.join("\n") + "\n\n" + } + + // wrap declarations into global module definition + outputCode += `declare module "${name}" {\n` + outputCode += declarations.map(line => `\t${line}`).join("\n") + outputCode += `\n}` + + // save + apiTypes.code = outputCode; + } + } +} + +export default config; \ No newline at end of file diff --git a/src/extensions/tsconfig.json b/src/extensions/tsconfig.json deleted file mode 100644 index bc688c7ebe..0000000000 --- a/src/extensions/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "module": "AMD", - "declaration": true, - "emitDeclarationOnly": true, - "outFile": "./../../static/types/extension-api.d.ts" - }, - "include": [ - "../../types", - "./extension-api.ts" - ] -} diff --git a/types/mocks.d.ts b/types/mocks.d.ts index 9c8bf60047..7ddd25267b 100644 --- a/types/mocks.d.ts +++ b/types/mocks.d.ts @@ -3,7 +3,6 @@ declare module "mac-ca" declare module "win-ca" declare module "@hapi/call" declare module "@hapi/subtext" -declare module "@lens/extensions" // fixme: provide generated types from "extension-api.ts" // Global path to static assets declare const __static: string; diff --git a/yarn.lock b/yarn.lock index 0b8f52d5cd..924e3adda1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,6 +14,13 @@ dependencies: "@babel/highlight" "^7.10.1" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/compat-data@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" @@ -246,6 +253,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + "@babel/helper-wrap-function@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" @@ -274,6 +286,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.10.2", "@babel/parser@^7.9.6": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" @@ -1589,6 +1610,22 @@ resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/pluginutils@^3.0.8": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1742,6 +1779,11 @@ dependencies: electron "*" +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/fs-extra@^9.0.1": version "9.0.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918" @@ -3529,7 +3571,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3731,7 +3773,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.0.0, cli-spinners@^2.2.0: +cli-spinners@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5" integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w== @@ -3873,7 +3915,7 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.2.1, colors@^1.3.3: +colors@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -4318,7 +4360,7 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@^2.2.0, debug@^2.3.3, debug@^2.5.1, debug@^2.6.9: +debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -4457,11 +4499,6 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4719,21 +4756,6 @@ electron-publish@22.7.0: lazy-val "^1.0.4" mime "^2.4.5" -electron-rebuild@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.11.0.tgz#e384773a9ad30fe0a6a5bbb326b779d51f668b6a" - integrity sha512-cn6AqZBQBVtaEyj5jZW1/LOezZZ22PA1HvhEP7asvYPJ8PDF4i4UFt9be4i9T7xJKiSiomXvY5Fd+dSq3FXZxA== - dependencies: - colors "^1.3.3" - debug "^4.1.1" - detect-libc "^1.0.3" - fs-extra "^8.1.0" - node-abi "^2.11.0" - node-gyp "^6.0.1" - ora "^3.4.0" - spawn-rx "^3.0.0" - yargs "^14.2.0" - electron-to-chromium@^1.3.413: version "1.3.464" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5" @@ -5075,6 +5097,16 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -5602,13 +5634,6 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -5919,7 +5944,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -7692,11 +7717,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= - lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" @@ -7722,13 +7742,6 @@ lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -8111,14 +8124,6 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0, minipass@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" @@ -8126,13 +8131,6 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - minizlib@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" @@ -8300,13 +8298,6 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" -node-abi@^2.11.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.18.0.tgz#1f5486cfd7d38bd4f5392fa44a4ad4d9a0dffbf4" - integrity sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw== - dependencies: - semver "^5.4.1" - node-forge@^0.7.5: version "0.7.6" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" @@ -8335,23 +8326,6 @@ node-gyp@^3.8.0: tar "^2.0.0" which "1" -node-gyp@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-6.1.0.tgz#64e31c61a4695ad304c1d5b82cf6b7c79cc79f3f" - integrity sha512-h4A2zDlOujeeaaTx06r4Vy+8MZ1679lU+wbCKDS4ZtvY2A37DESo37oejIw0mtmR3+rvNwts5B6Kpt1KrNYdNw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.1.2" - request "^2.88.0" - rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -8487,14 +8461,6 @@ nodemon@^2.0.4: dependencies: abbrev "1" -nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -8575,7 +8541,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.1.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -8776,18 +8742,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== - dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - ora@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d" @@ -8826,7 +8780,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@0, osenv@^0.1.4: +osenv@0: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -9184,7 +9138,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -10167,6 +10121,32 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +rollup-plugin-dts@^1.4.13: + version "1.4.13" + resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-1.4.13.tgz#4f086e84f4fdcc1f49160799ebc66f6b09db292b" + integrity sha512-7mxoQ6PcmCkBE5ZhrjGDL4k42XLy8BkSqpiRi1MipwiGs+7lwi4mQkp2afX+OzzLjJp/TGM8llfe8uayIUhPEw== + optionalDependencies: + "@babel/code-frame" "^7.10.4" + +rollup-plugin-ignore-import@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-ignore-import/-/rollup-plugin-ignore-import-1.3.2.tgz#5379eac73d2c7e389ebeb5b3a90ae4c15c15e6c8" + integrity sha512-q7yH2c+PKVfb61+MTXqqyBHIgflikumC7OEB+OfQWNsSmDqE5FLZLeewcBGl1VDmjDjSXuALXsaBjyIsl3oNmQ== + +rollup-pluginutils@^2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^2.28.2: + version "2.28.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.28.2.tgz#599ec4978144a82d8a8ec3d37670a8440cb04e4b" + integrity sha512-8txbsFBFLmm9Xdt4ByTOGa9Muonmc8MfNjnGAR8U8scJlF1ZW7AgNZa7aqBXaKtlvnYP/ab++fQIq9dB9NWUbg== + optionalDependencies: + fsevents "~2.1.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -10201,13 +10181,6 @@ rx-lite@*, rx-lite@^4.0.8: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= -rxjs@^6.3.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" - integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== - dependencies: - tslib "^1.9.0" - rxjs@^6.5.2, rxjs@^6.5.3: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" @@ -10605,15 +10578,6 @@ spawn-command@^0.0.2-1: resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= -spawn-rx@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spawn-rx/-/spawn-rx-3.0.0.tgz#1d33511e13ec26337da51d78630e08beb57a6767" - integrity sha512-dw4Ryg/KMNfkKa5ezAR5aZe9wNwPdKlnHEXtHOjVnyEDSPQyOpIPPRtcIiu7127SmtHhaCjw21yC43HliW0iIg== - dependencies: - debug "^2.5.1" - lodash.assign "^4.2.0" - rxjs "^6.3.1" - spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -11086,19 +11050,6 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4.4.12: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - tar@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" @@ -12183,7 +12134,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -12214,14 +12165,6 @@ yargs-parser@^13.1.0, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^15.0.1: - version "15.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" - integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -12255,23 +12198,6 @@ yargs@^13.3.0, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^14.2.0: - version "14.2.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" - integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== - dependencies: - cliui "^5.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^15.0.1" - yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" From 459742556b931c4c16bb36681996438ade043670 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Tue, 29 Sep 2020 10:58:16 +0300 Subject: [PATCH 007/102] Fix CRD conditions rendering (#994) * Using reason field if no type provided Signed-off-by: Alex Andreev * Lowecasing condition badge class Signed-off-by: Alex Andreev --- src/renderer/api/endpoints/crd.api.ts | 2 +- .../+custom-resources/crd-resource-details.tsx | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/renderer/api/endpoints/crd.api.ts b/src/renderer/api/endpoints/crd.api.ts index 2a029e6f9d..444d59497d 100644 --- a/src/renderer/api/endpoints/crd.api.ts +++ b/src/renderer/api/endpoints/crd.api.ts @@ -50,7 +50,7 @@ export class CustomResourceDefinition extends KubeObject { message: string; reason: string; status: string; - type: string; + type?: string; }[]; acceptedNames: { plural: string; diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index 201d5e6e4a..ac64bea683 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -13,8 +13,9 @@ import { apiManager } from "../../api/api-manager"; import { crdStore } from "./crd.store"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { Input } from "../input"; +import { CustomResourceDefinition } from "../../api/endpoints/crd.api"; -interface Props extends KubeObjectDetailsProps { +interface Props extends KubeObjectDetailsProps { } function CrdColumnValue({ value }: { value: any[] | {} | string }) { @@ -66,12 +67,14 @@ export class CrdResourceDetails extends React.Component { })} {showStatus && ( Status} className="status" labelsOnly> - {object.status.conditions.map((condition: { type: string; message: string; status: string }) => { - const { type, message, status } = condition; + {object.status.conditions.map((condition, index) => { + const { type, reason, message, status } = condition; + const kind = type || reason; + if (!kind) return null; return ( ); From 82c4177cb956fc71b6a3a501c996e414afae3956 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Tue, 29 Sep 2020 16:45:42 +0300 Subject: [PATCH 008/102] Move in-tree extensions to top-level folder (#993) Signed-off-by: Jari Kolehmainen --- .../example-extension/README.md | 0 .../example-extension/example-extension.tsx | 0 .../example-extension/package.json | 0 .../example-extension/tsconfig.json | 4 ++-- package.json | 12 ++++++++---- src/extensions/extension-store.ts | 4 ---- 6 files changed, 10 insertions(+), 10 deletions(-) rename {src/extensions => extensions}/example-extension/README.md (100%) rename {src/extensions => extensions}/example-extension/example-extension.tsx (100%) rename {src/extensions => extensions}/example-extension/package.json (100%) rename {src/extensions => extensions}/example-extension/tsconfig.json (74%) diff --git a/src/extensions/example-extension/README.md b/extensions/example-extension/README.md similarity index 100% rename from src/extensions/example-extension/README.md rename to extensions/example-extension/README.md diff --git a/src/extensions/example-extension/example-extension.tsx b/extensions/example-extension/example-extension.tsx similarity index 100% rename from src/extensions/example-extension/example-extension.tsx rename to extensions/example-extension/example-extension.tsx diff --git a/src/extensions/example-extension/package.json b/extensions/example-extension/package.json similarity index 100% rename from src/extensions/example-extension/package.json rename to extensions/example-extension/package.json diff --git a/src/extensions/example-extension/tsconfig.json b/extensions/example-extension/tsconfig.json similarity index 74% rename from src/extensions/example-extension/tsconfig.json rename to extensions/example-extension/tsconfig.json index 993992112a..595bfda02f 100644 --- a/src/extensions/example-extension/tsconfig.json +++ b/extensions/example-extension/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { "outDir": ".", "module": "CommonJS", @@ -7,7 +7,7 @@ "declaration": false }, "include": [ - "../../../types", + "../../types", "./example-extension.tsx" ] } diff --git a/package.json b/package.json index 7d52e4588d..20b5740e58 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,12 @@ "dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"", "dev:main": "yarn compile:main --watch", "dev:renderer": "yarn compile:renderer --watch", - "dev:extensions": "rollup --config src/extensions/rollup.config.js --watch", + "dev:extensions": "yarn compile:extensions --watch", "compile": "env NODE_ENV=production concurrently yarn:compile:*", "compile:main": "webpack --config webpack.main.ts", "compile:renderer": "webpack --config webpack.renderer.ts", "compile:i18n": "lingui compile", + "compile:extensions": "rollup --config src/extensions/rollup.config.js", "build:linux": "yarn compile && electron-builder --linux --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", @@ -34,7 +35,7 @@ "download:kubectl": "yarn run ts-node build/download_kubectl.ts", "download:helm": "yarn run ts-node build/download_helm.ts", "lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/", - "extensions:example": "tsc --project src/extensions/example-extension/tsconfig.json --watch" + "extensions:example": "tsc --project extensions/example-extension/tsconfig.json --watch" }, "config": { "bundledKubectlVersion": "1.17.11", @@ -94,9 +95,12 @@ "filter": "!**/main.js" }, { - "from": "src/extensions/", + "from": "extensions/", "to": "./extensions/", - "filter": "**/*.js*" + "filter": [ + "**/*.js*", + "!**/node_modules" + ] }, "LICENSE" ], diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index 13be1eb927..a563535a38 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -4,7 +4,6 @@ import path from "path"; import fs from "fs-extra"; import { action, observable, reaction, toJS, } from "mobx"; import { BaseStore } from "../common/base-store"; -import { isDevelopment } from "../common/vars"; import logger from "../main/logger"; export interface ExtensionStoreModel { @@ -44,9 +43,6 @@ export class ExtensionStore extends BaseStore { @observable installed = observable.map([], { deep: false }); get folderPath(): string { - if (isDevelopment) { - return path.resolve(__static, "../src/extensions"); - } return path.resolve(__static, "../extensions"); } From c542ad03482c25df0c0ac768401b43d35e6ad44c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 29 Sep 2020 11:25:29 -0400 Subject: [PATCH 009/102] refactor overview statuses to be more DRY (#912) * refactor overview statuses to be more DRY Signed-off-by: Sebastian Malton --- locales/en/messages.po | 170 ++++++++++-------- locales/fi/messages.po | 170 ++++++++++-------- locales/ru/messages.po | 170 ++++++++++-------- .../+workloads-daemonsets/daemonsets.store.ts | 6 +- .../deployments.store.ts | 6 +- .../+workloads-overview/overview-statuses.tsx | 88 ++++----- .../replicasets.store.ts | 6 +- .../statefulset.store.ts | 6 +- src/renderer/components/+workloads/index.ts | 2 +- .../components/+workloads/workloads.route.ts | 10 ++ .../components/+workloads/workloads.stores.ts | 17 ++ src/renderer/kube-object.store.ts | 2 + src/renderer/utils/rbac.ts | 28 +++ 13 files changed, 377 insertions(+), 304 deletions(-) create mode 100644 src/renderer/components/+workloads/workloads.stores.ts create mode 100644 src/renderer/utils/rbac.ts diff --git a/locales/en/messages.po b/locales/en/messages.po index 8ae622d54c..092161d0cf 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -25,7 +25,7 @@ msgstr "" msgid "(as a percentage of request)" msgstr "(as a percentage of request)" -#: src/renderer/components/+workspaces/workspaces.tsx:108 +#: src/renderer/components/+workspaces/workspaces.tsx:121 msgid "(current)" msgstr "(current)" @@ -57,11 +57,11 @@ msgstr "<0>{0} successfully created" #~ msgid "A HTTP proxy server URL (format: http://
:)" #~ msgstr "A HTTP proxy server URL (format: http://
:)" -#: src/renderer/components/input/input.validators.ts:40 +#: src/renderer/components/input/input.validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." -#: src/renderer/components/+workspaces/workspaces.tsx:84 +#: src/renderer/components/+workspaces/workspaces.tsx:93 msgid "A single workspaces contains a list of clusters and their full configuration." msgstr "A single workspaces contains a list of clusters and their full configuration." @@ -87,8 +87,8 @@ msgstr "Account Name" msgid "Active" msgstr "Active" -#: src/renderer/components/+add-cluster/add-cluster.tsx:303 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 +#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "Add Cluster" @@ -100,7 +100,7 @@ msgstr "Add Namespace" msgid "Add RoleBinding" msgstr "Add RoleBinding" -#: src/renderer/components/+workspaces/workspaces.tsx:125 +#: src/renderer/components/+workspaces/workspaces.tsx:138 msgid "Add Workspace" msgstr "Add Workspace" @@ -112,7 +112,7 @@ msgstr "Add bindings to {name}" #~ msgid "Add cluster" #~ msgstr "Add cluster" -#: src/renderer/components/+add-cluster/add-cluster.tsx:320 +#: src/renderer/components/+add-cluster/add-cluster.tsx:305 msgid "Add cluster(s)" msgstr "Add cluster(s)" @@ -136,7 +136,7 @@ msgstr "Add field" #~ msgid "Adding clusters: <0>{0}" #~ msgstr "Adding clusters: <0>{0}" -#: src/renderer/components/+preferences/preferences.tsx:103 +#: src/renderer/components/+preferences/preferences.tsx:111 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "Adding helm branch <0>{0} has failed: {1}" @@ -191,7 +191,7 @@ msgstr "Affinities" msgid "Age" msgstr "Age" -#: src/renderer/components/+workspaces/workspaces.tsx:64 +#: src/renderer/components/+workspaces/workspaces.tsx:65 msgid "All clusters within workspace will be cleared as well" msgstr "All clusters within workspace will be cleared as well" @@ -219,11 +219,11 @@ msgstr "Allocatable" msgid "Allow Privilege Escalation" msgstr "Allow Privilege Escalation" -#: src/renderer/components/+preferences/preferences.tsx:162 +#: src/renderer/components/+preferences/preferences.tsx:169 msgid "Allow telemetry & usage tracking" msgstr "Allow telemetry & usage tracking" -#: src/renderer/components/+preferences/preferences.tsx:154 +#: src/renderer/components/+preferences/preferences.tsx:161 msgid "Allow untrusted Certificate Authorities" msgstr "Allow untrusted Certificate Authorities" @@ -281,7 +281,7 @@ msgstr "Applying.." msgid "Apps" msgstr "Apps" -#: src/renderer/components/+workspaces/workspaces.tsx:61 +#: src/renderer/components/+workspaces/workspaces.tsx:62 msgid "Are you sure you want remove workspace <0>{0}?" msgstr "Are you sure you want remove workspace <0>{0}?" @@ -293,7 +293,7 @@ msgstr "Are you sure you want to drain <0>{nodeName}?" msgid "Arguments" msgstr "Arguments" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:108 +#: src/renderer/components/+landing-page/landing-page.tsx:27 msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button." msgstr "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button." @@ -323,7 +323,7 @@ msgstr "Binding targets" msgid "Bindings" msgstr "Bindings" -#: src/renderer/components/+add-cluster/add-cluster.tsx:251 +#: src/renderer/components/+add-cluster/add-cluster.tsx:236 msgid "Browse" msgstr "Browse" @@ -402,7 +402,7 @@ msgstr "CPU requests" msgid "CPU:" msgstr "CPU:" -#: src/renderer/components/+workspaces/workspaces.tsx:119 +#: src/renderer/components/+workspaces/workspaces.tsx:133 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/wizard/wizard.tsx:130 @@ -422,7 +422,7 @@ msgstr "Cancel" msgid "Capacity" msgstr "Capacity" -#: src/renderer/components/+preferences/preferences.tsx:153 +#: src/renderer/components/+preferences/preferences.tsx:160 msgid "Certificate Trust" msgstr "Certificate Trust" @@ -501,7 +501,7 @@ msgstr "Cluster IP" msgid "Cluster Issuers" msgstr "Cluster Issuers" -#: src/renderer/components/+preferences/preferences.tsx:126 +#: src/renderer/components/+preferences/preferences.tsx:134 msgid "Color Theme" msgstr "Color Theme" @@ -712,7 +712,6 @@ msgid "Cron Jobs" msgstr "Cron Jobs" #: src/renderer/components/+workloads/workloads.tsx:77 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:67 msgid "CronJobs" msgstr "CronJobs" @@ -759,7 +758,6 @@ msgid "Daemon Sets" msgstr "Daemon Sets" #: src/renderer/components/+workloads/workloads.tsx:53 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:57 msgid "DaemonSets" msgstr "DaemonSets" @@ -784,11 +782,15 @@ msgstr "Default Add Capabilities" msgid "Default Runtime Class Name" msgstr "Default Runtime Class Name" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:30 +msgid "Default:" +msgstr "Default:" + #: src/renderer/components/+custom-resources/custom-resources.tsx:22 msgid "Definitions" msgstr "Definitions" -#: src/renderer/components/+workspaces/workspaces.tsx:113 +#: src/renderer/components/+workspaces/workspaces.tsx:126 #: src/renderer/components/menu/menu-actions.tsx:84 msgid "Delete" msgstr "Delete" @@ -799,12 +801,11 @@ msgstr "Deploy Revisions" #: src/renderer/components/+workloads/workloads.tsx:45 #: src/renderer/components/+workloads-deployments/deployments.tsx:57 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:47 msgid "Deployments" msgstr "Deployments" #: src/renderer/components/+apps-helm-charts/helm-charts.tsx:65 -#: src/renderer/components/+workspaces/workspaces.tsx:118 +#: src/renderer/components/+workspaces/workspaces.tsx:131 msgid "Description" msgstr "Description" @@ -817,7 +818,7 @@ msgstr "Desired Healthy" msgid "Desired number of replicas" msgstr "Desired number of replicas" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:64 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:65 msgid "Disconnect" msgstr "Disconnect" @@ -831,7 +832,7 @@ msgstr "Disk" msgid "Disk:" msgstr "Disk:" -#: src/renderer/components/+preferences/preferences.tsx:158 +#: src/renderer/components/+preferences/preferences.tsx:165 msgid "Does not affect cluster communications!" msgstr "Does not affect cluster communications!" @@ -840,14 +841,22 @@ msgid "Domains" msgstr "Domains" #: src/renderer/components/+preferences/preferences.tsx:129 -msgid "Download Mirror" -msgstr "Download Mirror" +#~ msgid "Download Mirror" +#~ msgstr "Download Mirror" #: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90 msgid "Download file" msgstr "Download file" -#: src/renderer/components/+preferences/preferences.tsx:130 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:39 +msgid "Download kubectl binaries" +msgstr "Download kubectl binaries" + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:37 +msgid "Download kubectl binaries matching to Kubernetes cluster verison." +msgstr "Download kubectl binaries matching to Kubernetes cluster verison." + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:41 msgid "Download mirror for kubectl" msgstr "Download mirror for kubectl" @@ -873,7 +882,7 @@ msgstr "Duration" msgid "E-mail" msgstr "E-mail" -#: src/renderer/components/+workspaces/workspaces.tsx:112 +#: src/renderer/components/+workspaces/workspaces.tsx:125 #: src/renderer/components/menu/menu-actions.tsx:80 #: src/renderer/components/menu/menu-actions.tsx:81 msgid "Edit" @@ -1000,7 +1009,7 @@ msgstr "From <0>{from} to <1>{to}" msgid "Fs Group" msgstr "Fs Group" -#: src/renderer/components/+landing-page/landing-page.tsx:23 +#: src/renderer/components/+landing-page/landing-page.tsx:37 msgid "Get started by associating one or more clusters to Lens." msgstr "Get started by associating one or more clusters to Lens." @@ -1022,7 +1031,7 @@ msgstr "Groups" msgid "HPA" msgstr "HPA" -#: src/renderer/components/+preferences/preferences.tsx:147 +#: src/renderer/components/+preferences/preferences.tsx:137 msgid "HTTP Proxy" msgstr "HTTP Proxy" @@ -1030,7 +1039,7 @@ msgstr "HTTP Proxy" #~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API." #~ msgstr "HTTP Proxy server. Used for communicating with Kubernetes API." -#: src/renderer/components/+preferences/preferences.tsx:132 +#: src/renderer/components/+preferences/preferences.tsx:145 msgid "Helm" msgstr "Helm" @@ -1050,7 +1059,7 @@ msgstr "Helm Install: {repo}/{name}" msgid "Helm Upgrade: {0}" msgstr "Helm Upgrade: {0}" -#: src/renderer/components/+preferences/preferences.tsx:47 +#: src/renderer/components/+preferences/preferences.tsx:51 msgid "Helm branch <0>{0} already in use" msgstr "Helm branch <0>{0} already in use" @@ -1157,11 +1166,11 @@ msgstr "Installation complete!" msgid "Installing..." msgstr "Installing..." -#: src/renderer/components/input/input.validators.ts:44 +#: src/renderer/components/input/input.validators.ts:50 msgid "Invalid account ID" msgstr "Invalid account ID" -#: src/renderer/components/input/input.validators.ts:15 +#: src/renderer/components/input/input.validators.ts:16 msgid "Invalid number" msgstr "Invalid number" @@ -1197,7 +1206,6 @@ msgstr "Job name" #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62 msgid "Jobs" msgstr "Jobs" @@ -1241,6 +1249,10 @@ msgstr "Kubeconfig" msgid "Kubeconfig File" msgstr "Kubeconfig File" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +msgid "Kubectl Binary" +msgstr "Kubectl Binary" + #: src/renderer/components/+nodes/node-details.tsx:98 msgid "Kubelet version" msgstr "Kubelet version" @@ -1357,7 +1369,7 @@ msgstr "Max Pods" msgid "Max Unavailable" msgstr "Max Unavailable" -#: src/renderer/components/input/input.validators.ts:35 +#: src/renderer/components/input/input.validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "Maximum length is {maxLength}" @@ -1433,7 +1445,7 @@ msgstr "Min Pods" msgid "Minimize" msgstr "Minimize" -#: src/renderer/components/input/input.validators.ts:30 +#: src/renderer/components/input/input.validators.ts:36 msgid "Minimum length is {minLength}" msgstr "Minimum length is {minLength}" @@ -1497,7 +1509,7 @@ msgstr "Mounts" #: src/renderer/components/+workloads-pods/pods.tsx:74 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 -#: src/renderer/components/+workspaces/workspaces.tsx:117 +#: src/renderer/components/+workspaces/workspaces.tsx:130 #: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" @@ -1565,7 +1577,7 @@ msgstr "Namespaces" msgid "Namespaces: {0}" msgstr "Namespaces: {0}" -#: src/renderer/components/+preferences/preferences.tsx:157 +#: src/renderer/components/+preferences/preferences.tsx:164 msgid "Needed with some corporate proxies that do certificate re-writing." msgstr "Needed with some corporate proxies that do certificate re-writing." @@ -1626,7 +1638,7 @@ msgstr "No Nodes Available." #~ msgid "No contexts available or they already added" #~ msgstr "No contexts available or they already added" -#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#: src/renderer/components/+add-cluster/add-cluster.tsx:260 msgid "No contexts available or they have been added already" msgstr "No contexts available or they have been added already" @@ -1742,7 +1754,7 @@ msgid "Organization" msgstr "Organization" #: src/renderer/components/+workloads/workloads.tsx:29 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35 +#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45 msgid "Overview" msgstr "Overview" @@ -1758,7 +1770,7 @@ msgstr "Parallelism" msgid "Parameters" msgstr "Parameters" -#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#: src/renderer/components/+add-cluster/add-cluster.tsx:230 msgid "Paste as text" msgstr "Paste as text" @@ -1848,7 +1860,6 @@ msgstr "Pod shell" #: src/renderer/components/+workloads/workloads.tsx:37 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47 #: src/renderer/components/+workloads-deployments/deployments.tsx:60 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:89 #: src/renderer/components/+workloads-pods/pods.tsx:73 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:52 @@ -1899,7 +1910,7 @@ msgstr "Privileged" #~ msgid "Pro-Tip: paste kubeconfig to collect available contexts" #~ msgstr "Pro-Tip: paste kubeconfig to collect available contexts" -#: src/renderer/components/+add-cluster/add-cluster.tsx:263 +#: src/renderer/components/+add-cluster/add-cluster.tsx:248 msgid "Pro-Tip: paste kubeconfig to get available contexts" msgstr "Pro-Tip: paste kubeconfig to get available contexts" @@ -1907,7 +1918,7 @@ msgstr "Pro-Tip: paste kubeconfig to get available contexts" #~ msgid "Pro-Tip: paste kubeconfig to parse available contexts" #~ msgstr "Pro-Tip: paste kubeconfig to parse available contexts" -#: src/renderer/components/+add-cluster/add-cluster.tsx:254 +#: src/renderer/components/+add-cluster/add-cluster.tsx:239 msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" msgstr "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" @@ -1924,11 +1935,11 @@ msgstr "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" msgid "Provisioner" msgstr "Provisioner" -#: src/renderer/components/+preferences/preferences.tsx:150 +#: src/renderer/components/+preferences/preferences.tsx:140 msgid "Proxy is used only for non-cluster communication." msgstr "Proxy is used only for non-cluster communication." -#: src/renderer/components/+add-cluster/add-cluster.tsx:308 +#: src/renderer/components/+add-cluster/add-cluster.tsx:293 msgid "Proxy settings" msgstr "Proxy settings" @@ -2008,10 +2019,10 @@ msgstr "Release: {0}" msgid "Releases" msgstr "Releases" -#: src/renderer/components/+preferences/preferences.tsx:139 +#: src/renderer/components/+preferences/preferences.tsx:152 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:74 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:80 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:76 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:82 #: 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:85 @@ -2022,7 +2033,7 @@ msgstr "Remove" msgid "Remove <0>{releaseNames}?" msgstr "Remove <0>{releaseNames}?" -#: src/renderer/components/+workspaces/workspaces.tsx:51 +#: src/renderer/components/+workspaces/workspaces.tsx:52 msgid "Remove Workspace" msgstr "Remove Workspace" @@ -2050,7 +2061,7 @@ msgstr "Remove selected items ({0})" msgid "Remove {resourceKind} <0>{resourceName}?" msgstr "Remove {resourceKind} <0>{resourceName}?" -#: src/renderer/components/+preferences/preferences.tsx:114 +#: src/renderer/components/+preferences/preferences.tsx:122 msgid "Removing helm branch <0>{0} has failed: {1}" msgstr "Removing helm branch <0>{0} has failed: {1}" @@ -2074,7 +2085,7 @@ msgstr "Replicas" msgid "Repo/Name" msgstr "Repo/Name" -#: src/renderer/components/+preferences/preferences.tsx:133 +#: src/renderer/components/+preferences/preferences.tsx:146 msgid "Repositories" msgstr "Repositories" @@ -2109,7 +2120,7 @@ msgstr "Required Drop Capabilities" msgid "Required field" msgstr "Required field" -#: src/renderer/components/+add-cluster/add-cluster.tsx:250 +#: src/renderer/components/+add-cluster/add-cluster.tsx:235 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "Reset" @@ -2252,7 +2263,7 @@ msgstr "Runtime Class" #: src/renderer/components/+config-maps/config-map-details.tsx:78 #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 -#: src/renderer/components/+workspaces/workspaces.tsx:120 +#: src/renderer/components/+workspaces/workspaces.tsx:132 #: src/renderer/components/dock/edit-resource.tsx:88 msgid "Save" msgstr "Save" @@ -2341,13 +2352,13 @@ msgstr "Select a quota.." #~ msgid "Select context(s)" #~ msgstr "Select context(s)" -#: src/renderer/components/+add-cluster/add-cluster.tsx:272 -#~ msgid "Select contexts" -#~ msgstr "Select contexts" +#: src/renderer/components/+add-cluster/add-cluster.tsx:257 +msgid "Select contexts" +msgstr "Select contexts" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts (available: {0})" -msgstr "Select contexts (available: {0})" +#~ msgid "Select contexts (available: {0})" +#~ msgstr "Select contexts (available: {0})" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 #: src/renderer/components/+add-cluster/add-cluster.tsx:76 @@ -2371,7 +2382,7 @@ msgstr "Select custom kubeconfig file" #~ msgid "Select kubeconfig" #~ msgstr "Select kubeconfig" -#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 msgid "Select kubeconfig file" msgstr "Select kubeconfig file" @@ -2399,7 +2410,7 @@ msgstr "Select service accounts" #~ msgid "Selected contexts ({0}): <0>{1}" #~ msgstr "Selected contexts ({0}): <0>{1}" -#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +#: src/renderer/components/+add-cluster/add-cluster.tsx:256 msgid "Selected contexts: <0>{0}" msgstr "Selected contexts: <0>{0}" @@ -2503,7 +2514,6 @@ msgid "Stateful Sets" msgstr "Stateful Sets" #: src/renderer/components/+workloads/workloads.tsx:61 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52 msgid "StatefulSets" msgstr "StatefulSets" @@ -2605,11 +2615,11 @@ msgstr "TLS" msgid "Taints" msgstr "Taints" -#: src/renderer/components/+preferences/preferences.tsx:161 +#: src/renderer/components/+preferences/preferences.tsx:168 msgid "Telemetry & Usage Tracking" msgstr "Telemetry & Usage Tracking" -#: src/renderer/components/+preferences/preferences.tsx:164 +#: src/renderer/components/+preferences/preferences.tsx:171 msgid "Telemetry & usage data is collected to continuously improve the Lens experience." msgstr "Telemetry & usage data is collected to continuously improve the Lens experience." @@ -2629,15 +2639,19 @@ msgstr "There are no logs available for container." msgid "There are no logs available." msgstr "There are no logs available." -#: src/renderer/components/input/input.validators.ts:5 +#: src/renderer/components/input/input.validators.ts:6 msgid "This field is required" msgstr "This field is required" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 +#: src/renderer/components/input/input.validators.ts:31 +msgid "This field must be a valid path" +msgstr "This field must be a valid path" + +#: src/renderer/components/+landing-page/landing-page.tsx:25 msgid "This is the quick launch menu." msgstr "This is the quick launch menu." -#: src/renderer/components/+preferences/preferences.tsx:156 +#: src/renderer/components/+preferences/preferences.tsx:163 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." @@ -2661,13 +2675,13 @@ msgstr "Tolerations" msgid "Transmit" msgstr "Transmit" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 msgid "Trigger" msgstr "Trigger" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102 msgid "Trigger CronJob <0>{cronjobName}" msgstr "Trigger CronJob <0>{cronjobName}" @@ -2690,7 +2704,7 @@ msgstr "Trigger CronJob <0>{cronjobName}" msgid "Type" msgstr "Type" -#: src/renderer/components/+preferences/preferences.tsx:148 +#: src/renderer/components/+preferences/preferences.tsx:138 msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)" msgstr "Type HTTP proxy url (example: http://proxy.acme.org:8080)" @@ -2827,11 +2841,11 @@ msgstr "Waiting services to be running" msgid "Warnings: {0}" msgstr "Warnings: {0}" -#: src/renderer/components/+landing-page/landing-page.tsx:20 +#: src/renderer/components/+landing-page/landing-page.tsx:34 msgid "Welcome!" msgstr "Welcome!" -#: src/renderer/components/+workspaces/workspaces.tsx:79 +#: src/renderer/components/+workspaces/workspaces.tsx:88 msgid "What is a Workspace?" msgstr "What is a Workspace?" @@ -2844,19 +2858,19 @@ msgid "Workloads" msgstr "Workloads" #: src/renderer/components/+workspaces/workspace-menu.tsx:39 -#: src/renderer/components/+workspaces/workspaces.tsx:91 +#: src/renderer/components/+workspaces/workspaces.tsx:100 msgid "Workspaces" msgstr "Workspaces" -#: src/renderer/components/+workspaces/workspaces.tsx:81 +#: src/renderer/components/+workspaces/workspaces.tsx:90 msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "Workspaces are used to organize number of clusters into logical groups." -#: src/renderer/components/input/input.validators.ts:10 +#: src/renderer/components/input/input.validators.ts:11 msgid "Wrong email format" msgstr "Wrong email format" -#: src/renderer/components/input/input.validators.ts:25 +#: src/renderer/components/input/input.validators.ts:26 msgid "Wrong url format" msgstr "Wrong url format" @@ -2915,7 +2929,7 @@ msgstr "listKind" msgid "never" msgstr "never" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:133 msgid "new" msgstr "new" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index f64a50f272..363b9a49cc 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -25,7 +25,7 @@ msgstr "" msgid "(as a percentage of request)" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:108 +#: src/renderer/components/+workspaces/workspaces.tsx:121 msgid "(current)" msgstr "" @@ -57,11 +57,11 @@ msgstr "" #~ msgid "A HTTP proxy server URL (format: http://
:)" #~ msgstr "" -#: src/renderer/components/input/input.validators.ts:40 +#: src/renderer/components/input/input.validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:84 +#: src/renderer/components/+workspaces/workspaces.tsx:93 msgid "A single workspaces contains a list of clusters and their full configuration." msgstr "" @@ -87,8 +87,8 @@ msgstr "" msgid "Active" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:303 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 +#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "" @@ -100,7 +100,7 @@ msgstr "" msgid "Add RoleBinding" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:125 +#: src/renderer/components/+workspaces/workspaces.tsx:138 msgid "Add Workspace" msgstr "" @@ -112,7 +112,7 @@ msgstr "" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:320 +#: src/renderer/components/+add-cluster/add-cluster.tsx:305 msgid "Add cluster(s)" msgstr "" @@ -136,7 +136,7 @@ msgstr "" #~ msgid "Adding clusters: <0>{0}" #~ msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:103 +#: src/renderer/components/+preferences/preferences.tsx:111 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "" @@ -191,7 +191,7 @@ msgstr "" msgid "Age" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:64 +#: src/renderer/components/+workspaces/workspaces.tsx:65 msgid "All clusters within workspace will be cleared as well" msgstr "" @@ -219,11 +219,11 @@ msgstr "" msgid "Allow Privilege Escalation" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:162 +#: src/renderer/components/+preferences/preferences.tsx:169 msgid "Allow telemetry & usage tracking" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:154 +#: src/renderer/components/+preferences/preferences.tsx:161 msgid "Allow untrusted Certificate Authorities" msgstr "" @@ -281,7 +281,7 @@ msgstr "" msgid "Apps" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:61 +#: src/renderer/components/+workspaces/workspaces.tsx:62 msgid "Are you sure you want remove workspace <0>{0}?" msgstr "" @@ -293,7 +293,7 @@ msgstr "" msgid "Arguments" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:108 +#: src/renderer/components/+landing-page/landing-page.tsx:27 msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button." msgstr "" @@ -323,7 +323,7 @@ msgstr "" msgid "Bindings" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:251 +#: src/renderer/components/+add-cluster/add-cluster.tsx:236 msgid "Browse" msgstr "" @@ -402,7 +402,7 @@ msgstr "" msgid "CPU:" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:119 +#: src/renderer/components/+workspaces/workspaces.tsx:133 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/wizard/wizard.tsx:130 @@ -422,7 +422,7 @@ msgstr "" msgid "Capacity" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:153 +#: src/renderer/components/+preferences/preferences.tsx:160 msgid "Certificate Trust" msgstr "" @@ -497,7 +497,7 @@ msgstr "" msgid "Cluster Issuers" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:126 +#: src/renderer/components/+preferences/preferences.tsx:134 msgid "Color Theme" msgstr "" @@ -708,7 +708,6 @@ msgid "Cron Jobs" msgstr "" #: src/renderer/components/+workloads/workloads.tsx:77 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:67 msgid "CronJobs" msgstr "" @@ -755,7 +754,6 @@ msgid "Daemon Sets" msgstr "" #: src/renderer/components/+workloads/workloads.tsx:53 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:57 msgid "DaemonSets" msgstr "" @@ -780,11 +778,15 @@ msgstr "" msgid "Default Runtime Class Name" msgstr "" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:30 +msgid "Default:" +msgstr "" + #: src/renderer/components/+custom-resources/custom-resources.tsx:22 msgid "Definitions" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:113 +#: src/renderer/components/+workspaces/workspaces.tsx:126 #: src/renderer/components/menu/menu-actions.tsx:84 msgid "Delete" msgstr "" @@ -795,12 +797,11 @@ msgstr "" #: src/renderer/components/+workloads/workloads.tsx:45 #: src/renderer/components/+workloads-deployments/deployments.tsx:57 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:47 msgid "Deployments" msgstr "" #: src/renderer/components/+apps-helm-charts/helm-charts.tsx:65 -#: src/renderer/components/+workspaces/workspaces.tsx:118 +#: src/renderer/components/+workspaces/workspaces.tsx:131 msgid "Description" msgstr "" @@ -813,7 +814,7 @@ msgstr "" msgid "Desired number of replicas" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:64 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:65 msgid "Disconnect" msgstr "" @@ -827,7 +828,7 @@ msgstr "" msgid "Disk:" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:158 +#: src/renderer/components/+preferences/preferences.tsx:165 msgid "Does not affect cluster communications!" msgstr "" @@ -836,14 +837,22 @@ msgid "Domains" msgstr "" #: src/renderer/components/+preferences/preferences.tsx:129 -msgid "Download Mirror" -msgstr "" +#~ msgid "Download Mirror" +#~ msgstr "" #: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90 msgid "Download file" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:130 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:39 +msgid "Download kubectl binaries" +msgstr "" + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:37 +msgid "Download kubectl binaries matching to Kubernetes cluster verison." +msgstr "" + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:41 msgid "Download mirror for kubectl" msgstr "" @@ -869,7 +878,7 @@ msgstr "" msgid "E-mail" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:112 +#: src/renderer/components/+workspaces/workspaces.tsx:125 #: src/renderer/components/menu/menu-actions.tsx:80 #: src/renderer/components/menu/menu-actions.tsx:81 msgid "Edit" @@ -991,7 +1000,7 @@ msgstr "" msgid "Fs Group" msgstr "" -#: src/renderer/components/+landing-page/landing-page.tsx:23 +#: src/renderer/components/+landing-page/landing-page.tsx:37 msgid "Get started by associating one or more clusters to Lens." msgstr "" @@ -1013,7 +1022,7 @@ msgstr "" msgid "HPA" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:147 +#: src/renderer/components/+preferences/preferences.tsx:137 msgid "HTTP Proxy" msgstr "" @@ -1021,7 +1030,7 @@ msgstr "" #~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API." #~ msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:132 +#: src/renderer/components/+preferences/preferences.tsx:145 msgid "Helm" msgstr "" @@ -1041,7 +1050,7 @@ msgstr "" msgid "Helm Upgrade: {0}" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:47 +#: src/renderer/components/+preferences/preferences.tsx:51 msgid "Helm branch <0>{0} already in use" msgstr "" @@ -1148,11 +1157,11 @@ msgstr "" msgid "Installing..." msgstr "" -#: src/renderer/components/input/input.validators.ts:44 +#: src/renderer/components/input/input.validators.ts:50 msgid "Invalid account ID" msgstr "" -#: src/renderer/components/input/input.validators.ts:15 +#: src/renderer/components/input/input.validators.ts:16 msgid "Invalid number" msgstr "" @@ -1188,7 +1197,6 @@ msgstr "" #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62 msgid "Jobs" msgstr "" @@ -1232,6 +1240,10 @@ msgstr "" msgid "Kubeconfig File" msgstr "" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +msgid "Kubectl Binary" +msgstr "" + #: src/renderer/components/+nodes/node-details.tsx:98 msgid "Kubelet version" msgstr "" @@ -1348,7 +1360,7 @@ msgstr "" msgid "Max Unavailable" msgstr "" -#: src/renderer/components/input/input.validators.ts:35 +#: src/renderer/components/input/input.validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "" @@ -1424,7 +1436,7 @@ msgstr "" msgid "Minimize" msgstr "" -#: src/renderer/components/input/input.validators.ts:30 +#: src/renderer/components/input/input.validators.ts:36 msgid "Minimum length is {minLength}" msgstr "" @@ -1488,7 +1500,7 @@ msgstr "" #: src/renderer/components/+workloads-pods/pods.tsx:74 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 -#: src/renderer/components/+workspaces/workspaces.tsx:117 +#: src/renderer/components/+workspaces/workspaces.tsx:130 #: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" @@ -1556,7 +1568,7 @@ msgstr "" msgid "Namespaces: {0}" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:157 +#: src/renderer/components/+preferences/preferences.tsx:164 msgid "Needed with some corporate proxies that do certificate re-writing." msgstr "" @@ -1609,7 +1621,7 @@ msgstr "" #~ msgid "No contexts available or they already added" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#: src/renderer/components/+add-cluster/add-cluster.tsx:260 msgid "No contexts available or they have been added already" msgstr "" @@ -1725,7 +1737,7 @@ msgid "Organization" msgstr "" #: src/renderer/components/+workloads/workloads.tsx:29 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35 +#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45 msgid "Overview" msgstr "" @@ -1741,7 +1753,7 @@ msgstr "" msgid "Parameters" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#: src/renderer/components/+add-cluster/add-cluster.tsx:230 msgid "Paste as text" msgstr "" @@ -1831,7 +1843,6 @@ msgstr "" #: src/renderer/components/+workloads/workloads.tsx:37 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47 #: src/renderer/components/+workloads-deployments/deployments.tsx:60 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:89 #: src/renderer/components/+workloads-pods/pods.tsx:73 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:52 @@ -1882,7 +1893,7 @@ msgstr "" #~ msgid "Pro-Tip: paste kubeconfig to collect available contexts" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:263 +#: src/renderer/components/+add-cluster/add-cluster.tsx:248 msgid "Pro-Tip: paste kubeconfig to get available contexts" msgstr "" @@ -1890,7 +1901,7 @@ msgstr "" #~ msgid "Pro-Tip: paste kubeconfig to parse available contexts" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:254 +#: src/renderer/components/+add-cluster/add-cluster.tsx:239 msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" msgstr "" @@ -1907,11 +1918,11 @@ msgstr "" msgid "Provisioner" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:150 +#: src/renderer/components/+preferences/preferences.tsx:140 msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:308 +#: src/renderer/components/+add-cluster/add-cluster.tsx:293 msgid "Proxy settings" msgstr "" @@ -1991,10 +2002,10 @@ msgstr "" msgid "Releases" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:139 +#: src/renderer/components/+preferences/preferences.tsx:152 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:74 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:80 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:76 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:82 #: 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:85 @@ -2005,7 +2016,7 @@ msgstr "" msgid "Remove <0>{releaseNames}?" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:51 +#: src/renderer/components/+workspaces/workspaces.tsx:52 msgid "Remove Workspace" msgstr "" @@ -2033,7 +2044,7 @@ msgstr "" msgid "Remove {resourceKind} <0>{resourceName}?" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:114 +#: src/renderer/components/+preferences/preferences.tsx:122 msgid "Removing helm branch <0>{0} has failed: {1}" msgstr "" @@ -2057,7 +2068,7 @@ msgstr "" msgid "Repo/Name" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:133 +#: src/renderer/components/+preferences/preferences.tsx:146 msgid "Repositories" msgstr "" @@ -2092,7 +2103,7 @@ msgstr "" msgid "Required field" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:250 +#: src/renderer/components/+add-cluster/add-cluster.tsx:235 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "" @@ -2235,7 +2246,7 @@ msgstr "" #: src/renderer/components/+config-maps/config-map-details.tsx:78 #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 -#: src/renderer/components/+workspaces/workspaces.tsx:120 +#: src/renderer/components/+workspaces/workspaces.tsx:132 #: src/renderer/components/dock/edit-resource.tsx:88 msgid "Save" msgstr "" @@ -2324,13 +2335,13 @@ msgstr "" #~ msgid "Select context(s)" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:272 -#~ msgid "Select contexts" -#~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:257 +msgid "Select contexts" +msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts (available: {0})" -msgstr "" +#~ msgid "Select contexts (available: {0})" +#~ msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 #: src/renderer/components/+add-cluster/add-cluster.tsx:76 @@ -2354,7 +2365,7 @@ msgstr "" #~ msgid "Select kubeconfig" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 msgid "Select kubeconfig file" msgstr "" @@ -2382,7 +2393,7 @@ msgstr "" #~ msgid "Selected contexts ({0}): <0>{1}" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +#: src/renderer/components/+add-cluster/add-cluster.tsx:256 msgid "Selected contexts: <0>{0}" msgstr "" @@ -2486,7 +2497,6 @@ msgid "Stateful Sets" msgstr "" #: src/renderer/components/+workloads/workloads.tsx:61 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52 msgid "StatefulSets" msgstr "" @@ -2588,11 +2598,11 @@ msgstr "" msgid "Taints" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:161 +#: src/renderer/components/+preferences/preferences.tsx:168 msgid "Telemetry & Usage Tracking" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:164 +#: src/renderer/components/+preferences/preferences.tsx:171 msgid "Telemetry & usage data is collected to continuously improve the Lens experience." msgstr "" @@ -2612,15 +2622,19 @@ msgstr "" msgid "There are no logs available." msgstr "" -#: src/renderer/components/input/input.validators.ts:5 +#: src/renderer/components/input/input.validators.ts:6 msgid "This field is required" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 +#: src/renderer/components/input/input.validators.ts:31 +msgid "This field must be a valid path" +msgstr "" + +#: src/renderer/components/+landing-page/landing-page.tsx:25 msgid "This is the quick launch menu." msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:156 +#: src/renderer/components/+preferences/preferences.tsx:163 msgid "This will make Lens to trust ANY certificate authority without any validations." msgstr "" @@ -2644,13 +2658,13 @@ msgstr "" msgid "Transmit" msgstr "" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 msgid "Trigger" msgstr "" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102 msgid "Trigger CronJob <0>{cronjobName}" msgstr "" @@ -2673,7 +2687,7 @@ msgstr "" msgid "Type" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:148 +#: src/renderer/components/+preferences/preferences.tsx:138 msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)" msgstr "" @@ -2810,11 +2824,11 @@ msgstr "" msgid "Warnings: {0}" msgstr "" -#: src/renderer/components/+landing-page/landing-page.tsx:20 +#: src/renderer/components/+landing-page/landing-page.tsx:34 msgid "Welcome!" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:79 +#: src/renderer/components/+workspaces/workspaces.tsx:88 msgid "What is a Workspace?" msgstr "" @@ -2827,19 +2841,19 @@ msgid "Workloads" msgstr "" #: src/renderer/components/+workspaces/workspace-menu.tsx:39 -#: src/renderer/components/+workspaces/workspaces.tsx:91 +#: src/renderer/components/+workspaces/workspaces.tsx:100 msgid "Workspaces" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:81 +#: src/renderer/components/+workspaces/workspaces.tsx:90 msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "" -#: src/renderer/components/input/input.validators.ts:10 +#: src/renderer/components/input/input.validators.ts:11 msgid "Wrong email format" msgstr "" -#: src/renderer/components/input/input.validators.ts:25 +#: src/renderer/components/input/input.validators.ts:26 msgid "Wrong url format" msgstr "" @@ -2898,7 +2912,7 @@ msgstr "" msgid "never" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:133 msgid "new" msgstr "" diff --git a/locales/ru/messages.po b/locales/ru/messages.po index e3a021d837..ea3d5a9731 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -26,7 +26,7 @@ msgstr "" msgid "(as a percentage of request)" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:108 +#: src/renderer/components/+workspaces/workspaces.tsx:121 msgid "(current)" msgstr "" @@ -58,11 +58,11 @@ msgstr "" #~ msgid "A HTTP proxy server URL (format: http://
:)" #~ msgstr "" -#: src/renderer/components/input/input.validators.ts:40 +#: src/renderer/components/input/input.validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "Это поле может содержать только латинские буквы в нижнем регистре, номера и дефис." -#: src/renderer/components/+workspaces/workspaces.tsx:84 +#: src/renderer/components/+workspaces/workspaces.tsx:93 msgid "A single workspaces contains a list of clusters and their full configuration." msgstr "" @@ -88,8 +88,8 @@ msgstr "Название аккаунта" msgid "Active" msgstr "Активный" -#: src/renderer/components/+add-cluster/add-cluster.tsx:303 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 +#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "" @@ -101,7 +101,7 @@ msgstr "Добавить Namespace" msgid "Add RoleBinding" msgstr "Добавить привязку ролей" -#: src/renderer/components/+workspaces/workspaces.tsx:125 +#: src/renderer/components/+workspaces/workspaces.tsx:138 msgid "Add Workspace" msgstr "" @@ -113,7 +113,7 @@ msgstr "Добавить привязки к {name}" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:320 +#: src/renderer/components/+add-cluster/add-cluster.tsx:305 msgid "Add cluster(s)" msgstr "" @@ -137,7 +137,7 @@ msgstr "Добавить поле" #~ msgid "Adding clusters: <0>{0}" #~ msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:103 +#: src/renderer/components/+preferences/preferences.tsx:111 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "" @@ -192,7 +192,7 @@ msgstr "Аффинитеты" msgid "Age" msgstr "Возраст" -#: src/renderer/components/+workspaces/workspaces.tsx:64 +#: src/renderer/components/+workspaces/workspaces.tsx:65 msgid "All clusters within workspace will be cleared as well" msgstr "" @@ -220,11 +220,11 @@ msgstr "" msgid "Allow Privilege Escalation" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:162 +#: src/renderer/components/+preferences/preferences.tsx:169 msgid "Allow telemetry & usage tracking" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:154 +#: src/renderer/components/+preferences/preferences.tsx:161 msgid "Allow untrusted Certificate Authorities" msgstr "" @@ -282,7 +282,7 @@ msgstr "Применение.." msgid "Apps" msgstr "Приложения" -#: src/renderer/components/+workspaces/workspaces.tsx:61 +#: src/renderer/components/+workspaces/workspaces.tsx:62 msgid "Are you sure you want remove workspace <0>{0}?" msgstr "" @@ -294,7 +294,7 @@ msgstr "Выполнить команду drain для ноды <0>{nodeName}{from} до <1>{to}" msgid "Fs Group" msgstr "" -#: src/renderer/components/+landing-page/landing-page.tsx:23 +#: src/renderer/components/+landing-page/landing-page.tsx:37 msgid "Get started by associating one or more clusters to Lens." msgstr "" @@ -1023,7 +1032,7 @@ msgstr "Группы" msgid "HPA" msgstr "HPA" -#: src/renderer/components/+preferences/preferences.tsx:147 +#: src/renderer/components/+preferences/preferences.tsx:137 msgid "HTTP Proxy" msgstr "" @@ -1031,7 +1040,7 @@ msgstr "" #~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API." #~ msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:132 +#: src/renderer/components/+preferences/preferences.tsx:145 msgid "Helm" msgstr "" @@ -1051,7 +1060,7 @@ msgstr "Helm установка: {repo}/{name}" msgid "Helm Upgrade: {0}" msgstr "Helm обновление: {0}" -#: src/renderer/components/+preferences/preferences.tsx:47 +#: src/renderer/components/+preferences/preferences.tsx:51 msgid "Helm branch <0>{0} already in use" msgstr "" @@ -1158,11 +1167,11 @@ msgstr "Установка завершена!" msgid "Installing..." msgstr "Установка.." -#: src/renderer/components/input/input.validators.ts:44 +#: src/renderer/components/input/input.validators.ts:50 msgid "Invalid account ID" msgstr "Неверный ID аккаунта" -#: src/renderer/components/input/input.validators.ts:15 +#: src/renderer/components/input/input.validators.ts:16 msgid "Invalid number" msgstr "Неверный номер" @@ -1198,7 +1207,6 @@ msgstr "" #: src/renderer/components/+workloads/workloads.tsx:69 #: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62 #: src/renderer/components/+workloads-jobs/jobs.tsx:36 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62 msgid "Jobs" msgstr "Jobs" @@ -1242,6 +1250,10 @@ msgstr "Файл конфигурации" msgid "Kubeconfig File" msgstr "Файл конфигурации" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +msgid "Kubectl Binary" +msgstr "" + #: src/renderer/components/+nodes/node-details.tsx:98 msgid "Kubelet version" msgstr "Версия Kubelet" @@ -1358,7 +1370,7 @@ msgstr "Макс. подов" msgid "Max Unavailable" msgstr "" -#: src/renderer/components/input/input.validators.ts:35 +#: src/renderer/components/input/input.validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "Максимальная длина {maxLength}" @@ -1434,7 +1446,7 @@ msgstr "Мин. подов" msgid "Minimize" msgstr "Минимизировать" -#: src/renderer/components/input/input.validators.ts:30 +#: src/renderer/components/input/input.validators.ts:36 msgid "Minimum length is {minLength}" msgstr "Минимальная длина {minLength}" @@ -1498,7 +1510,7 @@ msgstr "Установки" #: src/renderer/components/+workloads-pods/pods.tsx:74 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 -#: src/renderer/components/+workspaces/workspaces.tsx:117 +#: src/renderer/components/+workspaces/workspaces.tsx:130 #: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" @@ -1566,7 +1578,7 @@ msgstr "Namespaces" msgid "Namespaces: {0}" msgstr "Namespaces: {0}" -#: src/renderer/components/+preferences/preferences.tsx:157 +#: src/renderer/components/+preferences/preferences.tsx:164 msgid "Needed with some corporate proxies that do certificate re-writing." msgstr "" @@ -1627,7 +1639,7 @@ msgstr "Нет доступных нод." #~ msgid "No contexts available or they already added" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#: src/renderer/components/+add-cluster/add-cluster.tsx:260 msgid "No contexts available or they have been added already" msgstr "" @@ -1743,7 +1755,7 @@ msgid "Organization" msgstr "Организация" #: src/renderer/components/+workloads/workloads.tsx:29 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35 +#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45 msgid "Overview" msgstr "Обзор" @@ -1759,7 +1771,7 @@ msgstr "Параллелизм" msgid "Parameters" msgstr "Параметры" -#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#: src/renderer/components/+add-cluster/add-cluster.tsx:230 msgid "Paste as text" msgstr "" @@ -1849,7 +1861,6 @@ msgstr "Командная строка пода" #: src/renderer/components/+workloads/workloads.tsx:37 #: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47 #: src/renderer/components/+workloads-deployments/deployments.tsx:60 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:89 #: src/renderer/components/+workloads-pods/pods.tsx:73 #: src/renderer/components/+workloads-replicasets/replicasets.tsx:52 @@ -1900,7 +1911,7 @@ msgstr "" #~ msgid "Pro-Tip: paste kubeconfig to collect available contexts" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:263 +#: src/renderer/components/+add-cluster/add-cluster.tsx:248 msgid "Pro-Tip: paste kubeconfig to get available contexts" msgstr "" @@ -1908,7 +1919,7 @@ msgstr "" #~ msgid "Pro-Tip: paste kubeconfig to parse available contexts" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:254 +#: src/renderer/components/+add-cluster/add-cluster.tsx:239 msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" msgstr "" @@ -1925,11 +1936,11 @@ msgstr "" msgid "Provisioner" msgstr "Комиссия" -#: src/renderer/components/+preferences/preferences.tsx:150 +#: src/renderer/components/+preferences/preferences.tsx:140 msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:308 +#: src/renderer/components/+add-cluster/add-cluster.tsx:293 msgid "Proxy settings" msgstr "" @@ -2009,10 +2020,10 @@ msgstr "Установка: {0}" msgid "Releases" msgstr "Релизы" -#: src/renderer/components/+preferences/preferences.tsx:139 +#: src/renderer/components/+preferences/preferences.tsx:152 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:74 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:80 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:76 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:82 #: 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:85 @@ -2023,7 +2034,7 @@ msgstr "Удалить" msgid "Remove <0>{releaseNames}?" msgstr "Удалить <0>{releaseNames}?" -#: src/renderer/components/+workspaces/workspaces.tsx:51 +#: src/renderer/components/+workspaces/workspaces.tsx:52 msgid "Remove Workspace" msgstr "" @@ -2051,7 +2062,7 @@ msgstr "Удалить выбранные элементы ({0})" msgid "Remove {resourceKind} <0>{resourceName}?" msgstr "Удалить {resourceKind} <0>{resourceName}?" -#: src/renderer/components/+preferences/preferences.tsx:114 +#: src/renderer/components/+preferences/preferences.tsx:122 msgid "Removing helm branch <0>{0} has failed: {1}" msgstr "" @@ -2075,7 +2086,7 @@ msgstr "Реплики" msgid "Repo/Name" msgstr "Репозиторий/Имя" -#: src/renderer/components/+preferences/preferences.tsx:133 +#: src/renderer/components/+preferences/preferences.tsx:146 msgid "Repositories" msgstr "" @@ -2110,7 +2121,7 @@ msgstr "" msgid "Required field" msgstr "Обязательное поле" -#: src/renderer/components/+add-cluster/add-cluster.tsx:250 +#: src/renderer/components/+add-cluster/add-cluster.tsx:235 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "Сбросить" @@ -2253,7 +2264,7 @@ msgstr "" #: src/renderer/components/+config-maps/config-map-details.tsx:78 #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 -#: src/renderer/components/+workspaces/workspaces.tsx:120 +#: src/renderer/components/+workspaces/workspaces.tsx:132 #: src/renderer/components/dock/edit-resource.tsx:88 msgid "Save" msgstr "Сохранить" @@ -2342,13 +2353,13 @@ msgstr "Выберите квоту..." #~ msgid "Select context(s)" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:272 -#~ msgid "Select contexts" -#~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:257 +msgid "Select contexts" +msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts (available: {0})" -msgstr "" +#~ msgid "Select contexts (available: {0})" +#~ msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 #: src/renderer/components/+add-cluster/add-cluster.tsx:76 @@ -2372,7 +2383,7 @@ msgstr "" #~ msgid "Select kubeconfig" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 msgid "Select kubeconfig file" msgstr "" @@ -2400,7 +2411,7 @@ msgstr "Выбрать сервисные аккаунты" #~ msgid "Selected contexts ({0}): <0>{1}" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +#: src/renderer/components/+add-cluster/add-cluster.tsx:256 msgid "Selected contexts: <0>{0}" msgstr "" @@ -2504,7 +2515,6 @@ msgid "Stateful Sets" msgstr "" #: src/renderer/components/+workloads/workloads.tsx:61 -#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52 msgid "StatefulSets" msgstr "StatefulSets" @@ -2606,11 +2616,11 @@ msgstr "TLS" msgid "Taints" msgstr "Метки блокировки" -#: src/renderer/components/+preferences/preferences.tsx:161 +#: src/renderer/components/+preferences/preferences.tsx:168 msgid "Telemetry & Usage Tracking" msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:164 +#: src/renderer/components/+preferences/preferences.tsx:171 msgid "Telemetry & usage data is collected to continuously improve the Lens experience." msgstr "" @@ -2630,15 +2640,19 @@ msgstr "Для контейнера нет логов." msgid "There are no logs available." msgstr "Логи отсутствуют." -#: src/renderer/components/input/input.validators.ts:5 +#: src/renderer/components/input/input.validators.ts:6 msgid "This field is required" msgstr "Это обязательное поле" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 +#: src/renderer/components/input/input.validators.ts:31 +msgid "This field must be a valid path" +msgstr "" + +#: src/renderer/components/+landing-page/landing-page.tsx:25 msgid "This is the quick launch menu." msgstr "" -#: src/renderer/components/+preferences/preferences.tsx:156 +#: src/renderer/components/+preferences/preferences.tsx:163 msgid "This will make Lens to trust ANY certificate authority without any validations." msgstr "" @@ -2662,13 +2676,13 @@ msgstr "Толерантности" msgid "Transmit" msgstr "Транзит" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79 #: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80 msgid "Trigger" msgstr "" -#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103 +#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102 msgid "Trigger CronJob <0>{cronjobName}" msgstr "" @@ -2691,7 +2705,7 @@ msgstr "" msgid "Type" msgstr "Тип" -#: src/renderer/components/+preferences/preferences.tsx:148 +#: src/renderer/components/+preferences/preferences.tsx:138 msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)" msgstr "" @@ -2828,11 +2842,11 @@ msgstr "Ожидание запуска сервисов" msgid "Warnings: {0}" msgstr "Предупреждения: {0}" -#: src/renderer/components/+landing-page/landing-page.tsx:20 +#: src/renderer/components/+landing-page/landing-page.tsx:34 msgid "Welcome!" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:79 +#: src/renderer/components/+workspaces/workspaces.tsx:88 msgid "What is a Workspace?" msgstr "" @@ -2845,19 +2859,19 @@ msgid "Workloads" msgstr "Ресурсы" #: src/renderer/components/+workspaces/workspace-menu.tsx:39 -#: src/renderer/components/+workspaces/workspaces.tsx:91 +#: src/renderer/components/+workspaces/workspaces.tsx:100 msgid "Workspaces" msgstr "" -#: src/renderer/components/+workspaces/workspaces.tsx:81 +#: src/renderer/components/+workspaces/workspaces.tsx:90 msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "" -#: src/renderer/components/input/input.validators.ts:10 +#: src/renderer/components/input/input.validators.ts:11 msgid "Wrong email format" msgstr "Неверный формат электронной почты" -#: src/renderer/components/input/input.validators.ts:25 +#: src/renderer/components/input/input.validators.ts:26 msgid "Wrong url format" msgstr "Неверный url формат" @@ -2916,7 +2930,7 @@ msgstr "" msgid "never" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:133 msgid "new" msgstr "" diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts index 3e9acdd036..dd59aad8ff 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts @@ -11,11 +11,9 @@ export class DaemonSetStore extends KubeObjectStore { @observable metrics: IPodMetrics = null; - loadMetrics(daemonSet: DaemonSet) { + async loadMetrics(daemonSet: DaemonSet) { const pods = this.getChildPods(daemonSet); - return podsApi.getMetrics(pods, daemonSet.getNs(), "").then(metrics => - this.metrics = metrics - ); + this.metrics = await podsApi.getMetrics(pods, daemonSet.getNs(), ""); } getChildPods(daemonSet: DaemonSet): Pod[] { diff --git a/src/renderer/components/+workloads-deployments/deployments.store.ts b/src/renderer/components/+workloads-deployments/deployments.store.ts index 59acd6c100..07fc095629 100644 --- a/src/renderer/components/+workloads-deployments/deployments.store.ts +++ b/src/renderer/components/+workloads-deployments/deployments.store.ts @@ -16,11 +16,9 @@ export class DeploymentStore extends KubeObjectStore { ], "desc"); } - loadMetrics(deployment: Deployment) { + async loadMetrics(deployment: Deployment) { const pods = this.getChildPods(deployment); - return podsApi.getMetrics(pods, deployment.getNs(), "").then(metrics => - this.metrics = metrics - ); + this.metrics = await podsApi.getMetrics(pods, deployment.getNs(), ""); } getStatuses(deployments?: Deployment[]) { diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index be770cd39a..ca6ad0d9ba 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -5,72 +5,54 @@ import { observer } from "mobx-react"; import { Trans } from "@lingui/macro"; import { OverviewWorkloadStatus } from "./overview-workload-status"; import { Link } from "react-router-dom"; -import { cronJobsURL, daemonSetsURL, deploymentsURL, jobsURL, podsURL, statefulSetsURL } from "../+workloads"; -import { podsStore } from "../+workloads-pods/pods.store"; -import { deploymentStore } from "../+workloads-deployments/deployments.store"; -import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store"; -import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store"; -import { jobStore } from "../+workloads-jobs/job.store"; -import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; +import { workloadURL, workloadStores } from "../+workloads"; import { namespaceStore } from "../+namespaces/namespace.store"; import { PageFiltersList } from "../item-object-list/page-filters-list"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select"; -import { isAllowedResource } from "../../../common/rbac"; +import { isAllowedResource, KubeResource } from "../../../common/rbac"; +import { ResourceNames } from "../../../renderer/utils/rbac"; +import { autobind } from "../../utils"; +import { _i18n } from "../../i18n"; + +const resources: KubeResource[] = [ + "pods", + "deployments", + "statefulsets", + "daemonsets", + "jobs", + "cronjobs", +] @observer export class OverviewStatuses extends React.Component { + @autobind() + renderWorkload(resource: KubeResource): React.ReactElement { + const store = workloadStores[resource]; + const items = store.getAllByNs(namespaceStore.contextNs) + return ( +
+
+ {ResourceNames[resource]} ({items.length}) +
+ +
+ ) + } + render() { - const { contextNs } = namespaceStore; - const pods = isAllowedResource("pods") ? podsStore.getAllByNs(contextNs) : []; - const deployments = isAllowedResource("deployments") ? deploymentStore.getAllByNs(contextNs) : []; - const statefulSets = isAllowedResource("statefulsets") ? statefulSetStore.getAllByNs(contextNs) : []; - const daemonSets = isAllowedResource("daemonsets") ? daemonSetStore.getAllByNs(contextNs) : []; - const jobs = isAllowedResource("jobs") ? jobStore.getAllByNs(contextNs) : []; - const cronJobs = isAllowedResource("cronjobs") ? cronJobStore.getAllByNs(contextNs) : []; + const workloads = resources + .filter(isAllowedResource) + .map(this.renderWorkload); + return (
Overview
- +
- +
- {isAllowedResource("pods") && -
-
Pods ({pods.length})
- -
- } - {isAllowedResource("deployments") && -
-
Deployments ({deployments.length})
- -
- } - {isAllowedResource("statefulsets") && -
-
StatefulSets ({statefulSets.length})
- -
- } - {isAllowedResource("daemonsets") && -
-
DaemonSets ({daemonSets.length})
- -
- } - {isAllowedResource("jobs") && -
-
Jobs ({jobs.length})
- -
- } - {isAllowedResource("cronjobs") && -
-
CronJobs ({cronJobs.length})
- -
- } + {workloads}
) diff --git a/src/renderer/components/+workloads-replicasets/replicasets.store.ts b/src/renderer/components/+workloads-replicasets/replicasets.store.ts index cf658e36f7..3b91999699 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.store.ts +++ b/src/renderer/components/+workloads-replicasets/replicasets.store.ts @@ -10,11 +10,9 @@ export class ReplicaSetStore extends KubeObjectStore { api = replicaSetApi @observable metrics: IPodMetrics = null; - loadMetrics(replicaSet: ReplicaSet) { + async loadMetrics(replicaSet: ReplicaSet) { const pods = this.getChildPods(replicaSet); - return podsApi.getMetrics(pods, replicaSet.getNs(), "").then(metrics => - this.metrics = metrics - ); + this.metrics = await podsApi.getMetrics(pods, replicaSet.getNs(), ""); } getChildPods(replicaSet: ReplicaSet) { diff --git a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts index 5119ce6e00..5bca0a19b6 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts +++ b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts @@ -10,11 +10,9 @@ export class StatefulSetStore extends KubeObjectStore { api = statefulSetApi @observable metrics: IPodMetrics = null; - loadMetrics(statefulSet: StatefulSet) { + async loadMetrics(statefulSet: StatefulSet) { const pods = this.getChildPods(statefulSet); - return podsApi.getMetrics(pods, statefulSet.getNs(), "").then(metrics => - this.metrics = metrics - ); + this.metrics = await podsApi.getMetrics(pods, statefulSet.getNs(), ""); } getChildPods(statefulSet: StatefulSet) { diff --git a/src/renderer/components/+workloads/index.ts b/src/renderer/components/+workloads/index.ts index 300747c6af..1f2ae11149 100644 --- a/src/renderer/components/+workloads/index.ts +++ b/src/renderer/components/+workloads/index.ts @@ -1,3 +1,3 @@ export * from "./workloads.route" export * from "./workloads" - +export * from "./workloads.stores" diff --git a/src/renderer/components/+workloads/workloads.route.ts b/src/renderer/components/+workloads/workloads.route.ts index 94a7afd6ba..5586102faf 100644 --- a/src/renderer/components/+workloads/workloads.route.ts +++ b/src/renderer/components/+workloads/workloads.route.ts @@ -1,6 +1,7 @@ import { RouteProps } from "react-router" import { Workloads } from "./workloads"; import { buildURL, IURLParams } from "../../navigation"; +import { KubeResource } from "../../../common/rbac"; export const workloadsRoute: RouteProps = { get path() { @@ -62,3 +63,12 @@ export const daemonSetsURL = buildURL(daemonSetsRoute.pa export const statefulSetsURL = buildURL(statefulSetsRoute.path) export const jobsURL = buildURL(jobsRoute.path) export const cronJobsURL = buildURL(cronJobsRoute.path) + +export const workloadURL: Partial>> = { + "pods": podsURL, + "deployments": deploymentsURL, + "daemonsets": daemonSetsURL, + "statefulsets": statefulSetsURL, + "jobs": jobsURL, + "cronjobs": cronJobsURL, +} diff --git a/src/renderer/components/+workloads/workloads.stores.ts b/src/renderer/components/+workloads/workloads.stores.ts new file mode 100644 index 0000000000..998075140d --- /dev/null +++ b/src/renderer/components/+workloads/workloads.stores.ts @@ -0,0 +1,17 @@ +import { KubeObjectStore } from "../../kube-object.store"; +import { podsStore } from "../+workloads-pods/pods.store"; +import { deploymentStore } from "../+workloads-deployments/deployments.store"; +import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store"; +import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store"; +import { jobStore } from "../+workloads-jobs/job.store"; +import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; +import { KubeResource } from "../../../common/rbac"; + +export const workloadStores: Partial> = { + "pods": podsStore, + "deployments": deploymentStore, + "daemonsets": daemonSetStore, + "statefulsets": statefulSetStore, + "jobs": jobStore, + "cronjobs": cronJobStore, +} diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 0f647ced6d..16c5f0e9d0 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -19,6 +19,8 @@ export abstract class KubeObjectStore extends ItemSt kubeWatchApi.addListener(this, this.onWatchApiEvent); } + getStatuses?(items: T[]): Record; + getAllByNs(namespace: string | string[], strict = false): T[] { const namespaces: string[] = [].concat(namespace); if (namespaces.length) { diff --git a/src/renderer/utils/rbac.ts b/src/renderer/utils/rbac.ts new file mode 100644 index 0000000000..9f4ba97943 --- /dev/null +++ b/src/renderer/utils/rbac.ts @@ -0,0 +1,28 @@ +import { KubeResource } from "../../common/rbac"; +import { _i18n } from "../i18n"; + +export const ResourceNames: Record = { + "namespaces": _i18n._("Namespaces"), + "nodes": _i18n._("Nodes"), + "events": _i18n._("Events"), + "resourcequotas": _i18n._("Resource Quotas"), + "services": _i18n._("Services"), + "secrets": _i18n._("Secrets"), + "configmaps": _i18n._("Config Maps"), + "ingresses": _i18n._("Ingresses"), + "networkpolicies": _i18n._("Network Policies"), + "persistentvolumes": _i18n._("Persistent Volumes"), + "storageclasses": _i18n._("Storage Classes"), + "pods": _i18n._("Pods"), + "daemonsets": _i18n._("Daemon Sets"), + "deployments": _i18n._("Deployments"), + "statefulsets": _i18n._("Stateful Sets"), + "replicasets": _i18n._("Replica Sets"), + "jobs": _i18n._("Jobs"), + "cronjobs": _i18n._("Cron Jobs"), + "endpoints": _i18n._("Endpoints"), + "customresourcedefinitions": _i18n._("Custom Resource Definitions"), + "horizontalpodautoscalers": _i18n._("Horizontal Pod Autoscalers"), + "podsecuritypolicies": _i18n._("Pod Security Policies"), + "poddisruptionbudgets": _i18n._("Pod Disruption Budgets"), +} From bec288d22e9ff4bd325b4bd467c34684cf67a373 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 30 Sep 2020 11:33:49 +0300 Subject: [PATCH 010/102] fix extension-api module alias on production (#1001) Signed-off-by: Jari Kolehmainen --- src/common/vars.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/vars.ts b/src/common/vars.ts index c17c54e118..0ee9e05eb4 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -26,9 +26,6 @@ export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss"); export const extensionsLibName = `${appName}-extensions.api` export const extensionsDir = path.join(contextDir, "src/extensions"); -// Special dynamic module aliases -addAlias("@lens/extensions", path.resolve(buildDir, `${extensionsLibName}.js`)); // fixme: provide path in prod - // Special runtime paths defineGlobal("__static", { get() { @@ -39,6 +36,13 @@ defineGlobal("__static", { } }) +// Special dynamic module aliases +if (isProduction && process.resourcesPath) { + addAlias("@lens/extensions", path.join(process.resourcesPath, "static", `build/${extensionsLibName}.js`)) +} else { + addAlias("@lens/extensions", path.join(contextDir, "static", `build/${extensionsLibName}.js`)) +} + // Apis export const apiPrefix = "/api" // local router apis export const apiKubePrefix = "/api-kube" // k8s cluster apis From a92a6ed0b038e1bc879339598e1b3e2b4fdd2570 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 30 Sep 2020 11:34:14 +0300 Subject: [PATCH 011/102] Expose react to extensions (#1000) Signed-off-by: Jari Kolehmainen --- extensions/example-extension/.gitignore | 1 + extensions/example-extension/example-extension.tsx | 3 +-- src/extensions/extension-api.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 extensions/example-extension/.gitignore diff --git a/extensions/example-extension/.gitignore b/extensions/example-extension/.gitignore new file mode 100644 index 0000000000..a6c7c2852d --- /dev/null +++ b/extensions/example-extension/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/extensions/example-extension/example-extension.tsx b/extensions/example-extension/example-extension.tsx index 1967ce0d60..3b08af5d01 100644 --- a/extensions/example-extension/example-extension.tsx +++ b/extensions/example-extension/example-extension.tsx @@ -1,5 +1,4 @@ -import { Button, DynamicPageType, Icon, IconProps, LensExtension } from "@lens/extensions"; -import React from "react"; +import { Button, DynamicPageType, Icon, IconProps, LensExtension, React } from "@lens/extensions"; import path from "path"; export default class ExampleExtension extends LensExtension { diff --git a/src/extensions/extension-api.ts b/src/extensions/extension-api.ts index 5b9fce7d02..7f364898dd 100644 --- a/src/extensions/extension-api.ts +++ b/src/extensions/extension-api.ts @@ -6,6 +6,7 @@ export * from "./lens-extension" export { DynamicPageType } from "./register-page"; // TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps) +export { default as React } from "react" export * from "../renderer/components/icon" export * from "../renderer/components/tooltip" export * from "../renderer/components/button" From 62afdc7415d61af031d177b0a94594968c5abe41 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 30 Sep 2020 11:34:35 +0300 Subject: [PATCH 012/102] Cleanup production build (#1002) Signed-off-by: Jari Kolehmainen --- package.json | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 20b5740e58..0a8204576a 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,9 @@ ] }, "build": { + "files": [ + "static/build/main.js" + ], "afterSign": "build/notarize.js", "extraResources": [ { @@ -174,20 +177,6 @@ "@hapi/call": "^8.0.0", "@hapi/subtext": "^7.0.3", "@kubernetes/client-node": "^0.12.0", - "@types/crypto-js": "^3.1.47", - "@types/electron-window-state": "^2.0.34", - "@types/fs-extra": "^9.0.1", - "@types/http-proxy": "^1.17.4", - "@types/js-yaml": "^3.12.4", - "@types/jsonpath": "^0.2.0", - "@types/lodash": "^4.14.155", - "@types/marked": "^0.7.4", - "@types/mock-fs": "^4.10.0", - "@types/module-alias": "^2.0.0", - "@types/node": "^12.12.45", - "@types/proper-lockfile": "^4.1.1", - "@types/react-beautiful-dnd": "^13.0.0", - "@types/tar": "^4.0.3", "array-move": "^3.0.0", "chalk": "^4.1.0", "conf": "^7.0.1", @@ -215,8 +204,6 @@ "openid-client": "^3.15.2", "path-to-regexp": "^6.1.0", "proper-lockfile": "^4.1.1", - "react-beautiful-dnd": "^13.0.0", - "react-router": "^5.2.0", "request": "^2.88.2", "request-promise-native": "^1.0.8", "semver": "^7.3.2", @@ -248,6 +235,20 @@ "@lingui/react": "^3.0.0-13", "@material-ui/core": "^4.10.1", "@rollup/plugin-json": "^4.1.0", + "@types/crypto-js": "^3.1.47", + "@types/electron-window-state": "^2.0.34", + "@types/fs-extra": "^9.0.1", + "@types/http-proxy": "^1.17.4", + "@types/js-yaml": "^3.12.4", + "@types/jsonpath": "^0.2.0", + "@types/lodash": "^4.14.155", + "@types/marked": "^0.7.4", + "@types/mock-fs": "^4.10.0", + "@types/module-alias": "^2.0.0", + "@types/node": "^12.12.45", + "@types/proper-lockfile": "^4.1.1", + "@types/react-beautiful-dnd": "^13.0.0", + "@types/tar": "^4.0.3", "@types/chart.js": "^2.9.21", "@types/circular-dependency-plugin": "^5.0.1", "@types/color": "^3.0.1", @@ -318,8 +319,10 @@ "progress-bar-webpack-plugin": "^2.1.0", "raw-loader": "^4.0.1", "react": "^16.13.1", + "react-beautiful-dnd": "^13.0.0", "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", + "react-router": "^5.2.0", "react-select": "^3.1.0", "react-window": "^1.8.5", "rollup": "^2.28.2", From c049918d25543759872d00314c315f0722c4a28b Mon Sep 17 00:00:00 2001 From: Lauri Nevala Date: Wed, 30 Sep 2020 14:02:18 +0300 Subject: [PATCH 013/102] Add support for Docker Enterprise Container Cloud metrics (#998) * Add support for Docker Enterprise Container Cloud metrics Signed-off-by: Lauri Nevala --- src/common/prometheus-providers.ts | 3 +- src/main/prometheus/stacklight.ts | 83 ++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/main/prometheus/stacklight.ts diff --git a/src/common/prometheus-providers.ts b/src/common/prometheus-providers.ts index 87f081b94e..2c6e6fac8e 100644 --- a/src/common/prometheus-providers.ts +++ b/src/common/prometheus-providers.ts @@ -1,9 +1,10 @@ import { PrometheusLens } from "../main/prometheus/lens"; import { PrometheusHelm } from "../main/prometheus/helm"; import { PrometheusOperator } from "../main/prometheus/operator"; +import { PrometheusStacklight } from "../main/prometheus/stacklight"; import { PrometheusProviderRegistry } from "../main/prometheus/provider-registry"; -[PrometheusLens, PrometheusHelm, PrometheusOperator].forEach(providerClass => { +[PrometheusLens, PrometheusHelm, PrometheusOperator, PrometheusStacklight].forEach(providerClass => { const provider = new providerClass() PrometheusProviderRegistry.registerProvider(provider.id, provider) }); diff --git a/src/main/prometheus/stacklight.ts b/src/main/prometheus/stacklight.ts new file mode 100644 index 0000000000..4cb946c81d --- /dev/null +++ b/src/main/prometheus/stacklight.ts @@ -0,0 +1,83 @@ +import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry"; +import { CoreV1Api } from "@kubernetes/client-node"; +import logger from "../logger" + +export class PrometheusStacklight implements PrometheusProvider { + id = "stacklight" + name = "Stacklight" + rateAccuracy = "1m" + + public async getPrometheusService(client: CoreV1Api): Promise { + try { + const resp = await client.readNamespacedService("prometheus-server", "stacklight") + const service = resp.body + return { + id: this.id, + namespace: service.metadata.namespace, + service: service.metadata.name, + port: service.spec.ports[0].port + } + } catch(error) { + logger.warn(`PrometheusLens: failed to list services: ${error.response.body.message}`) + } + } + + public getQueries(opts: PrometheusQueryOpts): PrometheusQuery { + switch(opts.category) { + case 'cluster': + return { + memoryUsage: ` + sum( + node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) + ) by (kubernetes_name) + `.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`), + memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`, + memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`, + memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`, + cpuUsage: `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`, + cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`, + cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`, + cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`, + podUsage: `sum(kubelet_running_pod_count{instance=~"${opts.nodes}"})`, + podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`, + fsSize: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`, + fsUsage: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)` + } + case 'nodes': + return { + memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`, + memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`, + cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`, + cpuCapacity: `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`, + fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`, + fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)` + } + case 'pods': + return { + cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, + cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, + cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, + memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`, + memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`, + memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`, + fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`, + networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, + networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})` + } + case 'pvc': + return { + diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`, + diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)` + } + case 'ingress': + const bytesSent = (ingress: string, statuses: string) => + `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)` + return { + bytesSentSuccess: bytesSent(opts.igress, "^2\\\\d*"), + bytesSentFailure: bytesSent(opts.ingres, "^5\\\\d*"), + requestDurationSeconds: `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`, + responseDurationSeconds: `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)` + } + } + } +} From 8823c6f5f288125d59cb5f4636480e3e979d4f7a Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 30 Sep 2020 14:08:42 +0300 Subject: [PATCH 014/102] Terminal: set NO_PROXY env for localhost communication (#982) * terminal: set NO_PROXY env for localhost communication Signed-off-by: Jari Kolehmainen Co-authored-by: Lauri Nevala --- src/main/kubectl.ts | 11 +++++++++++ src/main/shell-session.ts | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index 1e57e29d5d..672d93cf65 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -283,6 +283,12 @@ export class Kubectl { bashScript += "fi\n" bashScript += `export PATH="${helmPath}:${kubectlPath}:$PATH"\n` bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n" + + bashScript += "NO_PROXY=\",${NO_PROXY:-localhost},\"\n" + bashScript += "NO_PROXY=\"${NO_PROXY//,localhost,/,}\"\n" + bashScript += "NO_PROXY=\"${NO_PROXY//,127.0.0.1,/,}\"\n" + bashScript += "NO_PROXY=\"localhost,127.0.0.1${NO_PROXY%,}\"\n" + bashScript += "export NO_PROXY\n" bashScript += "unset tempkubeconfig\n" await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 }) @@ -308,6 +314,11 @@ export class Kubectl { zshScript += "d=${d/#:/}\n" zshScript += "export PATH=\"$helmpath:$kubectlpath:${d/%:/}\"\n" zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n" + zshScript += "NO_PROXY=\",${NO_PROXY:-localhost},\"\n" + zshScript += "NO_PROXY=\"${NO_PROXY//,localhost,/,}\"\n" + zshScript += "NO_PROXY=\"${NO_PROXY//,127.0.0.1,/,}\"\n" + zshScript += "NO_PROXY=\"localhost,127.0.0.1${NO_PROXY%,}\"\n" + zshScript += "export NO_PROXY\n" zshScript += "unset tempkubeconfig\n" zshScript += "unset OLD_ZDOTDIR\n" await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 }) diff --git a/src/main/shell-session.ts b/src/main/shell-session.ts index 1d9d722f57..962074c803 100644 --- a/src/main/shell-session.ts +++ b/src/main/shell-session.ts @@ -125,6 +125,8 @@ export class ShellSession extends EventEmitter { if (this.preferences.httpsProxy) { env["HTTPS_PROXY"] = this.preferences.httpsProxy } + const no_proxy = ["localhost", "127.0.0.1", env["NO_PROXY"]] + env["NO_PROXY"] = no_proxy.filter(address => !!address).join() if (env.DEBUG) { // do not pass debug option to bash delete env["DEBUG"] } From f596b3c45ce43727ef01f840157b80b24121683a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=A0tiller?= Date: Wed, 30 Sep 2020 13:56:22 +0200 Subject: [PATCH 015/102] Fix CPU/Memory usage metrics when using prometheus operator. (#632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cluster CPU and Memory usage cannot be sum by node as it is rendered as single cluster metrics. Without this changes, the cluster graphs (standard and pie) shows data only from first (or latest) nodes. For pod specific metrics, when we use prometheus operator the `container_` metics are doubled. Restric search to get those have image tag. Signed-off-by: Jakub Štiller Co-authored-by: Jakub Štiller --- src/main/prometheus/operator.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/prometheus/operator.ts b/src/main/prometheus/operator.ts index 37efa64b98..3d335ff554 100644 --- a/src/main/prometheus/operator.ts +++ b/src/main/prometheus/operator.ts @@ -37,19 +37,19 @@ export class PrometheusOperator implements PrometheusProvider { memoryUsage: ` sum( node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) - ) by (node) + ) `.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`), - memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`, - memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`, - memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`, - cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`, - cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`, - cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`, - cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`, + memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"})`, + memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"})`, + memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"})`, + cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`, + cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"})`, + cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"})`, + cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"})`, podUsage: `sum(kubelet_running_pod_count{node=~"${opts.nodes}"})`, - podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`, - fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`, - fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)` + podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"})`, + fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`, + fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})` } case 'nodes': return { @@ -62,10 +62,10 @@ export class PrometheusOperator implements PrometheusProvider { } case 'pods': return { - cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, + cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`, cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`, - memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`, + memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`, memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`, memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`, fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`, From 3d0b843891b490661a937ecfbc95941856ba564a Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 30 Sep 2020 16:54:21 +0300 Subject: [PATCH 016/102] Preventing drawer close on secret's click (#1003) Signed-off-by: Alex Andreev --- .../components/+workloads-pods/pod-container-env.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/+workloads-pods/pod-container-env.tsx b/src/renderer/components/+workloads-pods/pod-container-env.tsx index 7da7d7e03d..fb779a9eaa 100644 --- a/src/renderer/components/+workloads-pods/pod-container-env.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-env.tsx @@ -113,7 +113,8 @@ const SecretKey = (props: SecretKeyProps) => { const [loading, setLoading] = useState(false) const [secret, setSecret] = useState() - const showKey = async () => { + const showKey = async (evt: React.MouseEvent) => { + evt.preventDefault() setLoading(true) const secret = await secretsStore.load({ name, namespace }); setLoading(false) @@ -126,7 +127,7 @@ const SecretKey = (props: SecretKeyProps) => { return ( <> - secretKeyRef({name}.{key})  + secretKeyRef({name}.{key})  Date: Wed, 30 Sep 2020 21:54:12 +0300 Subject: [PATCH 017/102] Release v3.6.5 (#1004) * Release v3.6.5 Signed-off-by: Lauri Nevala --- package.json | 2 +- static/RELEASE_NOTES.md | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 53488e932d..059ffc63ef 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "3.6.5-rc.1", + "version": "3.6.5", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index e3f2af3cfb..11e3ef4aa7 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -2,7 +2,13 @@ Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights! -## 3.6.5-rc.1 (current version) +## 3.6.5 (current version) +- Prevent drawer close when revealing secret value +- Fix app crash when CRD conditions were not present +- Add support for Stacklight prometheus metrics +- Terminal: set NO_PROXY env for localhost communication +- Fix CPU/Memory usage metrics when using prometheus operator +- Display last-applied-configuration annotation - Fix Notifications not to block items not visually under them from being interacted with - Fix side bar not to scroll after clicking on lower menu item - Auto-select context if only one context is present in pasted Kubeconfig From 135282da91ce52a56011f10aeec6efa0a2af57d1 Mon Sep 17 00:00:00 2001 From: Lauri Nevala Date: Thu, 1 Oct 2020 11:16:20 +0300 Subject: [PATCH 018/102] Fix cluster dashboard opening and state refreshing (#1006) * Fix cluster dashboard opening and state refreshing Signed-off-by: Lauri Nevala --- src/common/cluster-ipc.ts | 11 +++++++++++ src/main/cluster.ts | 3 ++- src/renderer/bootstrap.tsx | 2 +- src/renderer/components/+cluster/cluster.tsx | 5 +++-- src/renderer/components/app.tsx | 4 ++-- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index e11a232ea3..6ab46c1489 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -14,6 +14,17 @@ export const clusterIpc = { }, }), + setFrameId: createIpcChannel({ + channel: "cluster:set-frame-id", + handle: (clusterId: ClusterId, frameId?: number) => { + const cluster = clusterStore.getById(clusterId); + if (cluster) { + if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates + return cluster.pushState(); + } + }, + }), + refresh: createIpcChannel({ channel: "cluster:refresh", handle: (clusterId: ClusterId) => { diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 12acb793ab..3e3ba3f6b5 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -59,6 +59,7 @@ export class Cluster implements ClusterModel { @observable online = false; @observable accessible = false; @observable ready = false; + @observable reconnecting = false; @observable disconnected = true; @observable failureReason: string; @observable nodes = 0; @@ -110,7 +111,7 @@ export class Cluster implements ClusterModel { protected bindEvents() { logger.info(`[CLUSTER]: bind events`, this.getMeta()); - const refreshTimer = setInterval(() => this.online && this.refresh(), 30000); // every 30s + const refreshTimer = setInterval(() => !this.disconnected && this.refresh(), 30000); // every 30s this.eventDisposers.push( reaction(this.getState, this.pushState), diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 37fdc35e03..f03df719b6 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -11,7 +11,7 @@ import { App } from "./components/app"; import { LensApp } from "./lens-app"; type AppComponent = React.ComponentType & { - init?(): void; + init?(): Promise; } export async function bootstrap(App: AppComponent) { diff --git a/src/renderer/components/+cluster/cluster.tsx b/src/renderer/components/+cluster/cluster.tsx index 9ef032335b..9a1ce47123 100644 --- a/src/renderer/components/+cluster/cluster.tsx +++ b/src/renderer/components/+cluster/cluster.tsx @@ -14,14 +14,15 @@ import { podsStore } from "../+workloads-pods/pods.store"; import { clusterStore } from "./cluster.store"; import { eventStore } from "../+events/event.store"; import { isAllowedResource } from "../../../common/rbac"; +import { getHostedCluster } from "../../../common/cluster-store"; @observer export class Cluster extends React.Component { private dependentStores = [nodesStore, podsStore]; private watchers = [ - interval(60, () => clusterStore.getMetrics()), - interval(20, () => eventStore.loadAll()) + interval(60, () => { getHostedCluster().available && clusterStore.getMetrics()}), + interval(20, () => { getHostedCluster().available && eventStore.loadAll()}) ]; @computed get isLoaded() { diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 053d8fa3d6..aee156ff31 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -44,8 +44,8 @@ export class App extends React.Component { const clusterId = getHostedClusterId(); logger.info(`[APP]: Init dashboard, clusterId=${clusterId}, frameId=${frameId}`) await Terminal.preloadFonts() - await clusterIpc.activate.invokeFromRenderer(clusterId, frameId); - await getHostedCluster().whenReady; // cluster.refresh() is done at this point + await clusterIpc.setFrameId.invokeFromRenderer(clusterId, frameId); + await getHostedCluster().whenReady; // cluster.activate() is done at this point } get startURL() { From 9ecfeb316c4ea59681eaea5549e84fc64a886a5a Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 1 Oct 2020 11:37:27 -0400 Subject: [PATCH 019/102] cleanup Draggable into ResizingAnchor (#989) * Rename `Draggable` as `ResizingAnchor` since that is what it is used for and shouldn't be mistaken as a general draggable item * Refactor `ResizingAnchor` to be much more smart about how it handles mouse movements. Allow it to know which direction the resizing should be in allowing it to produce exact resized values. * Add event handlers for the min and max extents so that actions can be triggered when those extents are passed (in either direction) * Add double click support Signed-off-by: Sebastian Malton --- src/renderer/components/dock/dock.scss | 23 +- src/renderer/components/dock/dock.store.ts | 25 +- src/renderer/components/dock/dock.tsx | 61 ++-- src/renderer/components/dock/terminal.ts | 7 +- .../components/draggable/draggable.scss | 5 - .../components/draggable/draggable.tsx | 119 -------- src/renderer/components/draggable/index.ts | 1 - .../components/resizing-anchor/index.ts | 1 + .../resizing-anchor/resizing-anchor.scss | 46 +++ .../resizing-anchor/resizing-anchor.tsx | 281 ++++++++++++++++++ yarn.lock | 16 +- 11 files changed, 376 insertions(+), 209 deletions(-) delete mode 100644 src/renderer/components/draggable/draggable.scss delete mode 100644 src/renderer/components/draggable/draggable.tsx delete mode 100644 src/renderer/components/draggable/index.ts create mode 100644 src/renderer/components/resizing-anchor/index.ts create mode 100644 src/renderer/components/resizing-anchor/resizing-anchor.scss create mode 100644 src/renderer/components/resizing-anchor/resizing-anchor.tsx diff --git a/src/renderer/components/dock/dock.scss b/src/renderer/components/dock/dock.scss index 6b2d90925d..8c7ab7e07d 100644 --- a/src/renderer/components/dock/dock.scss +++ b/src/renderer/components/dock/dock.scss @@ -14,10 +14,6 @@ left: 0; bottom: 0; z-index: 100; - - > .resizer { - display: none; - } } } @@ -65,24 +61,7 @@ } } - .resizer { - $height: 12px; - - position: absolute; - top: -$height / 2; - left: 0; - right: 0; - bottom: 100%; - height: $height; - cursor: row-resize; - z-index: 10; - - &.disabled { - pointer-events: none; - } - } - .AceEditor { border: none; } -} \ No newline at end of file +} diff --git a/src/renderer/components/dock/dock.store.ts b/src/renderer/components/dock/dock.store.ts index c1b70cc925..2fed511e75 100644 --- a/src/renderer/components/dock/dock.store.ts +++ b/src/renderer/components/dock/dock.store.ts @@ -27,8 +27,8 @@ export class DockStore { ]; protected storage = createStorage("dock", {}); // keep settings in localStorage - public defaultTabId = this.initialTabs[0].id; - public minHeight = 100; + public readonly defaultTabId = this.initialTabs[0].id; + public readonly minHeight = 100; @observable isOpen = false; @observable fullSize = false; @@ -45,11 +45,12 @@ export class DockStore { } get maxHeight() { - const mainLayoutHeader = 40; - const mainLayoutTabs = 33; - const mainLayoutMargin = 16; - const dockTabs = 33; - return window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs; + const mainLayoutHeader = 40 + const mainLayoutTabs = 33 + const mainLayoutMargin = 16 + const dockTabs = 33 + const preferedMax = window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs + return Math.max(preferedMax, this.minHeight) // don't let max < min } constructor() { @@ -65,7 +66,6 @@ export class DockStore { }); // adjust terminal height if window size changes - this.checkMaxHeight(); window.addEventListener("resize", throttle(this.checkMaxHeight, 250)); } @@ -171,20 +171,19 @@ export class DockStore { @action selectTab(tabId: TabId) { - const tab = this.getTabById(tabId); - this.selectedTabId = tab ? tab.id : null; + this.selectedTabId = this.getTabById(tabId)?.id ?? null; } @action - setHeight(height: number) { - this.height = Math.max(0, Math.min(height, this.maxHeight)); + setHeight(height?: number) { + this.height = Math.max(this.minHeight, Math.min(height || this.minHeight, this.maxHeight)); } @action reset() { this.selectedTabId = this.defaultTabId; this.tabs.replace(this.initialTabs); - this.height = this.defaultHeight; + this.setHeight(this.defaultHeight); this.close(); } } diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index d384c36608..c020d20672 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -4,7 +4,7 @@ import React, { Fragment } from "react"; import { observer } from "mobx-react"; import { Trans } from "@lingui/macro"; import { autobind, cssNames, prevDefault } from "../../utils"; -import { Draggable, DraggableState } from "../draggable"; +import { ResizingAnchor, ResizeDirection } from "../resizing-anchor"; import { Icon } from "../icon"; import { Tabs } from "../tabs/tabs"; import { MenuItem } from "../menu"; @@ -29,28 +29,8 @@ interface Props { @observer export class Dock extends React.Component { - onResizeStart = () => { - const { isOpen, open, setHeight, minHeight } = dockStore; - if (!isOpen) { - open(); - setHeight(minHeight); - } - } - - onResize = ({ offsetY }: DraggableState) => { - const { isOpen, close, height, setHeight, minHeight, defaultHeight } = dockStore; - const newHeight = height + offsetY; - if (height > newHeight && newHeight < minHeight) { - setHeight(defaultHeight); - close(); - } - else if (isOpen) { - setHeight(newHeight); - } - } - onKeydown = (evt: React.KeyboardEvent) => { - const { close, closeTab, selectedTab, fullSize, toggleFillSize } = dockStore; + const { close, closeTab, selectedTab } = dockStore; if (!selectedTab) return; const { code, ctrlKey, shiftKey } = evt.nativeEvent; if (shiftKey && code === "Escape") { @@ -71,13 +51,13 @@ export class Dock extends React.Component { @autobind() renderTab(tab: IDockTab) { if (isTerminalTab(tab)) { - return + return } if (isCreateResourceTab(tab) || isEditResourceTab(tab)) { - return + return } if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) { - return }/> + return } /> } } @@ -86,11 +66,11 @@ export class Dock extends React.Component { if (!isOpen || !tab) return; return (
- {isCreateResourceTab(tab) && } - {isEditResourceTab(tab) && } - {isInstallChartTab(tab) && } - {isUpgradeChartTab(tab) && } - {isTerminalTab(tab) && } + {isCreateResourceTab(tab) && } + {isEditResourceTab(tab) && } + {isInstallChartTab(tab) && } + {isUpgradeChartTab(tab) && } + {isTerminalTab(tab) && }
) } @@ -104,11 +84,16 @@ export class Dock extends React.Component { onKeyDown={this.onKeydown} tabIndex={-1} > - dockStore.height} + minExtent={dockStore.minHeight} + maxExtent={dockStore.maxHeight} + direction={ResizeDirection.VERTICAL} + onStart={dockStore.open} + onMinExtentSubceed={dockStore.close} + onMinExtentExceed={dockStore.open} + onDrag={dockStore.setHeight} />
{
New tab }} closeOnScroll={false}> createTerminalTab()}> - + Terminal session createResourceTab()}> - + Create resource @@ -133,7 +118,7 @@ export class Dock extends React.Component { {hasTabs() && ( <> Exit full size mode : Fit to window} onClick={toggleFillSize} /> diff --git a/src/renderer/components/dock/terminal.ts b/src/renderer/components/dock/terminal.ts index f02fd478f2..cea1e91292 100644 --- a/src/renderer/components/dock/terminal.ts +++ b/src/renderer/components/dock/terminal.ts @@ -121,6 +121,8 @@ export class Terminal { } fit = () => { + // Since this function is debounced we need to read this value as late as possible + if (!this.isActive) return; this.fitAddon.fit(); const { cols, rows } = this.xterm; this.api.sendTerminalSize(cols, rows); @@ -150,7 +152,6 @@ export class Terminal { } onResize = () => { - if (!this.isActive) return; this.fitLazy(); this.focus(); } @@ -176,8 +177,8 @@ export class Terminal { if (this.xterm.hasSelection()) return false; break; - // Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim - // https://github.com/kontena/lens-app/issues/156#issuecomment-534906480 + // Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim + // https://github.com/kontena/lens-app/issues/156#issuecomment-534906480 case "KeyW": evt.preventDefault(); break; diff --git a/src/renderer/components/draggable/draggable.scss b/src/renderer/components/draggable/draggable.scss deleted file mode 100644 index e99e306ce2..0000000000 --- a/src/renderer/components/draggable/draggable.scss +++ /dev/null @@ -1,5 +0,0 @@ -body.dragging { - user-select: none; - -moz-user-select: none; - -webkit-user-select: none; -} \ No newline at end of file diff --git a/src/renderer/components/draggable/draggable.tsx b/src/renderer/components/draggable/draggable.tsx deleted file mode 100644 index 12fd020929..0000000000 --- a/src/renderer/components/draggable/draggable.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import "./draggable.scss"; -import React from "react"; -import { cssNames, IClassName, noop } from "../../utils"; -import throttle from "lodash/throttle"; - -export interface DraggableEventHandler { - (state: DraggableState): void; -} - -interface Props { - className?: IClassName; - vertical?: boolean; - horizontal?: boolean; - onStart?: DraggableEventHandler; - onEnter?: DraggableEventHandler; - onEnd?: DraggableEventHandler; -} - -export interface DraggableState { - inited?: boolean; - changed?: boolean; - initX?: number; - initY?: number; - pageX?: number; - pageY?: number; - offsetX?: number; - offsetY?: number; -} - -const initState: DraggableState = { - inited: false, - changed: false, - offsetX: 0, - offsetY: 0, -}; - -export class Draggable extends React.PureComponent { - public state = initState; - - static IS_DRAGGING = "dragging" - - static defaultProps: Props = { - vertical: true, - horizontal: true, - onStart: noop, - onEnter: noop, - onEnd: noop, - }; - - constructor(props: Props) { - super(props); - document.addEventListener("mousemove", this.onDrag); - document.addEventListener("mouseup", this.onDragEnd); - } - - componentWillUnmount() { - document.removeEventListener("mousemove", this.onDrag); - document.removeEventListener("mouseup", this.onDragEnd); - } - - onDragInit = (evt: React.MouseEvent) => { - document.body.classList.add(Draggable.IS_DRAGGING); - const { pageX, pageY } = evt; - this.setState({ - inited: true, - initX: pageX, - initY: pageY, - pageX: pageX, - pageY: pageY, - }) - } - - onDrag = throttle((evt: MouseEvent) => { - const { vertical, horizontal, onEnter, onStart } = this.props; - const { inited, pageX, pageY } = this.state; - const offsetX = pageX - evt.pageX; - const offsetY = pageY - evt.pageY; - let changed = false; - if (horizontal && offsetX !== 0) changed = true; - if (vertical && offsetY !== 0) changed = true; - if (inited && changed) { - const start = !this.state.changed; - const state = Object.assign({}, this.state, { - changed: true, - pageX: evt.pageX, - pageY: evt.pageY, - offsetX: offsetX, - offsetY: offsetY, - }); - if (start) onStart(state); - this.setState(state, () => onEnter(state)); - } - }, 100) - - onDragEnd = (evt: MouseEvent) => { - const { pageX, pageY } = evt; - const { inited, changed, initX, initY } = this.state; - if (inited) { - document.body.classList.remove(Draggable.IS_DRAGGING); - this.setState(initState, () => { - if (!changed) return; - const state = Object.assign({}, this.state, { - offsetX: initX - pageX, - offsetY: initY - pageY, - }); - this.props.onEnd(state); - }); - } - } - - render() { - const { className, children } = this.props; - return ( -
- {children} -
- ); - } -} \ No newline at end of file diff --git a/src/renderer/components/draggable/index.ts b/src/renderer/components/draggable/index.ts deleted file mode 100644 index 0a3b92d235..0000000000 --- a/src/renderer/components/draggable/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './draggable' \ No newline at end of file diff --git a/src/renderer/components/resizing-anchor/index.ts b/src/renderer/components/resizing-anchor/index.ts new file mode 100644 index 0000000000..6197be8d84 --- /dev/null +++ b/src/renderer/components/resizing-anchor/index.ts @@ -0,0 +1 @@ +export * from './resizing-anchor' diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.scss b/src/renderer/components/resizing-anchor/resizing-anchor.scss new file mode 100644 index 0000000000..310727af2c --- /dev/null +++ b/src/renderer/components/resizing-anchor/resizing-anchor.scss @@ -0,0 +1,46 @@ +body.resizing { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; +} + +.ResizingAnchor { + $dimension: 12px; + + position: absolute; + z-index: 10; + + &.disabled { + display: none; + } + + &.vertical { + left: 0; + right: 0; + cursor: row-resize; + height: $dimension; + + &.leading { + top: -$dimension / 2; + } + + &.trailing { + bottom: -$dimension / 2; + } + } + + &.horizontal { + top: 0; + bottom: 0; + cursor: col-resize; + width: $dimension; + + &.leading { + left: -$dimension / 2; + } + + &.trailing { + right: -$dimension / 2; + } + } +} diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.tsx b/src/renderer/components/resizing-anchor/resizing-anchor.tsx new file mode 100644 index 0000000000..a5a8ba2086 --- /dev/null +++ b/src/renderer/components/resizing-anchor/resizing-anchor.tsx @@ -0,0 +1,281 @@ +import "./resizing-anchor.scss"; +import React from "react"; +import { action, observable } from "mobx"; +import _ from "lodash" +import { findDOMNode } from "react-dom"; +import { cssNames, noop } from "../../utils"; + +export enum ResizeDirection { + HORIZONTAL = "horizontal", + VERTICAL = "vertical", +} + +/** + * ResizeSide is for customizing where the area should be rendered. + * That location is determined in conjunction with the `ResizeDirection` using the following table: + * + * +----------+------------+----------+ + * | | HORIZONTAL | VERTICAL | + * +----------+------------+----------+ + * | LEADING | left | top | + * +----------+------------+----------+ + * | TRAILING | right | bottom | + * +----------+------------+----------+ + */ +export enum ResizeSide { + LEADING = "leading", + TRAILING = "trailing", +} + +/** + * ResizeGrowthDirection determines how the anchor interprets the drag. + * + * Because the origin of the screen is top left a drag from bottom to top + * results in a negative directional delta. However, if the component being + * dragged grows in the opposite direction, this needs to be compensated for. + */ +export enum ResizeGrowthDirection { + TOP_TO_BOTTOM = 1, + BOTTOM_TO_TOP = -1, + LEFT_TO_RIGHT = 1, + RIGHT_TO_LEFT = -1, +} + +interface Props { + direction: ResizeDirection; + + /** + * getCurrentExtent should return the current prominent dimension in the + * given resizing direction. Width for HORIZONTAL and height for VERTICAL + */ + getCurrentExtent: () => number; + + disabled?: boolean; + placement?: ResizeSide; + growthDirection?: ResizeGrowthDirection; + + // Ability to restrict which mouse buttons are allowed to resize this component + // Reference: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons + onlyButtons?: number; + + // onStart is called when the ResizeAnchor is first clicked (mouse down) + onStart?: () => void; + + // onEnd is called when the ResizeAnchor is released (mouse up) + onEnd?: () => void; + + /** + * onDrag is called whenever there is a mousemove event. All calls will be + * bounded by matching `onStart` and `onEnd` calls. + */ + onDrag?: (newExtent: number) => void; + + // onDoubleClick is called when the the ResizeAnchor is double clicked + onDoubleClick?: () => void; + + /** + * The following two extents represent the max and min values set to `onDrag` + */ + maxExtent?: number; + minExtent?: number; + + /** + * The following events are triggerred with respect to the above values. + * - The "__Exceed" call will be made when the unbounded extent goes from + * < the above to >= the above + * - The "__Subceed" call is similar but is triggered when the unbounded + * extent goes from >= the above to < the above. + */ + onMaxExtentExceed?: () => void; + onMaxExtentSubceed?: () => void; + onMinExtentSubceed?: () => void; + onMinExtentExceed?: () => void; +} + +interface Position { + readonly pageX: number; + readonly pageY: number; +} + +/** + * Return the direction delta, but ignore drags leading up to a moved item + * 1. `->|` => return `false` + * 2. `<-|` => return `directed length (M, P2)` (negative) + * 3. `-|>` => return `directed length (M, P2)` (positive) + * 4. `<|-` => return `directed length (M, P2)` (negative) + * 5. `|->` => return `directed length (M, P2)` (positive) + * 6. `|<-` => return `false` + * @param P1 the starting position on the number line + * @param P2 the ending position on the number line + * @param M a third point that determines if the delta is meaningful + * @returns the directional difference between including appropriate sign. + */ +function directionDelta(P1: number, P2: number, M: number): number | false { + const delta = Math.abs(M - P2) + + if (P1 < M) { + if (P2 >= M) { + // case 3 + return delta + } + + if (P2 < P1) { + // case 2 + return -delta + } + + // case 1 + return false + } + + if (P2 < M) { + // case 4 + return -delta + } + + if (P1 < P2) { + // case 5 + return delta + } + + // case 6 + return false +} + +export class ResizingAnchor extends React.PureComponent { + @observable lastMouseEvent?: MouseEvent + @observable.ref ref?: React.RefObject; + + static defaultProps = { + onStart: noop, + onDrag: noop, + onEnd: noop, + onMaxExtentExceed: noop, + onMinExtentExceed: noop, + onMinExtentSubceed: noop, + onMaxExtentSubceed: noop, + onDoubleClick: noop, + disabled: false, + growthDirection: ResizeGrowthDirection.BOTTOM_TO_TOP, + maxExtent: Number.POSITIVE_INFINITY, + minExtent: 0, + placement: ResizeSide.LEADING, + } + static IS_RESIZING = "resizing" + + constructor(props: Props) { + super(props) + if (props.maxExtent < props.minExtent) { + throw new Error("maxExtent must be >= minExtent") + } + + this.ref = React.createRef() + } + + componentWillUnmount() { + document.removeEventListener("mousemove", this.onDrag) + document.removeEventListener("mouseup", this.onDragEnd) + } + + @action + onDragInit = (event: React.MouseEvent) => { + const { onStart, onlyButtons } = this.props + + if (typeof onlyButtons === "number" && onlyButtons !== event.buttons) { + return + } + + document.addEventListener("mousemove", this.onDrag) + document.addEventListener("mouseup", this.onDragEnd) + document.body.classList.add(ResizingAnchor.IS_RESIZING) + + this.lastMouseEvent = undefined + onStart() + } + + calculateDelta(from: Position, to: Position): number | false { + const node = this.ref.current + if (!node) { + return false + } + + const boundingBox = node.getBoundingClientRect() + + if (this.props.direction === ResizeDirection.HORIZONTAL) { + const barX = Math.round(boundingBox.x + (boundingBox.width / 2)) + return directionDelta(from.pageX, to.pageX, barX) + } else { // direction === ResizeDirection.VERTICAL + const barY = Math.round(boundingBox.y + (boundingBox.height / 2)) + return directionDelta(from.pageY, to.pageY, barY) + } + } + + onDrag = _.throttle((event: MouseEvent) => { + /** + * Some notes to help understand the following: + * - A browser's origin point is in the top left of the screen + * - X increases going from left to right + * - Y increases going from top to bottom + * - Since the resize bar should always be a rectangle, use its centre + * line (in the resizing direction) as the line for determining if + * the bar has "jumped around" + * + * Desire: + * - Always ignore movement in the non-resizing direction + * - Figure out how much the user has "dragged" the resize bar + * - If the resize bar has jumped around, compensate by ignoring movement + * in the resizing direction if it is moving "towards" the resize bar's + * new location. + */ + + if (!this.lastMouseEvent) { + this.lastMouseEvent = event + return + } + + const { maxExtent, minExtent, getCurrentExtent, growthDirection } = this.props + const { onDrag, onMaxExtentExceed, onMinExtentSubceed, onMaxExtentSubceed, onMinExtentExceed } = this.props + const delta = this.calculateDelta(this.lastMouseEvent, event) + + // always update the last mouse event + this.lastMouseEvent = event + + if (delta === false) { + return + } + + const previousExtent = getCurrentExtent() + const unboundedExtent = previousExtent + (delta * growthDirection) + const boundedExtent = Math.round(Math.max(minExtent, Math.min(maxExtent, unboundedExtent))) + onDrag(boundedExtent) + + if (previousExtent <= minExtent && minExtent <= unboundedExtent) { + onMinExtentExceed() + } else if (previousExtent >= minExtent && minExtent >= unboundedExtent) { + onMinExtentSubceed() + } + if (previousExtent <= maxExtent && maxExtent <= unboundedExtent) { + onMaxExtentExceed() + } else if (previousExtent >= maxExtent && maxExtent >= unboundedExtent) { + onMaxExtentSubceed() + } + }, 100) + + @action + onDragEnd = (_event: MouseEvent) => { + this.props.onEnd() + document.removeEventListener("mousemove", this.onDrag) + document.removeEventListener("mouseup", this.onDragEnd) + document.body.classList.remove(ResizingAnchor.IS_RESIZING) + } + + render() { + const { disabled, direction, placement, onDoubleClick } = this.props + return
+ } +} diff --git a/yarn.lock b/yarn.lock index 300efc58ce..f64e36b703 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8181,17 +8181,17 @@ mobx-observable-history@^1.0.3: history "^4.10.1" mobx "^5.15.4" -mobx-react-lite@2: - version "2.0.7" - resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.0.7.tgz#1bfb3b4272668e288047cf0c7940b14e91cba284" - integrity sha512-YKAh2gThC6WooPnVZCoC+rV1bODAKFwkhxikzgH18wpBjkgTkkR9Sb0IesQAH5QrAEH/JQVmy47jcpQkf2Au3Q== +mobx-react-lite@>=2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.2.2.tgz#87c217dc72b4e47b22493daf155daf3759f868a6" + integrity sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg== mobx-react@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.2.2.tgz#45e8e7c4894cac8399bba0a91060d7cfb8ea084b" - integrity sha512-Us6V4ng/iKIRJ8pWxdbdysC6bnS53ZKLKlVGBqzHx6J+gYPYbOotWvhHZnzh/W5mhpYXxlXif4kL2cxoWJOplQ== + version "6.3.0" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.3.0.tgz#7d11799f988bbdadc49e725081993b18baa20329" + integrity sha512-C14yya2nqEBRSEiJjPkhoWJLlV8pcCX3m2JRV7w1KivwANJqipoiPx9UMH4pm6QNMbqDdvJqoyl+LqNu9AhvEQ== dependencies: - mobx-react-lite "2" + mobx-react-lite ">=2.2.0" mobx@^5.15.4: version "5.15.4" From af71834676842ee807096afa3769d2fe27ebe436 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 2 Oct 2020 09:32:20 +0300 Subject: [PATCH 020/102] Moving dock info panel to the top (#1007) * Moving EditorPanel to the top Signed-off-by: Alex Andreev * Preventing save with empty yaml Signed-off-by: Alex Andreev * Make tooltips in @withTooltip narrower Signed-off-by: Alex Andreev * Not showing api errors in icon Signed-off-by: Alex Andreev * Success notification for create resource operation Signed-off-by: Alex Andreev * Button outlined style Signed-off-by: Alex Andreev * Making second button to stay outlined Signed-off-by: Alex Andreev * Fine-tuning dock info panel colors Signed-off-by: Alex Andreev * Removing border-radius from ace-editor Signed-off-by: Alex Andreev --- .../components/ace-editor/ace-editor.scss | 2 - src/renderer/components/button/button.scss | 11 +++++ src/renderer/components/button/button.tsx | 5 ++- .../components/dock/create-resource.tsx | 15 ++++--- .../components/dock/edit-resource.tsx | 10 ++--- src/renderer/components/dock/info-panel.scss | 29 +----------- src/renderer/components/dock/info-panel.tsx | 44 ++++++------------- .../components/dock/install-chart.tsx | 10 ++--- .../components/dock/upgrade-chart.tsx | 12 ++--- .../components/tooltip/withTooltip.tsx | 1 + src/renderer/themes/kontena-dark.json | 4 +- 11 files changed, 58 insertions(+), 85 deletions(-) diff --git a/src/renderer/components/ace-editor/ace-editor.scss b/src/renderer/components/ace-editor/ace-editor.scss index 26b74d1b64..7aabbf01d5 100644 --- a/src/renderer/components/ace-editor/ace-editor.scss +++ b/src/renderer/components/ace-editor/ace-editor.scss @@ -7,7 +7,6 @@ .theme-light & { border: 1px solid gainsboro; - border-radius: $radius; .ace_scrollbar { @include custom-scrollbar(dark); @@ -19,7 +18,6 @@ width: inherit; height: inherit; font-size: 90%; - border-radius: $radius; } // --Theme customization diff --git a/src/renderer/components/button/button.scss b/src/renderer/components/button/button.scss index e85647ca9f..f586a03c5a 100644 --- a/src/renderer/components/button/button.scss +++ b/src/renderer/components/button/button.scss @@ -41,6 +41,17 @@ } } + &.outlined { + color: inherit; + background: transparent; + + &.active, + &:focus { + color: inherit; + box-shadow: 0 0 0 1px inset; + } + } + &.big { font-size: 2.2 * $unit; border-radius: 50px; diff --git a/src/renderer/components/button/button.tsx b/src/renderer/components/button/button.tsx index c0cb8dcc49..65aa3a979e 100644 --- a/src/renderer/components/button/button.tsx +++ b/src/renderer/components/button/button.tsx @@ -9,6 +9,7 @@ export interface ButtonProps extends ButtonHTMLAttributes, TooltipDecorator primary?: boolean; accent?: boolean; plain?: boolean; + outlined?: boolean; hidden?: boolean; active?: boolean; big?: boolean; @@ -23,12 +24,12 @@ export class Button extends React.PureComponent { private button: HTMLButtonElement; render() { - const { className, waiting, label, primary, accent, plain, hidden, active, big, round, tooltip, children, ...props } = this.props; + const { className, waiting, label, primary, accent, plain, hidden, active, big, round, outlined, tooltip, children, ...props } = this.props; const btnProps = props as Partial; if (hidden) return null; btnProps.className = cssNames('Button', className, { - waiting, primary, accent, plain, active, big, round, + waiting, primary, accent, plain, active, big, round, outlined }); const btnContent: ReactNode = ( diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 6d89455b3b..e665c831a0 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -39,6 +39,7 @@ export class CreateResource extends React.Component { create = async () => { if (this.error) return; + if (!this.data.trim()) return; // do not save when field is empty const resources = jsYaml.safeLoadAll(this.data) .filter(v => !!v) // skip empty documents if "---" pasted at the beginning or end const createdResources: string[] = []; @@ -54,12 +55,14 @@ export class CreateResource extends React.Component { errors.forEach(Notifications.error); if (!createdResources.length) throw errors[0]; } - return ( + const successMessage = (

{" "} {createdResources.join(", ")} successfully created

) + Notifications.ok(successMessage) + return successMessage } render() { @@ -67,11 +70,6 @@ export class CreateResource extends React.Component { const { className } = this.props; return (
- { submitLabel={_i18n._(t`Create`)} showNotifications={false} /> +
) } diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource.tsx index 8168af28bc..6d9aa961c2 100644 --- a/src/renderer/components/dock/edit-resource.tsx +++ b/src/renderer/components/dock/edit-resource.tsx @@ -90,11 +90,6 @@ export class EditResource extends React.Component { const { kind, getNs, getName } = resource; return (
- {
)} /> +
) } diff --git a/src/renderer/components/dock/info-panel.scss b/src/renderer/components/dock/info-panel.scss index 3bc3f94f82..23dcc52243 100644 --- a/src/renderer/components/dock/info-panel.scss +++ b/src/renderer/components/dock/info-panel.scss @@ -2,7 +2,7 @@ @include hidden-scrollbar; background: $dockInfoBackground; - border-top: 1px solid $dockInfoBorderColor; + border-bottom: 1px solid $dockInfoBorderColor; padding: $padding $padding * 2; flex-shrink: 0; @@ -12,37 +12,12 @@ > .controls { white-space: nowrap; - - &:empty { - display: none; - } + flex: 1 1; &:not(:empty) + .info { - border: 1px solid $borderColor; - border-top: 0; - border-bottom: 0; min-height: 25px; padding-left: $padding; padding-right: $padding; } } - - > .info { - @include hidden-scrollbar; - - min-width: 40px; // min-space for icon - flex: 1 1; - white-space: nowrap; - text-overflow: ellipsis; - - > div { - padding-right: $padding; - flex-shrink: 0; - } - - .Icon { - margin: 0; - margin-right: $padding; - } - } } \ No newline at end of file diff --git a/src/renderer/components/dock/info-panel.tsx b/src/renderer/components/dock/info-panel.tsx index 39d998d9d4..122fea6d9f 100644 --- a/src/renderer/components/dock/info-panel.tsx +++ b/src/renderer/components/dock/info-panel.tsx @@ -38,35 +38,29 @@ export class InfoPanel extends Component { showNotifications: true, } - @observable.ref result: ReactNode; @observable error = ""; @observable waiting = false; componentDidMount() { disposeOnUnmount(this, [ reaction(() => this.props.tabId, () => { - this.result = "" - this.error = "" this.waiting = false }) ]) } @computed get errorInfo() { - return this.error || this.props.error; + return this.props.error; } submit = async () => { const { showNotifications } = this.props; - this.result = ""; - this.error = ""; this.waiting = true; try { - this.result = await this.props.submit() - if (showNotifications) Notifications.ok(this.result); + const result = await this.props.submit(); + if (showNotifications) Notifications.ok(result); } catch (error) { - this.error = error.toString(); - if (showNotifications) Notifications.error(this.error); + if (showNotifications) Notifications.error(error.toString()); } finally { this.waiting = false } @@ -81,27 +75,15 @@ export class InfoPanel extends Component { dockStore.closeTab(this.props.tabId); } - renderInfo() { - if (!this.props.showInlineInfo) { + renderErrorIcon() { + if (!this.props.showInlineInfo || !this.errorInfo) { return; } - const { result, errorInfo } = this; return ( - <> - {result && ( -
- - {result} -
- )} - {errorInfo && ( -
- - {errorInfo} -
- )} - - ) +
+ +
+ ); } render() { @@ -114,11 +96,13 @@ export class InfoPanel extends Component { {controls}
- {waiting ? <> {submittingMessage} : this.renderInfo()} + {waiting ? <> {submittingMessage} : this.renderErrorIcon()}
) return (
- { submittingMessage={_i18n._(t`Updating..`)} controls={controlsAndInfo} /> +
) } diff --git a/src/renderer/components/tooltip/withTooltip.tsx b/src/renderer/components/tooltip/withTooltip.tsx index f8c3e3ea93..5861e5af82 100644 --- a/src/renderer/components/tooltip/withTooltip.tsx +++ b/src/renderer/components/tooltip/withTooltip.tsx @@ -28,6 +28,7 @@ export function withTooltip>(Target: T): T { const tooltipProps: TooltipProps = { targetId: tooltipId, tooltipOnParentHover: tooltipOverrideDisabled, + formatters: { narrow: true }, ...(isReactNode(tooltip) ? { children: tooltip } : tooltip), }; targetProps.id = tooltipId; diff --git a/src/renderer/themes/kontena-dark.json b/src/renderer/themes/kontena-dark.json index a0d0eb9869..1071024841 100644 --- a/src/renderer/themes/kontena-dark.json +++ b/src/renderer/themes/kontena-dark.json @@ -58,8 +58,8 @@ "colorVague": "#36393e", "colorTerminated": "#4c5053", "dockHeadBackground": "#2e3136", - "dockInfoBackground": "#111111", - "dockInfoBorderColor": "#4c5053", + "dockInfoBackground": "#1e2125", + "dockInfoBorderColor": "#303136", "terminalBackground": "#000000", "terminalForeground": "#ffffff", "terminalCursor": "#ffffff", From 68317453c1f3e45bc8db507251a67a38288f9533 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 2 Oct 2020 12:49:03 +0300 Subject: [PATCH 021/102] Adding space after drawer badges (#1014) Signed-off-by: Alex Andreev --- src/renderer/components/badge/badge.tsx | 7 ------- src/renderer/components/drawer/drawer-item.scss | 5 +++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/badge/badge.tsx b/src/renderer/components/badge/badge.tsx index eca3c94063..3a70c98286 100644 --- a/src/renderer/components/badge/badge.tsx +++ b/src/renderer/components/badge/badge.tsx @@ -18,13 +18,6 @@ export class Badge extends React.Component { {label} {children} - { /** - * This is a zero-width-space. It makes there be a word seperation - * between adjacent Badge's because 's are ignored for browers' - * word detection algorithmns use for determining the extent of the - * text to highlight on multi-click sequences. - */} - ​ } } diff --git a/src/renderer/components/drawer/drawer-item.scss b/src/renderer/components/drawer/drawer-item.scss index b7e69fc1ae..f7727414a3 100644 --- a/src/renderer/components/drawer/drawer-item.scss +++ b/src/renderer/components/drawer/drawer-item.scss @@ -48,6 +48,11 @@ float: left; margin: $spacing; + &:after { + content: " "; + display: block; + } + &.disabled { opacity: 0.5; } From 14d3d88278c6c328d9579c133d5642e4d44b6b79 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 2 Oct 2020 14:33:29 +0300 Subject: [PATCH 022/102] Fix: check for location bevore navigate (#1016) * Check for location before navigate Signed-off-by: Alex Andreev * Handling every location type Signed-off-by: Alex Andreev --- src/renderer/navigation.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/renderer/navigation.ts b/src/renderer/navigation.ts index ce831feac4..f7d5b6e435 100644 --- a/src/renderer/navigation.ts +++ b/src/renderer/navigation.ts @@ -18,7 +18,11 @@ if (ipcRenderer) { } export function navigate(location: LocationDescriptor) { + const currentLocation = navigation.getPath(); navigation.push(location); + if (currentLocation === navigation.getPath()) { + navigation.goBack(); // prevent sequences of same url in history + } } export interface IURLParams

{ From 87061cee9e067744a7bf78e1cf4c9deb4d610a04 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 2 Oct 2020 10:05:59 -0400 Subject: [PATCH 023/102] Refactor testing harness to use defaults (#808) * Refactor testing harness to use defaults - Move tests into defaultly named test folders - Use default test suffix of ".test" instead of "_test" - Make cluster-store tests unit tests by adding more nesting, so that order of tests doesn't matter Signed-off-by: Sebastian Malton Co-authored-by: Sebastian Malton --- .../app_spec.ts => __tests__/app.tests.ts} | 0 integration/helpers/utils.ts | 19 +- locales/en/messages.po | 116 +++++----- locales/fi/messages.po | 114 +++++----- locales/ru/messages.po | 114 +++++----- package.json | 1 - .../cluster-store.test.ts} | 201 +++++++++--------- .../user-store.test.ts} | 2 +- .../workspace-store.test.ts} | 2 +- .../splitArray.test.ts} | 4 +- .../kube-api-parse.test.ts} | 15 +- .../components/cluster-name-setting.tsx | 4 +- .../components/cluster-proxy-setting.tsx | 4 +- .../add-quota-dialog.tsx | 10 +- .../+config-secrets/add-secret-dialog.tsx | 8 +- .../+namespaces/add-namespace-dialog.tsx | 2 +- .../+preferences/kubectl-binaries.tsx | 2 +- .../create-service-account-dialog.tsx | 6 +- .../cronjob-trigger-dialog.tsx | 2 +- .../components/+workspaces/workspaces.tsx | 2 +- .../input_validators.test.ts} | 2 +- src/renderer/components/input/input.tsx | 8 +- ...nput.validators.ts => input_validators.ts} | 0 .../formatDuration.test.ts} | 12 +- .../metricUnitsToNumber.test.ts} | 2 +- 25 files changed, 345 insertions(+), 307 deletions(-) rename integration/{specs/app_spec.ts => __tests__/app.tests.ts} (100%) rename src/common/{cluster-store_test.ts => __tests__/cluster-store.test.ts} (66%) rename src/common/{user-store_test.ts => __tests__/user-store.test.ts} (98%) rename src/common/{workspace-store_test.ts => __tests__/workspace-store.test.ts} (98%) rename src/common/utils/{splitArray_test.ts => __tests__/splitArray.test.ts} (95%) rename src/renderer/api/{kube-api-parse_test.ts => __tests__/kube-api-parse.test.ts} (89%) rename src/renderer/components/input/{input.validators_test.ts => __tests__/input_validators.test.ts} (97%) rename src/renderer/components/input/{input.validators.ts => input_validators.ts} (100%) rename src/renderer/utils/{formatDuration_spec.ts => __tests__/formatDuration.test.ts} (95%) rename src/renderer/utils/{metricUnitsToNumber_test.ts => __tests__/metricUnitsToNumber.test.ts} (85%) diff --git a/integration/specs/app_spec.ts b/integration/__tests__/app.tests.ts similarity index 100% rename from integration/specs/app_spec.ts rename to integration/__tests__/app.tests.ts diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index 72d6e3d732..a50b13e023 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -1,23 +1,16 @@ import { Application } from "spectron"; -let appPath = "" -switch(process.platform) { -case "win32": - appPath = "./dist/win-unpacked/Lens.exe" - break -case "linux": - appPath = "./dist/linux-unpacked/kontena-lens" - break -case "darwin": - appPath = "./dist/mac/Lens.app/Contents/MacOS/Lens" - break +const AppPaths: Partial> = { + "win32": "./dist/win-unpacked/Lens.exe", + "linux": "./dist/linux-unpacked/kontena-lens", + "darwin": "./dist/mac/Lens.app/Contents/MacOS/Lens", } export function setup(): Application { return new Application({ // path to electron app args: [], - path: appPath, + path: AppPaths[process.platform], startTimeout: 30000, waitTimeout: 30000, chromeDriverArgs: ['remote-debugging-port=9222'], @@ -32,7 +25,7 @@ export async function tearDown(app: Application) { await app.stop() try { process.kill(pid, 0); - } catch(e) { + } catch (e) { return } } diff --git a/locales/en/messages.po b/locales/en/messages.po index 092161d0cf..86f99a841f 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -49,7 +49,7 @@ msgstr "<0>Filtered: {itemsCount} / {allItemsCount}" #~ msgid "<0>Your browser does not support all Lens features. Please consider using another browser." #~ msgstr "<0>Your browser does not support all Lens features. Please consider using another browser." -#: src/renderer/components/dock/create-resource.tsx:56 +#: src/renderer/components/dock/create-resource.tsx:58 msgid "<0>{0} successfully created" msgstr "<0>{0} successfully created" @@ -57,7 +57,7 @@ msgstr "<0>{0} successfully created" #~ msgid "A HTTP proxy server URL (format: http://

:)" #~ msgstr "A HTTP proxy server URL (format: http://
:)" -#: src/renderer/components/input/input.validators.ts:46 +#: src/renderer/components/input/input_validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." @@ -87,7 +87,7 @@ msgstr "Account Name" msgid "Active" msgstr "Active" -#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/+add-cluster/add-cluster.tsx:289 #: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "Add Cluster" @@ -112,7 +112,7 @@ msgstr "Add bindings to {name}" #~ msgid "Add cluster" #~ msgstr "Add cluster" -#: src/renderer/components/+add-cluster/add-cluster.tsx:305 +#: src/renderer/components/+add-cluster/add-cluster.tsx:306 msgid "Add cluster(s)" msgstr "Add cluster(s)" @@ -273,7 +273,7 @@ msgstr "App Version" msgid "App crash at <0>{pageUrl}" msgstr "App crash at <0>{pageUrl}" -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Applying.." msgstr "Applying.." @@ -404,7 +404,7 @@ msgstr "CPU:" #: src/renderer/components/+workspaces/workspaces.tsx:133 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 -#: src/renderer/components/dock/info-panel.tsx:97 +#: src/renderer/components/dock/info-panel.tsx:85 #: src/renderer/components/wizard/wizard.tsx:130 msgid "Cancel" msgstr "Cancel" @@ -440,7 +440,7 @@ msgstr "Chart" msgid "Chart Release <0>{0} successfully created." msgstr "Chart Release <0>{0} successfully created." -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124 msgid "Chart: {0}" msgstr "Chart: {0}" @@ -647,7 +647,7 @@ msgstr "Count" #: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73 #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76 -#: src/renderer/components/dock/create-resource.tsx:71 +#: src/renderer/components/dock/create-resource.tsx:74 msgid "Create" msgstr "Create" @@ -691,7 +691,7 @@ msgstr "Create new Secret" msgid "Create new Service Account" msgstr "Create new Service Account" -#: src/renderer/components/dock/dock.tsx:111 +#: src/renderer/components/dock/dock.tsx:93 msgid "Create resource" msgstr "Create resource" @@ -783,8 +783,8 @@ msgid "Default Runtime Class Name" msgstr "Default Runtime Class Name" #: src/renderer/components/+preferences/kubectl-binaries.tsx:30 -msgid "Default:" -msgstr "Default:" +#~ msgid "Default:" +#~ msgstr "Default:" #: src/renderer/components/+custom-resources/custom-resources.tsx:22 msgid "Definitions" @@ -848,15 +848,19 @@ msgstr "Domains" msgid "Download file" msgstr "Download file" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:39 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:24 msgid "Download kubectl binaries" msgstr "Download kubectl binaries" #: src/renderer/components/+preferences/kubectl-binaries.tsx:37 -msgid "Download kubectl binaries matching to Kubernetes cluster verison." -msgstr "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgstr "Download kubectl binaries matching to Kubernetes cluster verison." -#: src/renderer/components/+preferences/kubectl-binaries.tsx:41 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:26 +msgid "Download kubectl binaries matching to Kubernetes cluster version." +msgstr "Download kubectl binaries matching to Kubernetes cluster version." + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:29 msgid "Download mirror for kubectl" msgstr "Download mirror for kubectl" @@ -944,7 +948,7 @@ msgstr "Everything is fine in the Cluster" #~ msgid "Excluded items with \"system:\" prefix" #~ msgstr "Excluded items with \"system:\" prefix" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Exit full size mode" msgstr "Exit full size mode" @@ -985,7 +989,7 @@ msgstr "Finalizers" msgid "First seen" msgstr "First seen" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Fit to window" msgstr "Fit to window" @@ -1073,7 +1077,7 @@ msgstr "Hide" msgid "High number of replicas may cause cluster performance issues" msgstr "High number of replicas may cause cluster performance issues" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88 msgid "Home" msgstr "Home" @@ -1128,7 +1132,7 @@ msgstr "Image" msgid "ImagePullPolicy" msgstr "ImagePullPolicy" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108 msgid "ImagePullSecrets" msgstr "ImagePullSecrets" @@ -1153,8 +1157,8 @@ msgstr "Ingresses" msgid "Init Containers" msgstr "Init Containers" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76 -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Install" msgstr "Install" @@ -1162,15 +1166,15 @@ msgstr "Install" msgid "Installation complete!" msgstr "Installation complete!" -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Installing..." msgstr "Installing..." -#: src/renderer/components/input/input.validators.ts:50 +#: src/renderer/components/input/input_validators.ts:50 msgid "Invalid account ID" msgstr "Invalid account ID" -#: src/renderer/components/input/input.validators.ts:16 +#: src/renderer/components/input/input_validators.ts:16 msgid "Invalid number" msgstr "Invalid number" @@ -1231,13 +1235,13 @@ msgstr "Key Size" msgid "Keys" msgstr "Keys" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94 msgid "Keywords" msgstr "Keywords" #: src/renderer/components/+events/event-details.tsx:57 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79 -#: src/renderer/components/dock/edit-resource.tsx:89 +#: src/renderer/components/dock/edit-resource.tsx:88 msgid "Kind" msgstr "Kind" @@ -1249,7 +1253,7 @@ msgstr "Kubeconfig" msgid "Kubeconfig File" msgstr "Kubeconfig File" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:23 msgid "Kubectl Binary" msgstr "Kubectl Binary" @@ -1343,7 +1347,7 @@ msgstr "Logs" msgid "Logs copied to clipboard." msgstr "Logs copied to clipboard." -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91 msgid "Maintainers" msgstr "Maintainers" @@ -1369,7 +1373,7 @@ msgstr "Max Pods" msgid "Max Unavailable" msgstr "Max Unavailable" -#: src/renderer/components/input/input.validators.ts:41 +#: src/renderer/components/input/input_validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "Maximum length is {maxLength}" @@ -1441,11 +1445,11 @@ msgstr "Min Available" msgid "Min Pods" msgstr "Min Pods" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Minimize" msgstr "Minimize" -#: src/renderer/components/input/input.validators.ts:36 +#: src/renderer/components/input/input_validators.ts:36 msgid "Minimum length is {minLength}" msgstr "Minimum length is {minLength}" @@ -1454,7 +1458,7 @@ msgstr "Minimum length is {minLength}" msgid "Mount Options" msgstr "Mount Options" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112 msgid "Mountable secrets" msgstr "Mountable secrets" @@ -1510,7 +1514,7 @@ msgstr "Mounts" #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 #: src/renderer/components/+workspaces/workspaces.tsx:130 -#: src/renderer/components/dock/edit-resource.tsx:90 +#: src/renderer/components/dock/edit-resource.tsx:89 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" msgstr "Name" @@ -1556,7 +1560,7 @@ msgstr "Names" #: src/renderer/components/+workloads-jobs/jobs.tsx:38 #: src/renderer/components/+workloads-pods/pods.tsx:76 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41 -#: src/renderer/components/dock/edit-resource.tsx:91 +#: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/dock/install-chart.tsx:122 #: src/renderer/components/dock/upgrade-chart.tsx:98 #: src/renderer/components/item-object-list/page-filters-select.tsx:57 @@ -1600,7 +1604,7 @@ msgstr "Network Policies" msgid "New logs since opening the dialog" msgstr "New logs since opening the dialog" -#: src/renderer/components/dock/dock.tsx:104 +#: src/renderer/components/dock/dock.tsx:86 msgid "New tab" msgstr "New tab" @@ -1734,7 +1738,7 @@ msgstr "Ok" msgid "Ok, got it!" msgstr "Ok, got it!" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Open" msgstr "Open" @@ -1939,7 +1943,7 @@ msgstr "Provisioner" msgid "Proxy is used only for non-cluster communication." msgstr "Proxy is used only for non-cluster communication." -#: src/renderer/components/+add-cluster/add-cluster.tsx:293 +#: src/renderer/components/+add-cluster/add-cluster.tsx:294 msgid "Proxy settings" msgstr "Proxy settings" @@ -2264,7 +2268,7 @@ msgstr "Runtime Class" #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 #: src/renderer/components/+workspaces/workspaces.tsx:132 -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Save" msgstr "Save" @@ -2318,6 +2322,10 @@ msgstr "Secret" msgid "Secret Name" msgstr "Secret Name" +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72 +msgid "Secret is not found" +msgstr "Secret is not found" + #: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147 msgid "Secret name" msgstr "Secret name" @@ -2472,7 +2480,7 @@ msgid "Shell" msgstr "Shell" #: src/renderer/components/+config-secrets/secret-details.tsx:93 -#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100 +#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215 #: src/renderer/components/drawer/drawer-param-toggler.tsx:19 msgid "Show" @@ -2580,12 +2588,12 @@ msgstr "Strategy Type" msgid "Sub-object" msgstr "Sub-object" -#: src/renderer/components/dock/info-panel.tsx:104 +#: src/renderer/components/dock/info-panel.tsx:93 #: src/renderer/components/wizard/wizard.tsx:131 msgid "Submit" msgstr "Submit" -#: src/renderer/components/dock/info-panel.tsx:105 +#: src/renderer/components/dock/info-panel.tsx:94 msgid "Submitting.." msgstr "Submitting.." @@ -2627,10 +2635,14 @@ msgstr "Telemetry & usage data is collected to continuously improve the Lens exp msgid "Terminal" msgstr "Terminal" -#: src/renderer/components/dock/dock.tsx:107 +#: src/renderer/components/dock/dock.tsx:89 msgid "Terminal session" msgstr "Terminal session" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:38 +msgid "The path to the kubectl binary on the system." +msgstr "The path to the kubectl binary on the system." + #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226 msgid "There are no logs available for container." msgstr "There are no logs available for container." @@ -2639,11 +2651,11 @@ msgstr "There are no logs available for container." msgid "There are no logs available." msgstr "There are no logs available." -#: src/renderer/components/input/input.validators.ts:6 +#: src/renderer/components/input/input_validators.ts:6 msgid "This field is required" msgstr "This field is required" -#: src/renderer/components/input/input.validators.ts:31 +#: src/renderer/components/input/input_validators.ts:31 msgid "This field must be a valid path" msgstr "This field must be a valid path" @@ -2663,7 +2675,7 @@ msgstr "To" msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker." msgstr "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker." -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104 msgid "Tokens" msgstr "Tokens" @@ -2730,12 +2742,12 @@ msgstr "Update" msgid "Updated" msgstr "Updated" -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Updating.." msgstr "Updating.." #: src/renderer/components/+apps-releases/release-details.tsx:176 -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Upgrade" msgstr "Upgrade" @@ -2799,7 +2811,7 @@ msgstr "Values" msgid "Verbs" msgstr "Verbs" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85 #: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66 #: src/renderer/components/+apps-releases/release-details.tsx:185 #: src/renderer/components/+apps-releases/releases.tsx:91 @@ -2866,11 +2878,11 @@ msgstr "Workspaces" msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "Workspaces are used to organize number of clusters into logical groups." -#: src/renderer/components/input/input.validators.ts:11 +#: src/renderer/components/input/input_validators.ts:11 msgid "Wrong email format" msgstr "Wrong email format" -#: src/renderer/components/input/input.validators.ts:26 +#: src/renderer/components/input/input_validators.ts:26 msgid "Wrong url format" msgstr "Wrong url format" @@ -2953,7 +2965,7 @@ msgstr "singular" msgid "timestamps" msgstr "timestamps" -#: src/renderer/components/dock/create-resource.tsx:55 +#: src/renderer/components/dock/create-resource.tsx:57 msgid "{0, plural, one {Resource} other {Resources}}" msgstr "{0, plural, one {Resource} other {Resources}}" @@ -3005,6 +3017,6 @@ msgstr "{resourceType} <0>{resourceName} updated." msgid "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}?} other {<2>Remove <3>{selectedCount} items <4>{selectedNames} {tail}?}}" msgstr "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}?} other {<2>Remove <3>{selectedCount} items <4>{selectedNames} {tail}?}}" -#: src/renderer/components/dock/info-panel.tsx:99 +#: src/renderer/components/dock/info-panel.tsx:88 msgid "{submitLabel} & Close" msgstr "{submitLabel} & Close" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index 363b9a49cc..1b53f1bffa 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -49,7 +49,7 @@ msgstr "" #~ msgid "<0>Your browser does not support all Lens features. Please consider using another browser." #~ msgstr "" -#: src/renderer/components/dock/create-resource.tsx:56 +#: src/renderer/components/dock/create-resource.tsx:58 msgid "<0>{0} successfully created" msgstr "" @@ -57,7 +57,7 @@ msgstr "" #~ msgid "A HTTP proxy server URL (format: http://
:)" #~ msgstr "" -#: src/renderer/components/input/input.validators.ts:46 +#: src/renderer/components/input/input_validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "" @@ -87,7 +87,7 @@ msgstr "" msgid "Active" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/+add-cluster/add-cluster.tsx:289 #: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "" @@ -112,7 +112,7 @@ msgstr "" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:305 +#: src/renderer/components/+add-cluster/add-cluster.tsx:306 msgid "Add cluster(s)" msgstr "" @@ -273,7 +273,7 @@ msgstr "" msgid "App crash at <0>{pageUrl}" msgstr "" -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Applying.." msgstr "" @@ -404,7 +404,7 @@ msgstr "" #: src/renderer/components/+workspaces/workspaces.tsx:133 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 -#: src/renderer/components/dock/info-panel.tsx:97 +#: src/renderer/components/dock/info-panel.tsx:85 #: src/renderer/components/wizard/wizard.tsx:130 msgid "Cancel" msgstr "" @@ -440,7 +440,7 @@ msgstr "" msgid "Chart Release <0>{0} successfully created." msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124 msgid "Chart: {0}" msgstr "" @@ -643,7 +643,7 @@ msgstr "" #: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73 #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76 -#: src/renderer/components/dock/create-resource.tsx:71 +#: src/renderer/components/dock/create-resource.tsx:74 msgid "Create" msgstr "" @@ -687,7 +687,7 @@ msgstr "" msgid "Create new Service Account" msgstr "" -#: src/renderer/components/dock/dock.tsx:111 +#: src/renderer/components/dock/dock.tsx:93 msgid "Create resource" msgstr "" @@ -779,8 +779,8 @@ msgid "Default Runtime Class Name" msgstr "" #: src/renderer/components/+preferences/kubectl-binaries.tsx:30 -msgid "Default:" -msgstr "" +#~ msgid "Default:" +#~ msgstr "" #: src/renderer/components/+custom-resources/custom-resources.tsx:22 msgid "Definitions" @@ -844,15 +844,19 @@ msgstr "" msgid "Download file" msgstr "" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:39 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:24 msgid "Download kubectl binaries" msgstr "" #: src/renderer/components/+preferences/kubectl-binaries.tsx:37 -msgid "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgstr "" + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:26 +msgid "Download kubectl binaries matching to Kubernetes cluster version." msgstr "" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:41 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:29 msgid "Download mirror for kubectl" msgstr "" @@ -935,7 +939,7 @@ msgstr "" msgid "Everything is fine in the Cluster" msgstr "" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Exit full size mode" msgstr "" @@ -976,7 +980,7 @@ msgstr "" msgid "First seen" msgstr "" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Fit to window" msgstr "" @@ -1064,7 +1068,7 @@ msgstr "" msgid "High number of replicas may cause cluster performance issues" msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88 msgid "Home" msgstr "" @@ -1119,7 +1123,7 @@ msgstr "" msgid "ImagePullPolicy" msgstr "" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108 msgid "ImagePullSecrets" msgstr "" @@ -1144,8 +1148,8 @@ msgstr "" msgid "Init Containers" msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76 -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Install" msgstr "" @@ -1153,15 +1157,15 @@ msgstr "" msgid "Installation complete!" msgstr "" -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Installing..." msgstr "" -#: src/renderer/components/input/input.validators.ts:50 +#: src/renderer/components/input/input_validators.ts:50 msgid "Invalid account ID" msgstr "" -#: src/renderer/components/input/input.validators.ts:16 +#: src/renderer/components/input/input_validators.ts:16 msgid "Invalid number" msgstr "" @@ -1222,13 +1226,13 @@ msgstr "" msgid "Keys" msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94 msgid "Keywords" msgstr "" #: src/renderer/components/+events/event-details.tsx:57 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79 -#: src/renderer/components/dock/edit-resource.tsx:89 +#: src/renderer/components/dock/edit-resource.tsx:88 msgid "Kind" msgstr "" @@ -1240,7 +1244,7 @@ msgstr "" msgid "Kubeconfig File" msgstr "" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:23 msgid "Kubectl Binary" msgstr "" @@ -1334,7 +1338,7 @@ msgstr "" msgid "Logs copied to clipboard." msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91 msgid "Maintainers" msgstr "" @@ -1360,7 +1364,7 @@ msgstr "" msgid "Max Unavailable" msgstr "" -#: src/renderer/components/input/input.validators.ts:41 +#: src/renderer/components/input/input_validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "" @@ -1432,11 +1436,11 @@ msgstr "" msgid "Min Pods" msgstr "" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Minimize" msgstr "" -#: src/renderer/components/input/input.validators.ts:36 +#: src/renderer/components/input/input_validators.ts:36 msgid "Minimum length is {minLength}" msgstr "" @@ -1445,7 +1449,7 @@ msgstr "" msgid "Mount Options" msgstr "" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112 msgid "Mountable secrets" msgstr "" @@ -1501,7 +1505,7 @@ msgstr "" #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 #: src/renderer/components/+workspaces/workspaces.tsx:130 -#: src/renderer/components/dock/edit-resource.tsx:90 +#: src/renderer/components/dock/edit-resource.tsx:89 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" msgstr "" @@ -1547,7 +1551,7 @@ msgstr "" #: src/renderer/components/+workloads-jobs/jobs.tsx:38 #: src/renderer/components/+workloads-pods/pods.tsx:76 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41 -#: src/renderer/components/dock/edit-resource.tsx:91 +#: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/dock/install-chart.tsx:122 #: src/renderer/components/dock/upgrade-chart.tsx:98 #: src/renderer/components/item-object-list/page-filters-select.tsx:57 @@ -1591,7 +1595,7 @@ msgstr "" msgid "New logs since opening the dialog" msgstr "" -#: src/renderer/components/dock/dock.tsx:104 +#: src/renderer/components/dock/dock.tsx:86 msgid "New tab" msgstr "" @@ -1717,7 +1721,7 @@ msgstr "" msgid "Ok, got it!" msgstr "" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Open" msgstr "" @@ -1922,7 +1926,7 @@ msgstr "" msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:293 +#: src/renderer/components/+add-cluster/add-cluster.tsx:294 msgid "Proxy settings" msgstr "" @@ -2247,7 +2251,7 @@ msgstr "" #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 #: src/renderer/components/+workspaces/workspaces.tsx:132 -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Save" msgstr "" @@ -2301,6 +2305,10 @@ msgstr "" msgid "Secret Name" msgstr "" +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72 +msgid "Secret is not found" +msgstr "" + #: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147 msgid "Secret name" msgstr "" @@ -2455,7 +2463,7 @@ msgid "Shell" msgstr "" #: src/renderer/components/+config-secrets/secret-details.tsx:93 -#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100 +#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215 #: src/renderer/components/drawer/drawer-param-toggler.tsx:19 msgid "Show" @@ -2563,12 +2571,12 @@ msgstr "" msgid "Sub-object" msgstr "" -#: src/renderer/components/dock/info-panel.tsx:104 +#: src/renderer/components/dock/info-panel.tsx:93 #: src/renderer/components/wizard/wizard.tsx:131 msgid "Submit" msgstr "" -#: src/renderer/components/dock/info-panel.tsx:105 +#: src/renderer/components/dock/info-panel.tsx:94 msgid "Submitting.." msgstr "" @@ -2610,10 +2618,14 @@ msgstr "" msgid "Terminal" msgstr "" -#: src/renderer/components/dock/dock.tsx:107 +#: src/renderer/components/dock/dock.tsx:89 msgid "Terminal session" msgstr "" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:38 +msgid "The path to the kubectl binary on the system." +msgstr "" + #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226 msgid "There are no logs available for container." msgstr "" @@ -2622,11 +2634,11 @@ msgstr "" msgid "There are no logs available." msgstr "" -#: src/renderer/components/input/input.validators.ts:6 +#: src/renderer/components/input/input_validators.ts:6 msgid "This field is required" msgstr "" -#: src/renderer/components/input/input.validators.ts:31 +#: src/renderer/components/input/input_validators.ts:31 msgid "This field must be a valid path" msgstr "" @@ -2646,7 +2658,7 @@ msgstr "" msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker." msgstr "" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104 msgid "Tokens" msgstr "" @@ -2713,12 +2725,12 @@ msgstr "" msgid "Updated" msgstr "" -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Updating.." msgstr "" #: src/renderer/components/+apps-releases/release-details.tsx:176 -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Upgrade" msgstr "" @@ -2782,7 +2794,7 @@ msgstr "" msgid "Verbs" msgstr "" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85 #: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66 #: src/renderer/components/+apps-releases/release-details.tsx:185 #: src/renderer/components/+apps-releases/releases.tsx:91 @@ -2849,11 +2861,11 @@ msgstr "" msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "" -#: src/renderer/components/input/input.validators.ts:11 +#: src/renderer/components/input/input_validators.ts:11 msgid "Wrong email format" msgstr "" -#: src/renderer/components/input/input.validators.ts:26 +#: src/renderer/components/input/input_validators.ts:26 msgid "Wrong url format" msgstr "" @@ -2936,7 +2948,7 @@ msgstr "" msgid "timestamps" msgstr "" -#: src/renderer/components/dock/create-resource.tsx:55 +#: src/renderer/components/dock/create-resource.tsx:57 msgid "{0, plural, one {Resource} other {Resources}}" msgstr "" @@ -2988,6 +3000,6 @@ msgstr "" msgid "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}?} other {<2>Remove <3>{selectedCount} items <4>{selectedNames} {tail}?}}" msgstr "" -#: src/renderer/components/dock/info-panel.tsx:99 +#: src/renderer/components/dock/info-panel.tsx:88 msgid "{submitLabel} & Close" msgstr "" diff --git a/locales/ru/messages.po b/locales/ru/messages.po index ea3d5a9731..5cbf7f603e 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -50,7 +50,7 @@ msgstr "<0>Отфильтровано: {itemsCount} / {allItemsCount}" #~ msgid "<0>Your browser does not support all Lens features. Please consider using another browser." #~ msgstr "<0>Ваш браузер не поддерживает все возможности Lens. Пожалуйста рассмотрите использование другого современного браузера." -#: src/renderer/components/dock/create-resource.tsx:56 +#: src/renderer/components/dock/create-resource.tsx:58 msgid "<0>{0} successfully created" msgstr "" @@ -58,7 +58,7 @@ msgstr "" #~ msgid "A HTTP proxy server URL (format: http://
:)" #~ msgstr "" -#: src/renderer/components/input/input.validators.ts:46 +#: src/renderer/components/input/input_validators.ts:46 msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics." msgstr "Это поле может содержать только латинские буквы в нижнем регистре, номера и дефис." @@ -88,7 +88,7 @@ msgstr "Название аккаунта" msgid "Active" msgstr "Активный" -#: src/renderer/components/+add-cluster/add-cluster.tsx:288 +#: src/renderer/components/+add-cluster/add-cluster.tsx:289 #: src/renderer/components/cluster-manager/clusters-menu.tsx:130 msgid "Add Cluster" msgstr "" @@ -113,7 +113,7 @@ msgstr "Добавить привязки к {name}" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:305 +#: src/renderer/components/+add-cluster/add-cluster.tsx:306 msgid "Add cluster(s)" msgstr "" @@ -274,7 +274,7 @@ msgstr "Версия приложения" msgid "App crash at <0>{pageUrl}" msgstr "Сбой работы приложения на <0>{pageUrl}" -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Applying.." msgstr "Применение.." @@ -405,7 +405,7 @@ msgstr "CPU:" #: src/renderer/components/+workspaces/workspaces.tsx:133 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 -#: src/renderer/components/dock/info-panel.tsx:97 +#: src/renderer/components/dock/info-panel.tsx:85 #: src/renderer/components/wizard/wizard.tsx:130 msgid "Cancel" msgstr "Отмена" @@ -441,7 +441,7 @@ msgstr "Чарт" msgid "Chart Release <0>{0} successfully created." msgstr "Релиз чарта <0>{0} успешно создан." -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124 msgid "Chart: {0}" msgstr "Чарт: {0}" @@ -648,7 +648,7 @@ msgstr "Кол-во" #: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73 #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212 #: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76 -#: src/renderer/components/dock/create-resource.tsx:71 +#: src/renderer/components/dock/create-resource.tsx:74 msgid "Create" msgstr "Создать" @@ -692,7 +692,7 @@ msgstr "Создать новый секрет" msgid "Create new Service Account" msgstr "Создать новый Service Account" -#: src/renderer/components/dock/dock.tsx:111 +#: src/renderer/components/dock/dock.tsx:93 msgid "Create resource" msgstr "Создать ресурс" @@ -784,8 +784,8 @@ msgid "Default Runtime Class Name" msgstr "" #: src/renderer/components/+preferences/kubectl-binaries.tsx:30 -msgid "Default:" -msgstr "" +#~ msgid "Default:" +#~ msgstr "" #: src/renderer/components/+custom-resources/custom-resources.tsx:22 msgid "Definitions" @@ -849,15 +849,19 @@ msgstr "Домены" msgid "Download file" msgstr "Скачать файл" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:39 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:24 msgid "Download kubectl binaries" msgstr "" #: src/renderer/components/+preferences/kubectl-binaries.tsx:37 -msgid "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison." +#~ msgstr "" + +#: src/renderer/components/+preferences/kubectl-binaries.tsx:26 +msgid "Download kubectl binaries matching to Kubernetes cluster version." msgstr "" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:41 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:29 msgid "Download mirror for kubectl" msgstr "" @@ -945,7 +949,7 @@ msgstr "В кластере все в порядке" #~ msgid "Excluded items with \"system:\" prefix" #~ msgstr "За исключением объектов с префиксом “system:”" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Exit full size mode" msgstr "Выйти из полного размера" @@ -986,7 +990,7 @@ msgstr "Финализаторы" msgid "First seen" msgstr "Увиденно впервые" -#: src/renderer/components/dock/dock.tsx:116 +#: src/renderer/components/dock/dock.tsx:98 msgid "Fit to window" msgstr "По размеру окна" @@ -1074,7 +1078,7 @@ msgstr "Скрыть" msgid "High number of replicas may cause cluster performance issues" msgstr "Большое количество реплик может вызвать проблемы с производительностью кластера" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88 msgid "Home" msgstr "Ссылка" @@ -1129,7 +1133,7 @@ msgstr "Изображение" msgid "ImagePullPolicy" msgstr "ImagePullPolicy" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108 msgid "ImagePullSecrets" msgstr "ImagePullSecrets" @@ -1154,8 +1158,8 @@ msgstr "Ingresses" msgid "Init Containers" msgstr "Контейнеры инициализации" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76 -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Install" msgstr "Установить" @@ -1163,15 +1167,15 @@ msgstr "Установить" msgid "Installation complete!" msgstr "Установка завершена!" -#: src/renderer/components/dock/install-chart.tsx:128 +#: src/renderer/components/dock/install-chart.tsx:127 msgid "Installing..." msgstr "Установка.." -#: src/renderer/components/input/input.validators.ts:50 +#: src/renderer/components/input/input_validators.ts:50 msgid "Invalid account ID" msgstr "Неверный ID аккаунта" -#: src/renderer/components/input/input.validators.ts:16 +#: src/renderer/components/input/input_validators.ts:16 msgid "Invalid number" msgstr "Неверный номер" @@ -1232,13 +1236,13 @@ msgstr "Размер ключа" msgid "Keys" msgstr "Ключи" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94 msgid "Keywords" msgstr "Ключевые слова" #: src/renderer/components/+events/event-details.tsx:57 #: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79 -#: src/renderer/components/dock/edit-resource.tsx:89 +#: src/renderer/components/dock/edit-resource.tsx:88 msgid "Kind" msgstr "Тип" @@ -1250,7 +1254,7 @@ msgstr "Файл конфигурации" msgid "Kubeconfig File" msgstr "Файл конфигурации" -#: src/renderer/components/+preferences/kubectl-binaries.tsx:35 +#: src/renderer/components/+preferences/kubectl-binaries.tsx:23 msgid "Kubectl Binary" msgstr "" @@ -1344,7 +1348,7 @@ msgstr "Логи" msgid "Logs copied to clipboard." msgstr "Скопировано." -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91 msgid "Maintainers" msgstr "Создатели" @@ -1370,7 +1374,7 @@ msgstr "Макс. подов" msgid "Max Unavailable" msgstr "" -#: src/renderer/components/input/input.validators.ts:41 +#: src/renderer/components/input/input_validators.ts:41 msgid "Maximum length is {maxLength}" msgstr "Максимальная длина {maxLength}" @@ -1442,11 +1446,11 @@ msgstr "" msgid "Min Pods" msgstr "Мин. подов" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Minimize" msgstr "Минимизировать" -#: src/renderer/components/input/input.validators.ts:36 +#: src/renderer/components/input/input_validators.ts:36 msgid "Minimum length is {minLength}" msgstr "Минимальная длина {minLength}" @@ -1455,7 +1459,7 @@ msgstr "Минимальная длина {minLength}" msgid "Mount Options" msgstr "Опции монтирования" -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112 msgid "Mountable secrets" msgstr "Монтируемые секреты" @@ -1511,7 +1515,7 @@ msgstr "Установки" #: src/renderer/components/+workloads-replicasets/replicasets.tsx:50 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40 #: src/renderer/components/+workspaces/workspaces.tsx:130 -#: src/renderer/components/dock/edit-resource.tsx:90 +#: src/renderer/components/dock/edit-resource.tsx:89 #: src/renderer/components/kube-object/kube-object-meta.tsx:20 msgid "Name" msgstr "Имя" @@ -1557,7 +1561,7 @@ msgstr "" #: src/renderer/components/+workloads-jobs/jobs.tsx:38 #: src/renderer/components/+workloads-pods/pods.tsx:76 #: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41 -#: src/renderer/components/dock/edit-resource.tsx:91 +#: src/renderer/components/dock/edit-resource.tsx:90 #: src/renderer/components/dock/install-chart.tsx:122 #: src/renderer/components/dock/upgrade-chart.tsx:98 #: src/renderer/components/item-object-list/page-filters-select.tsx:57 @@ -1601,7 +1605,7 @@ msgstr "Network Policies" msgid "New logs since opening the dialog" msgstr "Новые логи с момента открытия диалога" -#: src/renderer/components/dock/dock.tsx:104 +#: src/renderer/components/dock/dock.tsx:86 msgid "New tab" msgstr "Новая вкладка" @@ -1735,7 +1739,7 @@ msgstr "Ок" msgid "Ok, got it!" msgstr "" -#: src/renderer/components/dock/dock.tsx:117 +#: src/renderer/components/dock/dock.tsx:99 msgid "Open" msgstr "Открыть" @@ -1940,7 +1944,7 @@ msgstr "Комиссия" msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:293 +#: src/renderer/components/+add-cluster/add-cluster.tsx:294 msgid "Proxy settings" msgstr "" @@ -2265,7 +2269,7 @@ msgstr "" #: src/renderer/components/+config-secrets/secret-details.tsx:97 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216 #: src/renderer/components/+workspaces/workspaces.tsx:132 -#: src/renderer/components/dock/edit-resource.tsx:88 +#: src/renderer/components/dock/edit-resource.tsx:87 msgid "Save" msgstr "Сохранить" @@ -2319,6 +2323,10 @@ msgstr "Секрет" msgid "Secret Name" msgstr "Название секрета" +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72 +msgid "Secret is not found" +msgstr "" + #: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147 msgid "Secret name" msgstr "Имя секрета" @@ -2473,7 +2481,7 @@ msgid "Shell" msgstr "Командная строка" #: src/renderer/components/+config-secrets/secret-details.tsx:93 -#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100 +#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101 #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215 #: src/renderer/components/drawer/drawer-param-toggler.tsx:19 msgid "Show" @@ -2581,12 +2589,12 @@ msgstr "Тип стратегии" msgid "Sub-object" msgstr "Суб-объект" -#: src/renderer/components/dock/info-panel.tsx:104 +#: src/renderer/components/dock/info-panel.tsx:93 #: src/renderer/components/wizard/wizard.tsx:131 msgid "Submit" msgstr "Отправить" -#: src/renderer/components/dock/info-panel.tsx:105 +#: src/renderer/components/dock/info-panel.tsx:94 msgid "Submitting.." msgstr "Применение.." @@ -2628,10 +2636,14 @@ msgstr "" msgid "Terminal" msgstr "Терминал" -#: src/renderer/components/dock/dock.tsx:107 +#: src/renderer/components/dock/dock.tsx:89 msgid "Terminal session" msgstr "Сессия терминала" +#: src/renderer/components/+preferences/kubectl-binaries.tsx:38 +msgid "The path to the kubectl binary on the system." +msgstr "" + #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226 msgid "There are no logs available for container." msgstr "Для контейнера нет логов." @@ -2640,11 +2652,11 @@ msgstr "Для контейнера нет логов." msgid "There are no logs available." msgstr "Логи отсутствуют." -#: src/renderer/components/input/input.validators.ts:6 +#: src/renderer/components/input/input_validators.ts:6 msgid "This field is required" msgstr "Это обязательное поле" -#: src/renderer/components/input/input.validators.ts:31 +#: src/renderer/components/input/input_validators.ts:31 msgid "This field must be a valid path" msgstr "" @@ -2664,7 +2676,7 @@ msgstr "Из" msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker." msgstr "Чтобы помочь нам улучшить продукт пожалуйста отправляйте ошибки на {slackLink} сообщество или {githubLink} трекер ошибок." -#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76 +#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104 msgid "Tokens" msgstr "Токены" @@ -2731,12 +2743,12 @@ msgstr "Обновить" msgid "Updated" msgstr "Обновлено" -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Updating.." msgstr "Обновление.." #: src/renderer/components/+apps-releases/release-details.tsx:176 -#: src/renderer/components/dock/upgrade-chart.tsx:105 +#: src/renderer/components/dock/upgrade-chart.tsx:104 msgid "Upgrade" msgstr "Обновить" @@ -2800,7 +2812,7 @@ msgstr "Конфигурация" msgid "Verbs" msgstr "Определения" -#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78 +#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85 #: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66 #: src/renderer/components/+apps-releases/release-details.tsx:185 #: src/renderer/components/+apps-releases/releases.tsx:91 @@ -2867,11 +2879,11 @@ msgstr "" msgid "Workspaces are used to organize number of clusters into logical groups." msgstr "" -#: src/renderer/components/input/input.validators.ts:11 +#: src/renderer/components/input/input_validators.ts:11 msgid "Wrong email format" msgstr "Неверный формат электронной почты" -#: src/renderer/components/input/input.validators.ts:26 +#: src/renderer/components/input/input_validators.ts:26 msgid "Wrong url format" msgstr "Неверный url формат" @@ -2954,7 +2966,7 @@ msgstr "" msgid "timestamps" msgstr "временные метки" -#: src/renderer/components/dock/create-resource.tsx:55 +#: src/renderer/components/dock/create-resource.tsx:57 msgid "{0, plural, one {Resource} other {Resources}}" msgstr "{0, plural, one {Ресурс} few {Ресурсы} many {Ресурсы} other {Ресурсы}}" @@ -3013,6 +3025,6 @@ msgstr "" "other {<2>Удалить <3>{selectedCount} элементов <4>{selectedNames} {tail}?}\n" "}" -#: src/renderer/components/dock/info-panel.tsx:99 +#: src/renderer/components/dock/info-panel.tsx:88 msgid "{submitLabel} & Close" msgstr "{submitLabel} и закрыть" diff --git a/package.json b/package.json index 059ffc63ef..0d1589db30 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ ] }, "jest": { - "testRegex": ".*_(spec|test)\\.[jt]sx?$", "collectCoverage": false, "verbose": true, "testEnvironment": "node", diff --git a/src/common/cluster-store_test.ts b/src/common/__tests__/cluster-store.test.ts similarity index 66% rename from src/common/cluster-store_test.ts rename to src/common/__tests__/cluster-store.test.ts index 3c0369def3..724a83f0d7 100644 --- a/src/common/cluster-store_test.ts +++ b/src/common/__tests__/cluster-store.test.ts @@ -1,9 +1,9 @@ import fs from "fs"; import mockFs from "mock-fs"; import yaml from "js-yaml"; -import { Cluster } from "../main/cluster"; -import { ClusterStore } from "./cluster-store"; -import { workspaceStore } from "./workspace-store"; +import { Cluster } from "../../main/cluster"; +import { ClusterStore } from "../cluster-store"; +import { workspaceStore } from "../workspace-store"; const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png") @@ -12,7 +12,7 @@ console.log("") // fix bug let clusterStore: ClusterStore; describe("empty config", () => { - beforeAll(() => { + beforeEach(() => { ClusterStore.resetInstance(); const mockOpts = { 'tmp': { @@ -24,109 +24,120 @@ describe("empty config", () => { return clusterStore.load(); }) - afterAll(() => { + afterEach(() => { mockFs.restore(); }) - it("adds new cluster to store", async () => { - const cluster = new Cluster({ - id: "foo", - contextName: "minikube", - preferences: { - terminalCWD: "/tmp", - icon: "data:;base64,iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", - clusterName: "minikube" - }, - kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"), - workspace: workspaceStore.currentWorkspaceId + describe("with foo cluster added", () => { + beforeEach(() => { + clusterStore.addCluster( + new Cluster({ + id: "foo", + contextName: "minikube", + preferences: { + terminalCWD: "/tmp", + icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", + clusterName: "minikube" + }, + kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"), + workspace: workspaceStore.currentWorkspaceId + }) + ); + }) + + it("adds new cluster to store", async () => { + const storedCluster = clusterStore.getById("foo"); + expect(storedCluster.id).toBe("foo"); + expect(storedCluster.preferences.terminalCWD).toBe("/tmp"); + expect(storedCluster.preferences.icon).toBe("data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5"); + }) + + it("adds cluster to default workspace", () => { + const storedCluster = clusterStore.getById("foo"); + expect(storedCluster.workspace).toBe("default"); + }) + + it("removes cluster from store", async () => { + await clusterStore.removeById("foo"); + expect(clusterStore.getById("foo")).toBeUndefined(); + }) + + it("sets active cluster", () => { + clusterStore.setActive("foo"); + expect(clusterStore.activeCluster.id).toBe("foo"); + }) + }) + + describe("with prod and dev clusters added", () => { + beforeEach(() => { + clusterStore.addCluster( + new Cluster({ + id: "prod", + contextName: "prod", + preferences: { + clusterName: "prod" + }, + kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"), + workspace: "workstation" + }), + new Cluster({ + id: "dev", + contextName: "dev", + preferences: { + clusterName: "dev" + }, + kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"), + workspace: "workstation" + }) + ) + }) + + it("check if store can contain multiple clusters", () => { + expect(clusterStore.hasClusters()).toBeTruthy(); + expect(clusterStore.clusters.size).toBe(2); }); - clusterStore.addCluster(cluster); - const storedCluster = clusterStore.getById(cluster.id); - expect(storedCluster.id).toBe(cluster.id); - expect(storedCluster.preferences.terminalCWD).toBe(cluster.preferences.terminalCWD); - expect(storedCluster.preferences.icon).toBe(cluster.preferences.icon); - }) - it("adds cluster to default workspace", () => { - const storedCluster = clusterStore.getById("foo"); - expect(storedCluster.workspace).toBe("default"); - }) + it("gets clusters by workspaces", () => { + const wsClusters = clusterStore.getByWorkspaceId("workstation"); + const defaultClusters = clusterStore.getByWorkspaceId("default"); + expect(defaultClusters.length).toBe(0); + expect(wsClusters.length).toBe(2); + expect(wsClusters[0].id).toBe("prod"); + expect(wsClusters[1].id).toBe("dev"); + }) - it("check if store can contain multiple clusters", () => { - const prodCluster = new Cluster({ - id: "prod", - contextName: "prod", - preferences: { - clusterName: "prod" - }, - kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"), - workspace: "workstation" - }); - const devCluster = new Cluster({ - id: "dev", - contextName: "dev", - preferences: { - clusterName: "dev" - }, - kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"), - workspace: "workstation" - }); - clusterStore.addCluster(prodCluster); - clusterStore.addCluster(devCluster); - expect(clusterStore.hasClusters()).toBeTruthy(); - expect(clusterStore.clusters.size).toBe(3); - }); + it("check if cluster's kubeconfig file saved", () => { + const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig"); + expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig"); + }) - it("gets clusters by workspaces", () => { - const wsClusters = clusterStore.getByWorkspaceId("workstation"); - const defaultClusters = clusterStore.getByWorkspaceId("default"); - expect(defaultClusters.length).toBe(1); - expect(wsClusters.length).toBe(2); - expect(wsClusters[0].id).toBe("prod"); - expect(wsClusters[1].id).toBe("dev"); - }) + it("check if reorderring works for same from and to", () => { + clusterStore.swapIconOrders("workstation", 1, 1) - it("sets active cluster", () => { - clusterStore.setActive("foo"); - expect(clusterStore.activeCluster.id).toBe("foo"); - }) + const clusters = clusterStore.getByWorkspaceId("workstation"); + expect(clusters[0].id).toBe("prod") + expect(clusters[0].preferences.iconOrder).toBe(0) + expect(clusters[1].id).toBe("dev") + expect(clusters[1].preferences.iconOrder).toBe(1) + }) - it("check if cluster's kubeconfig file saved", () => { - const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig"); - expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig"); - }) + it("check if reorderring works for different from and to", () => { + clusterStore.swapIconOrders("workstation", 0, 1) - it("check if reorderring works for same from and to", () => { - clusterStore.swapIconOrders("workstation", 1, 1) + const clusters = clusterStore.getByWorkspaceId("workstation"); + expect(clusters[0].id).toBe("dev") + expect(clusters[0].preferences.iconOrder).toBe(0) + expect(clusters[1].id).toBe("prod") + expect(clusters[1].preferences.iconOrder).toBe(1) + }) - const clusters = clusterStore.getByWorkspaceId("workstation"); - expect(clusters[0].id).toBe("prod") - expect(clusters[0].preferences.iconOrder).toBe(0) - expect(clusters[1].id).toBe("dev") - expect(clusters[1].preferences.iconOrder).toBe(1) - }); + it("check if after icon reordering, changing workspaces still works", () => { + clusterStore.swapIconOrders("workstation", 1, 1) + clusterStore.getById("prod").workspace = "default" - it("check if reorderring works for different from and to", () => { - clusterStore.swapIconOrders("workstation", 0, 1) - - const clusters = clusterStore.getByWorkspaceId("workstation"); - expect(clusters[0].id).toBe("dev") - expect(clusters[0].preferences.iconOrder).toBe(0) - expect(clusters[1].id).toBe("prod") - expect(clusters[1].preferences.iconOrder).toBe(1) - }); - - it("check if after icon reordering, changing workspaces still works", () => { - clusterStore.swapIconOrders("workstation", 1, 1) - clusterStore.getById("prod").workspace = "default" - - expect(clusterStore.getByWorkspaceId("workstation").length).toBe(1); - expect(clusterStore.getByWorkspaceId("default").length).toBe(2); - }); - - it("removes cluster from store", async () => { - await clusterStore.removeById("foo"); - expect(clusterStore.getById("foo")).toBeUndefined(); + expect(clusterStore.getByWorkspaceId("workstation").length).toBe(1); + expect(clusterStore.getByWorkspaceId("default").length).toBe(1); + }) }) }) diff --git a/src/common/user-store_test.ts b/src/common/__tests__/user-store.test.ts similarity index 98% rename from src/common/user-store_test.ts rename to src/common/__tests__/user-store.test.ts index 4e9efe97d8..e361397565 100644 --- a/src/common/user-store_test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -10,7 +10,7 @@ jest.mock("electron", () => { } }) -import { UserStore } from "./user-store" +import { UserStore } from "../user-store" import { SemVer } from "semver" import electron from "electron" diff --git a/src/common/workspace-store_test.ts b/src/common/__tests__/workspace-store.test.ts similarity index 98% rename from src/common/workspace-store_test.ts rename to src/common/__tests__/workspace-store.test.ts index 232f0b013a..8ac3ac599d 100644 --- a/src/common/workspace-store_test.ts +++ b/src/common/__tests__/workspace-store.test.ts @@ -10,7 +10,7 @@ jest.mock("electron", () => { } }) -import { WorkspaceStore } from "./workspace-store" +import { WorkspaceStore } from "../workspace-store" describe("workspace store tests", () => { describe("for an empty config", () => { diff --git a/src/common/utils/splitArray_test.ts b/src/common/utils/__tests__/splitArray.test.ts similarity index 95% rename from src/common/utils/splitArray_test.ts rename to src/common/utils/__tests__/splitArray.test.ts index ede542d605..a401e07701 100644 --- a/src/common/utils/splitArray_test.ts +++ b/src/common/utils/__tests__/splitArray.test.ts @@ -1,4 +1,4 @@ -import { splitArray } from "./splitArray"; +import { splitArray } from "../splitArray"; describe("split array on element tests", () => { test("empty array", () => { @@ -16,7 +16,7 @@ describe("split array on element tests", () => { test("one elements, in array", () => { expect(splitArray([1], 1)).toStrictEqual([[], [], true]); }); - + test("ten elements, in front array", () => { expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0)).toStrictEqual([[], [1, 2, 3, 4, 5, 6, 7, 8, 9], true]); }); diff --git a/src/renderer/api/kube-api-parse_test.ts b/src/renderer/api/__tests__/kube-api-parse.test.ts similarity index 89% rename from src/renderer/api/kube-api-parse_test.ts rename to src/renderer/api/__tests__/kube-api-parse.test.ts index dee3bf031d..c2aec7fd58 100644 --- a/src/renderer/api/kube-api-parse_test.ts +++ b/src/renderer/api/__tests__/kube-api-parse.test.ts @@ -1,11 +1,11 @@ -import { IKubeApiParsed, parseKubeApi } from "./kube-api-parse"; +import { IKubeApiParsed, parseKubeApi } from "../kube-api-parse"; -interface KubeApi_Parse_Test { +interface KubeApiParseTestData { url: string; expected: Required; } -const tests: KubeApi_Parse_Test[] = [ +const tests: KubeApiParseTestData[] = [ { url: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/prometheuses.monitoring.coreos.com", expected: { @@ -125,11 +125,10 @@ const tests: KubeApi_Parse_Test[] = [ }, ]; -describe.only("parseApi unit tests", () => { - for (const i in tests) { - const { url: tUrl, expected:tExpect} = tests[i]; - test(`test #${parseInt(i)+1}`, () => { - expect(parseKubeApi(tUrl)).toStrictEqual(tExpect); +describe("parseApi unit tests", () => { + for (const { url, expected } of tests) { + test(`testing "${url}"`, () => { + expect(parseKubeApi(url)).toStrictEqual(expected); }); } }); diff --git a/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx index a11f8aed6e..54d76c08eb 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx @@ -4,7 +4,7 @@ import { Input } from "../../input"; import { observable, autorun } from "mobx"; import { observer, disposeOnUnmount } from "mobx-react"; import { SubTitle } from "../../layout/sub-title"; -import { isRequired } from "../../input/input.validators"; +import { isRequired } from "../../input/input_validators"; interface Props { cluster: Cluster; @@ -33,7 +33,7 @@ export class ClusterNameSetting extends React.Component { render() { return ( <> - +

Define cluster name.

{ render() { return ( <> - +

HTTP Proxy server. Used for communicating with Kubernetes API.

{ const isCount = quota.startsWith("count/"); const icon = isCompute ? "memory" : isStorage ? "storage" : isCount ? "looks_one" : ""; return { - label: icon ? {quota} : quota, + label: icon ? {quota} : quota, value: quota, }; }); @@ -151,7 +151,7 @@ export class AddQuotaDialog extends React.Component { /> - Namespace}/> + Namespace} /> { onChange={({ value }) => this.namespace = value} /> - Values}/> + Values} />
{
- Namespace}/> + Namespace} /> { />
- Secret type}/> + Secret type} /> this.name = v.toLowerCase()} /> - Namespace}/> + Namespace} /> { } diff --git a/src/renderer/components/+workspaces/workspaces.tsx b/src/renderer/components/+workspaces/workspaces.tsx index a2e7dbf74f..3cd5e71f82 100644 --- a/src/renderer/components/+workspaces/workspaces.tsx +++ b/src/renderer/components/+workspaces/workspaces.tsx @@ -12,7 +12,7 @@ import { Icon } from "../icon"; import { Input } from "../input"; import { cssNames, prevDefault } from "../../utils"; import { Button } from "../button"; -import { isRequired, Validator } from "../input/input.validators"; +import { isRequired, Validator } from "../input/input_validators"; @observer export class Workspaces extends React.Component { diff --git a/src/renderer/components/input/input.validators_test.ts b/src/renderer/components/input/__tests__/input_validators.test.ts similarity index 97% rename from src/renderer/components/input/input.validators_test.ts rename to src/renderer/components/input/__tests__/input_validators.test.ts index 4477d63e93..7bf08ffbdb 100644 --- a/src/renderer/components/input/input.validators_test.ts +++ b/src/renderer/components/input/__tests__/input_validators.test.ts @@ -1,4 +1,4 @@ -import { isEmail, systemName } from "./input.validators"; +import { isEmail, systemName } from "../input_validators"; describe("input validation tests", () => { describe("isEmail tests", () => { diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index c109735927..224f7f953e 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -3,7 +3,7 @@ import "./input.scss"; import React, { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react"; import { autobind, cssNames, debouncePromise } from "../../utils"; import { Icon } from "../icon"; -import { conditionalValidators, Validator } from "./input.validators"; +import { conditionalValidators, Validator } from "./input_validators"; import isString from "lodash/isString" import isFunction from "lodash/isFunction" import isBoolean from "lodash/isBoolean" @@ -288,9 +288,9 @@ export class Input extends React.Component { return (