diff --git a/locales/en/messages.po b/locales/en/messages.po index 8fe0a97064..9ccefa8003 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -915,7 +915,7 @@ msgstr "Environment" msgid "Error stack" msgstr "Error stack" -#: src/renderer/components/+add-cluster/add-cluster.tsx:129 +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 msgid "Error while adding cluster(s): {0}" msgstr "Error while adding cluster(s): {0}" @@ -1618,6 +1618,14 @@ msgstr "No" msgid "No Nodes Available." msgstr "No Nodes Available." +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ 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 +msgid "No contexts available or they have been added already" +msgstr "No contexts available or they have been added already" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "No filters available." @@ -1770,7 +1778,7 @@ msgstr "Persistent Volume Claims" msgid "Persistent Volumes" msgstr "Persistent Volumes" -#: src/renderer/components/+add-cluster/add-cluster.tsx:95 +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 msgid "Please select at least one cluster context" msgstr "Please select at least one cluster context" @@ -2330,16 +2338,20 @@ msgstr "Select a quota.." #~ msgstr "Select context(s)" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts" -msgstr "Select contexts" +#~ 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})" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 #: src/renderer/components/+add-cluster/add-cluster.tsx:76 #~ msgid "Select custom kube-config file" #~ msgstr "Select custom kube-config file" -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 msgid "Select custom kubeconfig file" msgstr "Select custom kubeconfig file" @@ -2567,7 +2579,7 @@ msgstr "Submitting.." msgid "Subsets" msgstr "Subsets" -#: src/renderer/components/+add-cluster/add-cluster.tsx:122 +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 msgid "Successfully imported <0>{0} cluster(s)" msgstr "Successfully imported <0>{0} cluster(s)" @@ -2721,8 +2733,8 @@ msgstr "Upgrade version" msgid "Usage" msgstr "Usage" -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 msgid "Use configuration" msgstr "Use configuration" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index dd6b82094c..21760f5441 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -911,7 +911,7 @@ msgstr "" msgid "Error stack" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:129 +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 msgid "Error while adding cluster(s): {0}" msgstr "" @@ -1601,6 +1601,14 @@ msgstr "" msgid "No Nodes Available." msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ msgid "No contexts available or they already added" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +msgid "No contexts available or they have been added already" +msgstr "" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "" @@ -1753,7 +1761,7 @@ msgstr "" msgid "Persistent Volumes" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:95 +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 msgid "Please select at least one cluster context" msgstr "" @@ -2313,7 +2321,11 @@ msgstr "" #~ msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts" +#~ msgid "Select contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:272 +msgid "Select contexts (available: {0})" msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 @@ -2321,8 +2333,8 @@ msgstr "" #~ msgid "Select custom kube-config file" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 msgid "Select custom kubeconfig file" msgstr "" @@ -2550,7 +2562,7 @@ msgstr "" msgid "Subsets" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:122 +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 msgid "Successfully imported <0>{0} cluster(s)" msgstr "" @@ -2704,8 +2716,8 @@ msgstr "" msgid "Usage" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 msgid "Use configuration" msgstr "" diff --git a/locales/ru/messages.po b/locales/ru/messages.po index a6d79a7f2b..e1ae65a244 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -916,7 +916,7 @@ msgstr "Среда" msgid "Error stack" msgstr "Стэк ошибки" -#: src/renderer/components/+add-cluster/add-cluster.tsx:129 +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 msgid "Error while adding cluster(s): {0}" msgstr "" @@ -1619,6 +1619,14 @@ msgstr "Нет" msgid "No Nodes Available." msgstr "Нет доступных нод." +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ msgid "No contexts available or they already added" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +msgid "No contexts available or they have been added already" +msgstr "" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "Нет доступных фильтров." @@ -1771,7 +1779,7 @@ msgstr "Persistent Volume Claims" msgid "Persistent Volumes" msgstr "Persistent Volumes" -#: src/renderer/components/+add-cluster/add-cluster.tsx:95 +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 msgid "Please select at least one cluster context" msgstr "" @@ -2331,7 +2339,11 @@ msgstr "Выберите квоту..." #~ msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:272 -msgid "Select contexts" +#~ msgid "Select contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:272 +msgid "Select contexts (available: {0})" msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:76 @@ -2339,8 +2351,8 @@ msgstr "" #~ msgid "Select custom kube-config file" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 -#: src/renderer/components/+add-cluster/add-cluster.tsx:85 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 msgid "Select custom kubeconfig file" msgstr "" @@ -2568,7 +2580,7 @@ msgstr "Применение.." msgid "Subsets" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:122 +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 msgid "Successfully imported <0>{0} cluster(s)" msgstr "" @@ -2722,8 +2734,8 @@ msgstr "Обновить версию" msgid "Usage" msgstr "Использование" -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 -#: src/renderer/components/+add-cluster/add-cluster.tsx:86 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 msgid "Use configuration" msgstr "" diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index a036363bba..63dbfafe0e 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -8,6 +8,9 @@ import { Cluster, ClusterState } from "../main/cluster"; import migrations from "../migrations/cluster-store" import logger from "../main/logger"; import { tracker } from "./tracker"; +import { dumpConfigYaml } from "./kube-helpers"; +import { saveToAppFiles } from "./utils/saveToAppFiles"; +import { KubeConfig } from "@kubernetes/client-node"; export interface ClusterIconUpload { clusterId: string; @@ -50,9 +53,19 @@ export interface ClusterPreferences { } export class ClusterStore extends BaseStore { - static get iconsDir() { - // TODO: remove remote cheat - return path.join((app || remote.app).getPath("userData"), "icons"); + static get iconsDir(): string { + return path.resolve((app || remote.app).getPath("userData"), "icons"); + } + + static getCustomKubeConfigPath(clusterId: ClusterId): string { + return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs", clusterId); + } + + static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string { + const filePath = ClusterStore.getCustomKubeConfigPath(clusterId); + const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig); + saveToAppFiles(filePath, fileContents); + return filePath; } private constructor() { @@ -109,16 +122,12 @@ export class ClusterStore extends BaseStore { } @action - addCluster(model: ClusterModel): Cluster { - tracker.event("cluster", "add"); - const cluster = new Cluster(model); - this.clusters.set(model.id, cluster); - return cluster; - } - - @action - addClusters(models: ClusterModel[]) { - models.forEach(model => this.addCluster(model)); + addCluster(...models: ClusterModel[]) { + models.forEach(model => { + tracker.event("cluster", "add"); + const cluster = new Cluster(model); + this.clusters.set(model.id, cluster); + }) } @action @@ -130,7 +139,10 @@ export class ClusterStore extends BaseStore { if (this.activeClusterId === clusterId) { this.activeClusterId = null; } - unlink(cluster.kubeConfigPath).catch(() => null); + // remove only custom kubeconfigs (pasted as text) + if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) { + unlink(cluster.kubeConfigPath).catch(() => null); + } } } diff --git a/src/common/cluster-store_test.ts b/src/common/cluster-store_test.ts index c2a74a8796..ed4dd3a369 100644 --- a/src/common/cluster-store_test.ts +++ b/src/common/cluster-store_test.ts @@ -4,7 +4,6 @@ import yaml from "js-yaml"; import { Cluster } from "../main/cluster"; import { ClusterStore } from "./cluster-store"; import { workspaceStore } from "./workspace-store"; -import { saveConfigToAppFiles } from "./kube-helpers"; let clusterStore: ClusterStore; @@ -33,7 +32,7 @@ describe("empty config", () => { icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", clusterName: "minikube" }, - kubeConfigPath: saveConfigToAppFiles("foo", "fancy foo config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"), workspace: workspaceStore.currentWorkspaceId }); clusterStore.addCluster(cluster); @@ -54,7 +53,7 @@ describe("empty config", () => { preferences: { clusterName: "prod" }, - kubeConfigPath: saveConfigToAppFiles("prod", "fancy config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"), workspace: "workstation" }); const devCluster = new Cluster({ @@ -62,7 +61,7 @@ describe("empty config", () => { preferences: { clusterName: "dev" }, - kubeConfigPath: saveConfigToAppFiles("dev", "fancy config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"), workspace: "workstation" }); clusterStore.addCluster(prodCluster); @@ -86,7 +85,7 @@ describe("empty config", () => { }) it("check if cluster's kubeconfig file saved", () => { - const file = saveConfigToAppFiles("boo", "kubeconfig"); + const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig"); expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig"); }) diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index 17eac6dcfb..850e714806 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -141,14 +141,3 @@ export function getNodeWarningConditions(node: V1Node) { c.status.toLowerCase() === "true" && c.type !== "Ready" && c.type !== "HostUpgrades" ) } - -// Write kubeconfigs to "embedded" store, i.e. "/Users/ixrock/Library/Application Support/Lens/kubeconfigs" -export function saveConfigToAppFiles(clusterId: string, kubeConfig: KubeConfig | string): string { - const userData = (app || remote.app).getPath("userData"); - const kubeConfigFile = path.join(userData, `kubeconfigs/${clusterId}`) - const kubeConfigContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig); - - ensureDirSync(path.dirname(kubeConfigFile)); - writeFileSync(kubeConfigFile, kubeConfigContents); - return kubeConfigFile; -} diff --git a/src/common/utils/saveToAppFiles.ts b/src/common/utils/saveToAppFiles.ts new file mode 100644 index 0000000000..b0b3ff8d7a --- /dev/null +++ b/src/common/utils/saveToAppFiles.ts @@ -0,0 +1,11 @@ +// Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS) +import path from "path"; +import { app, remote } from "electron"; +import { ensureDirSync, writeFileSync } from "fs-extra"; + +export function saveToAppFiles(filePath: string, contents: any): string { + const absPath = path.resolve((app || remote.app).getPath("userData"), filePath); + ensureDirSync(path.dirname(absPath)); + writeFileSync(absPath, contents); + return absPath; +} diff --git a/src/common/vars.ts b/src/common/vars.ts index 1df1e0f5df..e3651be2c6 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -5,7 +5,7 @@ import { defineGlobal } from "./utils/defineGlobal"; export const isMac = process.platform === "darwin" export const isWindows = process.platform === "win32" -export const isDebugging = process.env.DEBUG === "true"; +export const isDebugging = process.env.DEBUG === "lens"; export const isProduction = process.env.NODE_ENV === "production" export const isDevelopment = isDebugging || !isProduction; export const isTestEnv = !!process.env.JEST_WORKER_ID; diff --git a/src/migrations/cluster-store/3.6.0-beta.1.ts b/src/migrations/cluster-store/3.6.0-beta.1.ts index adc354d9ef..ae5edb2399 100644 --- a/src/migrations/cluster-store/3.6.0-beta.1.ts +++ b/src/migrations/cluster-store/3.6.0-beta.1.ts @@ -1,18 +1,16 @@ // Move embedded kubeconfig into separate file and add reference to it to cluster settings -import path from "path" -import { app, remote } from "electron" import { migration } from "../migration-wrapper"; import { ensureDirSync } from "fs-extra" -import { ClusterModel } from "../../common/cluster-store"; -import { loadConfig, saveConfigToAppFiles } from "../../common/kube-helpers"; +import { ClusterModel, ClusterStore } from "../../common/cluster-store"; +import { loadConfig } from "../../common/kube-helpers"; export default migration({ version: "3.6.0-beta.1", run(store, printLog) { const migratedClusters: ClusterModel[] = [] const storedClusters: ClusterModel[] = store.get("clusters"); - const kubeConfigBase = path.join((app || remote.app).getPath("userData"), "kubeconfigs") + const kubeConfigBase = ClusterStore.getCustomKubeConfigPath(""); if (!storedClusters) return; ensureDirSync(kubeConfigBase); @@ -20,8 +18,8 @@ export default migration({ printLog("Number of clusters to migrate: ", storedClusters.length) for (const cluster of storedClusters) { try { - // take the embedded kubeconfig and dump it into a file - cluster.kubeConfigPath = saveConfigToAppFiles(cluster.id, cluster.kubeConfig) + // take the embedded kubeconfig and dump it into a file] + cluster.kubeConfigPath = ClusterStore.embedCustomKubeConfig(cluster.id, cluster.kubeConfig); cluster.contextName = loadConfig(cluster.kubeConfigPath).getCurrentContext(); delete cluster.kubeConfig; migratedClusters.push(cluster) diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 1a15d4f820..9245926029 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -13,8 +13,8 @@ import { AceEditor } from "../ace-editor"; import { Button } from "../button"; import { Icon } from "../icon"; import { WizardLayout } from "../layout/wizard-layout"; -import { kubeConfigDefaultPath, loadConfig, saveConfigToAppFiles, splitConfig, validateConfig } from "../../../common/kube-helpers"; -import { ClusterModel, clusterStore } from "../../../common/cluster-store"; +import { kubeConfigDefaultPath, loadConfig, splitConfig, validateConfig } from "../../../common/kube-helpers"; +import { ClusterModel, ClusterStore, clusterStore } from "../../../common/cluster-store"; import { workspaceStore } from "../../../common/workspace-store"; import { v4 as uuid } from "uuid" import { navigate } from "../../navigation"; @@ -53,7 +53,7 @@ export class AddCluster extends React.Component { } @action - protected setKubeConfig(filePath: string, { throwError = false } = {}) { + setKubeConfig(filePath: string, { throwError = false } = {}) { try { this.kubeConfigLocal = loadConfig(filePath); validateConfig(this.kubeConfigLocal); @@ -70,7 +70,8 @@ export class AddCluster extends React.Component { } } - refreshContexts = (autoSelect = true) => { + @action + refreshContexts() { this.selectedContexts.clear(); this.kubeContexts.clear(); @@ -90,13 +91,9 @@ export class AddCluster extends React.Component { } break; } - if (autoSelect) { - const allContexts = Array.from(this.kubeContexts.keys()); - this.selectedContexts.replace(allContexts); - } } - protected getContexts(config: KubeConfig): Map { + getContexts(config: KubeConfig): Map { const contexts = new Map(); splitConfig(config).forEach(config => { const isExists = clusterStore.hasContext(config.currentContext); @@ -131,9 +128,12 @@ export class AddCluster extends React.Component { const newClusters: ClusterModel[] = this.selectedContexts.map(context => { const clusterId = uuid(); const kubeConfig = this.kubeContexts.get(context); + const kubeConfigPath = this.sourceTab === KubeConfigSourceTab.FILE + ? this.kubeConfigPath // save link to original kubeconfig in file-system + : ClusterStore.embedCustomKubeConfig(clusterId, kubeConfig); // save in app-files folder return { id: clusterId, - kubeConfigPath: saveConfigToAppFiles(clusterId, kubeConfig), + kubeConfigPath: kubeConfigPath, workspace: workspaceStore.currentWorkspaceId, contextName: kubeConfig.currentContext, preferences: { @@ -143,7 +143,7 @@ export class AddCluster extends React.Component { } }); runInAction(() => { - clusterStore.addClusters(newClusters); + clusterStore.addCluster(...newClusters); if (newClusters.length === 1) { const clusterId = newClusters[0].id; clusterStore.setActive(clusterId); @@ -292,6 +292,7 @@ export class AddCluster extends React.Component { isOptionSelected={() => false} options={allContexts} formatOptionLabel={this.formatContextLabel} + noOptionsMessage={() => _i18n._(t`No contexts available or they have been added already`)} onChange={({ value: ctx }: SelectOption) => { if (this.selectedContexts.includes(ctx)) { this.selectedContexts.remove(ctx) diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index c0fe7e372a..19a80c6217 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -96,7 +96,7 @@ export class ClustersMenu extends React.Component { const clusters = clusterStore.getByWorkspaceId(workspaceStore.currentWorkspaceId); const noClustersInScope = clusters.length === 0; const isLanding = navigation.getPath() === landingURL(); - const showStartupHint = this.showHint && isLanding && noClustersInScope; + const showStartupHint = this.showHint && isLanding && noClustersInScope; // fixme: broken, move to landing.tsx return (