diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 2264b8e868..cdb1f3973a 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -25,7 +25,7 @@ export class BaseStore extends Singleton { protected constructor(protected params: BaseStoreParams) { super(); this.params = { - autoLoad: true, + autoLoad: !app, // disabled in main process due delayed configuration syncEnabled: true, ...params, } @@ -54,7 +54,7 @@ export class BaseStore extends Singleton { } } - protected async load() { + async load() { const { configName, syncEnabled, confOptions = {} } = this.params; // use "await" to make pseudo-async "load" for more future-proof usages @@ -63,7 +63,10 @@ export class BaseStore extends Singleton { projectVersion: getAppVersion(), configName: configName, watch: syncEnabled, // watch for changes in multi-process app (e.g. main/renderer) - cwd: (app || remote.app).getPath("userData"), // todo: remove remote.app in favor ipc.invoke + get cwd() { + // todo: remove remote.app in favor ipc.invoke + return (app || remote.app).getPath("userData"); + }, ...confOptions, }); const data = this.storeConfig.store; diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 7536364e49..3d7483db09 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -1,86 +1,84 @@ -import Config from "conf" -import Singleton from "./utils/singleton"; +import { action, computed, toJS } from "mobx"; import migrations from "../migrations/cluster-store" -import { Cluster, ClusterBaseInfo } from "../main/cluster"; +import { BaseStore } from "./base-store"; +import { Cluster } from "../main/cluster"; -export class ClusterStore extends Singleton { - private storeConfig = new Config({ - configName: "lens-cluster-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - migrations: migrations, - }) +export interface ClusterStoreModel { + clusters: ClusterModel[] +} - public getAllClusterObjects(): Cluster[] { - return this.storeConfig.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => { - return new Cluster(clusterInfo) - }) +export type ClusterId = string; + +export interface ClusterModel { + id: ClusterId; + contextName: string; + kubeConfigPath: string; + kubeConfig?: string; + port?: number; + workspace?: string; + preferences?: ClusterPreferences; +} + +export interface ClusterPreferences { + terminalCWD?: string; + clusterName?: string; + prometheus?: { + namespace: string; + service: string; + port: number; + prefix: string; + }; + prometheusProvider?: { + type: string; + }; + icon?: string; + httpsProxy?: string; +} + +export class ClusterStore extends BaseStore { + private constructor() { + super({ + configName: "lens-cluster-store", + confOptions: { + migrations: migrations, + accessPropertiesByDotNotation: false, // To make dots safe in cluster context names + } + }); } - public getAllClusters(): ClusterBaseInfo[] { - return this.storeConfig.get("clusters", []) + // setup initial value + protected data: ClusterStoreModel = { + clusters: [], } - public removeCluster(id: string): void { - this.storeConfig.delete(id); - const clusterBaseInfos = this.getAllClusters() - const index = clusterBaseInfos.findIndex((cbi) => cbi.id === id) - if (index !== -1) { - clusterBaseInfos.splice(index, 1) - this.storeConfig.set("clusters", clusterBaseInfos) + @computed get clusters(): Cluster[] { + return toJS(this.data.clusters).map(model => new Cluster(model)); + } + + getById(clusterId: ClusterId): Cluster { + return this.clusters.find(cluster => cluster.id === clusterId) + } + + getIndexById(clusterId: ClusterId): number { + return this.clusters.findIndex(cluster => cluster.id === clusterId) + } + + @action + removeById(clusterId: ClusterId): void { + const index = this.getIndexById(clusterId); + if (index > -1) { + this.data.clusters.splice(index, 1); } } - public removeClustersByWorkspace(workspace: string) { - this.getAllClusters().forEach((cluster) => { - if (cluster.workspace === workspace) { - this.removeCluster(cluster.id) + @action + removeAllByWorkspaceId(workspaceId: string) { + this.clusters.forEach(cluster => { + if (cluster.workspace === workspaceId) { + this.removeById(cluster.id) } }) } - - public getCluster(id: string): Cluster { - const cluster = this.getAllClusterObjects().find((cluster) => cluster.id === id) - if (cluster) { - return cluster - } - - return null - } - - public saveCluster(cluster: ClusterBaseInfo) { - const clusters = this.getAllClusters(); - const index = clusters.findIndex((cl) => cl.id === cluster.id) - const storable = { - id: cluster.id, - kubeConfigPath: cluster.kubeConfigPath, - contextName: cluster.contextName, - preferences: cluster.preferences, - workspace: cluster.workspace - } - if (index === -1) { - clusters.push(storable) - } else { - clusters[index] = storable - } - this.storeConfig.set("clusters", clusters) - } - - public storeClusters(clusters: ClusterBaseInfo[]) { - clusters.forEach((cluster: ClusterBaseInfo) => { - this.removeCluster(cluster.id) - this.saveCluster(cluster) - }) - } - - public reloadCluster(cluster: ClusterBaseInfo): void { - const storedCluster = this.getCluster(cluster.id); - if (storedCluster) { - cluster.kubeConfigPath = storedCluster.kubeConfigPath - cluster.contextName = storedCluster.contextName - cluster.preferences = storedCluster.preferences - cluster.workspace = storedCluster.workspace - } - } } export const clusterStore: ClusterStore = ClusterStore.getInstance(); diff --git a/src/common/cluster-store_spec.ts b/src/common/cluster-store_spec.ts deleted file mode 100644 index fad10e8940..0000000000 --- a/src/common/cluster-store_spec.ts +++ /dev/null @@ -1,297 +0,0 @@ -import mockFs from "mock-fs" -import yaml from "js-yaml" -import { ClusterStore } from "./cluster-store"; -import { Cluster } from "../main/cluster"; - -let clusterStore: ClusterStore; - -beforeEach(() => { - ClusterStore.resetInstance() - clusterStore = ClusterStore.getInstance(); -}) - -afterEach(() => { - mockFs.restore() -}) - -describe("for an empty config", () => { - beforeEach(() => { - const mockOpts = { - 'tmp': { - 'lens-cluster-store.json': JSON.stringify({}) - } - } - mockFs(mockOpts) - }) - - it("allows to store and retrieve a cluster", async () => { - const cluster = new Cluster({ - id: 'foo', - kubeConfigPath: 'kubeconfig', - contextName: "foo", - preferences: { - terminalCWD: '/tmp', - icon: 'path to icon' - } - }) - clusterStore.saveCluster(cluster); - const storedCluster = clusterStore.getCluster(cluster.id); - expect(storedCluster.kubeConfigPath).toBe(cluster.kubeConfigPath) - expect(storedCluster.contextName).toBe(cluster.contextName) - expect(storedCluster.preferences.icon).toBe(cluster.preferences.icon) - expect(storedCluster.preferences.terminalCWD).toBe(cluster.preferences.terminalCWD) - expect(storedCluster.id).toBe(cluster.id) - }) - - it("allows to delete a cluster", async () => { - const cluster = new Cluster({ - id: 'foofoo', - kubeConfigPath: 'kubeconfig', - contextName: "foo", - preferences: { - terminalCWD: '/tmp' - } - }) - - clusterStore.saveCluster(cluster); - const storedCluster = clusterStore.getCluster(cluster.id); - expect(storedCluster.id).toBe(cluster.id) - - clusterStore.removeCluster(cluster.id); - - expect(clusterStore.getCluster(cluster.id)).toBe(null) - }) -}) - -describe("for a config with existing clusters", () => { - beforeEach(() => { - const mockOpts = { - 'tmp': { - 'lens-cluster-store.json': JSON.stringify({ - __internal__: { - migrations: { - version: "99.99.99" - } - }, - clusters: [ - { - id: 'cluster1', - kubeConfigPath: 'foo', - preferences: { terminalCWD: '/foo' } - }, - { - id: 'cluster2', - kubeConfigPath: 'foo2', - preferences: { terminalCWD: '/foo2' } - } - ] - }) - } - } - mockFs(mockOpts) - }) - - it("allows to retrieve a cluster", async () => { - const storedCluster = clusterStore.getCluster('cluster1') - expect(storedCluster.kubeConfigPath).toBe('foo') - expect(storedCluster.preferences.terminalCWD).toBe('/foo') - expect(storedCluster.id).toBe('cluster1') - - const storedCluster2 = clusterStore.getCluster('cluster2') - expect(storedCluster2.kubeConfigPath).toBe('foo2') - expect(storedCluster2.preferences.terminalCWD).toBe('/foo2') - expect(storedCluster2.id).toBe('cluster2') - }) - - it("allows to delete a cluster", async () => { - clusterStore.removeCluster('cluster2') - - // Verify the other cluster still exists: - const storedCluster = clusterStore.getCluster('cluster1') - expect(storedCluster.id).toBe('cluster1') - - const storedCluster2 = clusterStore.getCluster('cluster2') - expect(storedCluster2).toBe(null) - }) - - it("allows to reload a cluster in-place", async () => { - const cluster = new Cluster({ - id: 'cluster1', - kubeConfigPath: 'kubeconfig string', - contextName: "foo", - preferences: { - terminalCWD: '/tmp' - } - }) - - clusterStore.reloadCluster(cluster) - - expect(cluster.kubeConfigPath).toBe('foo') - expect(cluster.preferences.terminalCWD).toBe('/foo') - expect(cluster.id).toBe('cluster1') - }) - - it("allows getting all the clusters", async () => { - const storedClusters = clusterStore.getAllClusters() - - expect(storedClusters[0].id).toBe('cluster1') - expect(storedClusters[0].preferences.terminalCWD).toBe('/foo') - expect(storedClusters[0].kubeConfigPath).toBe('foo') - - expect(storedClusters[1].id).toBe('cluster2') - expect(storedClusters[1].preferences.terminalCWD).toBe('/foo2') - expect(storedClusters[1].kubeConfigPath).toBe('foo2') - }) - - it("allows storing the clusters in a different order", async () => { - const storedClusters = clusterStore.getAllClusters() - - const reorderedClusters = [storedClusters[1], storedClusters[0]] - clusterStore.storeClusters(reorderedClusters) - const storedClusters2 = clusterStore.getAllClusters() - - expect(storedClusters2[0].id).toBe('cluster2') - expect(storedClusters2[1].id).toBe('cluster1') - }) -}) - -// describe("for a pre 2.0 config with an existing cluster", () => { -// beforeEach(() => { -// const mockOpts = { -// 'tmp': { -// 'lens-cluster-store.json': JSON.stringify({ -// __internal__: { -// migrations: { -// version: "1.0.0" -// } -// }, -// cluster1: 'kubeconfig content' -// }) -// } -// } -// mockFs(mockOpts) -// }) -// -// it("migrates to modern format with kubeconfig under a key", async () => { -// const storedCluster = clusterStore.store.get('clusters')[0] -// expect(storedCluster.kubeConfigPath).toBe(`tmp/kubeconfigs/${storedCluster.id}`) -// }) -// }) - -// describe("for a pre 2.4.1 config with an existing cluster", () => { -// beforeEach(() => { -// const mockOpts = { -// 'tmp': { -// 'lens-cluster-store.json': JSON.stringify({ -// __internal__: { -// migrations: { -// version: "2.0.0-beta.2" -// } -// }, -// cluster1: { -// kubeConfig: 'foo', -// online: true, -// accessible: false, -// failureReason: 'user error' -// }, -// }) -// } -// } -// mockFs(mockOpts) -// }) -// -// it("migrates to modern format throwing out the state related data", async () => { -// const storedClusterData = clusterStore.store.get('clusters')[0] -// expect(storedClusterData.hasOwnProperty('online')).toBe(false) -// expect(storedClusterData.hasOwnProperty('accessible')).toBe(false) -// expect(storedClusterData.hasOwnProperty('failureReason')).toBe(false) -// }) -// }) - -// describe("for a pre 2.6.0 config with a cluster that has arrays in auth config", () => { -// beforeEach(() => { -// const mockOpts = { -// 'tmp': { -// 'lens-cluster-store.json': JSON.stringify({ -// __internal__: { -// migrations: { -// version: "2.4.1" -// } -// }, -// cluster1: { -// kubeConfig: "apiVersion: v1\nclusters:\n- cluster:\n server: https://10.211.55.6:8443\n name: minikube\ncontexts:\n- context:\n cluster: minikube\n user: minikube\n name: minikube\ncurrent-context: minikube\nkind: Config\npreferences: {}\nusers:\n- name: minikube\n user:\n client-certificate: /Users/kimmo/.minikube/client.crt\n client-key: /Users/kimmo/.minikube/client.key\n auth-provider:\n config:\n access-token:\n - should be string\n expiry:\n - should be string\n" -// }, -// }) -// } -// } -// mockFs(mockOpts) -// }) -// -// it("replaces array format access token and expiry into string", async () => { -// const storedClusterData = clusterStore.store.get('clusters')[0] -// const kc = yaml.safeLoad(fs.readFileSync(storedClusterData.kubeConfigPath).toString()) -// expect(kc.users[0].user['auth-provider'].config['access-token']).toBe("should be string") -// expect(kc.users[0].user['auth-provider'].config['expiry']).toBe("should be string") -// expect(storedClusterData.contextName).toBe("minikube") -// }) -// }) - -// describe("for a pre 2.6.0 config with a cluster icon", () => { -// beforeEach(() => { -// const mockOpts = { -// 'tmp': { -// 'lens-cluster-store.json': JSON.stringify({ -// __internal__: { -// migrations: { -// version: "2.4.1" -// } -// }, -// cluster1: { -// kubeConfig: "foo", -// icon: "icon path", -// preferences: { -// terminalCWD: "/tmp" -// } -// }, -// }) -// } -// } -// mockFs(mockOpts) -// }) -// -// it("moves the icon into preferences", async () => { -// const storedClusterData = clusterStore.store.get('clusters')[0] -// expect(storedClusterData.hasOwnProperty('icon')).toBe(false) -// expect(storedClusterData.preferences.hasOwnProperty('icon')).toBe(true) -// expect(storedClusterData.preferences.icon).toBe("icon path") -// }) -// }) - -// describe("for a pre 2.7.0-beta.0 config without a workspace", () => { -// beforeEach(() => { -// const mockOpts = { -// 'tmp': { -// 'lens-cluster-store.json': JSON.stringify({ -// __internal__: { -// migrations: { -// version: "2.6.6" -// } -// }, -// cluster1: { -// kubeConfig: "foo", -// icon: "icon path", -// preferences: { -// terminalCWD: "/tmp" -// } -// }, -// }) -// } -// } -// mockFs(mockOpts) -// }) -// -// it("adds cluster to default workspace", async () => { -// const storedClusterData = clusterStore.store.get("clusters")[0] -// expect(storedClusterData.workspace).toBe('default') -// }) -// }) diff --git a/src/common/user-store.ts b/src/common/user-store.ts index a3ef9ac944..ba4565660f 100644 --- a/src/common/user-store.ts +++ b/src/common/user-store.ts @@ -1,5 +1,5 @@ import semver from "semver" -import { observable, reaction, toJS } from "mobx"; +import { action, observable, reaction, toJS } from "mobx"; import { BaseStore } from "./base-store"; import migrations from "../migrations/user-store" import { getAppVersion } from "./utils/app-version"; @@ -47,17 +47,19 @@ export class UserStore extends BaseStore { return semver.gt(getAppVersion(), this.lastSeenAppVersion); } + @action saveLastSeenAppVersion() { this.lastSeenAppVersion = getAppVersion(); } + @action protected fromStore(data: Partial = {}) { const { lastSeenAppVersion, seenContexts, preferences } = data if (lastSeenAppVersion) { this.lastSeenAppVersion = lastSeenAppVersion; } if (seenContexts) { - this.seenContexts = observable.set(seenContexts) + this.seenContexts = observable.set(seenContexts); } if (preferences) { Object.assign(this.preferences, preferences); @@ -75,5 +77,4 @@ export class UserStore extends BaseStore { } } -const userStore: UserStore = UserStore.getInstance(); -export { userStore } +export const userStore: UserStore = UserStore.getInstance(); diff --git a/src/common/workspace-store.ts b/src/common/workspace-store.ts index 1c723e6586..ceed4d1297 100644 --- a/src/common/workspace-store.ts +++ b/src/common/workspace-store.ts @@ -1,4 +1,4 @@ -import { computed, toJS } from "mobx"; +import { action, computed, toJS } from "mobx"; import { BaseStore } from "./base-store"; import { clusterStore } from "./cluster-store" @@ -42,6 +42,7 @@ export class WorkspaceStore extends BaseStore { return this.workspaces.findIndex(workspace => workspace.id === id); } + @action public saveWorkspace(newWorkspace: Workspace) { const workspace = this.getById(newWorkspace.id); if (workspace) { @@ -51,6 +52,7 @@ export class WorkspaceStore extends BaseStore { } } + @action public removeWorkspace(workspaceOrId: Workspace | WorkspaceId) { const workspace = this.getById(typeof workspaceOrId == "string" ? workspaceOrId : workspaceOrId.id); if (!workspace) return; @@ -60,7 +62,7 @@ export class WorkspaceStore extends BaseStore { const index = this.getIndexById(workspace.id); if (index > -1) { this.data.workspaces.splice(index, 1) - clusterStore.removeClustersByWorkspace(workspace.id) + clusterStore.removeAllByWorkspaceId(workspace.id) } } } diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 650b7a620c..a05aebdc6e 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -1,8 +1,8 @@ import { KubeConfig } from "@kubernetes/client-node" import { PromiseIpc } from "electron-promise-ipc" import http from "http" -import { Cluster, ClusterBaseInfo } from "./cluster" -import { clusterStore } from "../common/cluster-store" +import { Cluster } from "./cluster" +import { ClusterModel, clusterStore } from "../common/cluster-store" import * as k8s from "./k8s" import logger from "./logger" import { LensProxy } from "./proxy" @@ -14,6 +14,8 @@ import filenamify from "filenamify" import { v4 as uuid } from "uuid" import { apiPrefix } from "../common/vars"; +// todo: refactor + reuse parts of cluster-store more heavily + export type FeatureInstallRequest = { name: string; clusterId: string; @@ -32,7 +34,10 @@ export type ClusterIconUpload = { } export class ClusterManager { - public static readonly clusterIconDir = path.join(app.getPath("userData"), "icons") + static get clusterIconDir(){ + return path.join(app.getPath("userData"), "icons") + } + protected promiseIpc: any protected proxyServer: LensProxy protected port: number @@ -83,7 +88,7 @@ export class ClusterManager { return kc; } - protected async addNewCluster(clusterData: ClusterBaseInfo): Promise { + protected async addNewCluster(clusterData: ClusterModel): Promise { return new Promise(async (resolve, reject) => { try { const kc = this.loadKubeConfig(clusterData.kubeConfigPath) @@ -110,7 +115,7 @@ export class ClusterManager { } protected listenEvents() { - this.promiseIpc.on("addCluster", async (clusterData: ClusterBaseInfo) => { + this.promiseIpc.on("addCluster", async (clusterData: ClusterModel) => { logger.debug(`IPC: addCluster`) const cluster = await this.addNewCluster(clusterData) return { @@ -174,10 +179,10 @@ export class ClusterManager { } try { const clusterIcon = await this.uploadClusterIcon(cluster, fileUpload.name, fileUpload.path) - clusterStore.reloadCluster(cluster); + // clusterStore.reloadCluster(cluster); if(!cluster.preferences) cluster.preferences = {}; cluster.preferences.icon = clusterIcon - clusterStore.saveCluster(cluster); + // clusterStore.saveCluster(cluster); return {success: true, cluster: cluster.toClusterInfo(), message: ""} } catch(error) { return {success: false, message: error} @@ -189,7 +194,7 @@ export class ClusterManager { const cluster = this.getCluster(id) if (cluster && cluster.preferences) { cluster.preferences.icon = null; - clusterStore.saveCluster(cluster) + // clusterStore.saveCluster(cluster) return {success: true, cluster: cluster.toClusterInfo(), message: ""} } else { return {success: false, message: "Cluster not found"} @@ -221,7 +226,7 @@ export class ClusterManager { logger.debug(`IPC: clusterStored: ${clusterId}`) const cluster = this.clusters.get(clusterId) if (cluster) { - clusterStore.reloadCluster(cluster); + // clusterStore.reloadCluster(cluster); cluster.stopServer() } }); @@ -244,7 +249,7 @@ export class ClusterManager { const cluster = this.clusters.get(id) if (cluster) { cluster.stopServer() - clusterStore.removeCluster(cluster.id); + clusterStore.removeById(cluster.id); this.clusters.delete(cluster.id) } return Array.from(this.clusters.values()) diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 8093fd15f7..725b9ae40a 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -1,7 +1,7 @@ import { ContextHandler } from "./context-handler" import { FeatureStatusMap } from "./feature" import * as k8s from "./k8s" -import { clusterStore } from "../common/cluster-store" +import { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store" import logger from "./logger" import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node" import * as fm from "./feature-manager"; @@ -10,6 +10,7 @@ import { KubeconfigManager } from "./kubeconfig-manager" import { PromiseIpc } from "electron-promise-ipc" import request from "request-promise-native" import { apiPrefix } from "../common/vars"; +import type { ClusterInfo } from "../renderer/_vue/store/modules/clusters"; enum ClusterStatus { AccessGranted = 2, @@ -17,49 +18,8 @@ enum ClusterStatus { Offline = 0 } -export interface ClusterBaseInfo { - id: string; - kubeConfig?: string; - kubeConfigPath: string; - contextName: string; - preferences?: ClusterPreferences; - port?: number; - workspace?: string; -} - -export interface ClusterInfo extends ClusterBaseInfo { - url: string; - apiUrl: string; - online?: boolean; - accessible?: boolean; - failureReason?: string; - nodes?: number; - version?: string; - distribution?: string; - isAdmin?: boolean; - features?: FeatureStatusMap; - kubeCtl?: Kubectl; - contextName: string; -} - -export type ClusterPreferences = { - terminalCWD?: string; - clusterName?: string; - prometheus?: { - namespace: string; - service: string; - port: number; - prefix: string; - }; - prometheusProvider?: { - type: string; - }; - icon?: string; - httpsProxy?: string; -} - -export class Cluster implements ClusterInfo { - public id: string; +export class Cluster implements ClusterModel { + public id: ClusterId; public workspace: string; public contextHandler: ContextHandler; public contextName: string; @@ -84,8 +44,8 @@ export class Cluster implements ClusterInfo { protected kubeconfigManager: KubeconfigManager; - constructor(clusterInfo: ClusterBaseInfo) { - if (clusterInfo) Object.assign(this, clusterInfo) + constructor(jsonModel: ClusterModel) { + if (jsonModel) Object.assign(this, jsonModel) if (!this.preferences) this.preferences = {} } @@ -129,7 +89,7 @@ export class Cluster implements ClusterInfo { } public async refreshCluster() { - clusterStore.reloadCluster(this) + // clusterStore.reloadCluster(this) this.contextHandler.setClusterPreferences(this.preferences) const connectionStatus = await this.getConnectionStatus() @@ -155,7 +115,7 @@ export class Cluster implements ClusterInfo { } public save() { - clusterStore.saveCluster(this) + // clusterStore.saveCluster(this) } public toClusterInfo(): ClusterInfo { diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index ad0bd85019..9b7227d53a 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -5,9 +5,10 @@ import * as url from "url" import logger from "./logger" import { getFreePort } from "./port" import { KubeAuthProxy } from "./kube-auth-proxy" -import { Cluster, ClusterPreferences } from "./cluster" +import { Cluster } from "./cluster" import { prometheusProviders } from "../common/prometheus-providers" -import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry" +import type { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry" +import type { ClusterPreferences } from "../common/cluster-store"; export class ContextHandler { public contextName: string diff --git a/src/main/index.ts b/src/main/index.ts index ebe5f4b187..26990f9be3 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,15 +1,9 @@ // Main process import "../common/system-ca" -import { app, dialog, protocol } from "electron" -import { isMac, vueAppName, isDevelopment } from "../common/vars"; -if (isDevelopment) { - const appName = 'LensDev'; - const appData = app.getPath('appData'); - app.setName(appName); - app.setPath('userData', path.join(appData, appName)); -} import "../common/prometheus-providers" +import { app, dialog, protocol } from "electron" +import { isDevelopment, isMac, vueAppName } from "../common/vars"; import { PromiseIpc } from "electron-promise-ipc" import path from "path" import { format as formatUrl } from "url" @@ -17,7 +11,6 @@ import logger from "./logger" import initMenu from "./menu" import * as proxy from "./proxy" import { WindowManager } from "./window-manager"; -import { clusterStore } from "../common/cluster-store" import { ClusterManager } from "./cluster-manager"; import AppUpdater from "./app-updater" import { shellSync } from "./shell-sync" @@ -25,8 +18,17 @@ import { getFreePort } from "./port" import { mangleProxyEnv } from "./proxy-env" import { findMainWebContents } from "./webcontents" import { registerStaticProtocol } from "../common/register-static"; +import { clusterStore } from "../common/cluster-store" +import { userStore } from "../common/user-store"; import { tracker } from "../common/tracker"; +if (isDevelopment) { + const appName = "LensDev"; + const appData = app.getPath("appData"); + app.setName(appName); + app.setPath("userData", path.join(appData, appName)); +} + mangleProxyEnv() if (app.commandLine.getSwitchValue("proxy-server") !== "") { process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server") @@ -69,8 +71,14 @@ async function main() { app.quit(); } + // preload required stores + await Promise.all([ + userStore.load(), + clusterStore.load(), + ]); + // create cluster manager - clusterManager = new ClusterManager(clusterStore.getAllClusterObjects(), port) + clusterManager = new ClusterManager(clusterStore.clusters, port) // run proxy try { proxyServer = proxy.listen(port, clusterManager) diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index c0cfd2e8f5..e95a7751fc 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -46,14 +46,16 @@ if(isDevelopment) { if(process.platform === "win32") bundledPath = `${bundledPath}.exe` export class Kubectl { - public kubectlVersion: string protected directory: string protected url: string protected path: string protected dirname: string - public static readonly kubectlDir = path.join((app || remote.app).getPath("userData"), "binaries", "kubectl") + static get kubectlDir(){ + return path.join((app || remote.app).getPath("userData"), "binaries", "kubectl") + } + public static readonly bundledKubectlPath = bundledPath public static readonly bundledKubectlVersion: string = bundledVersion private static bundledInstance: Kubectl; 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 e31d7f9af8..8b9e24c804 100644 --- a/src/migrations/cluster-store/3.6.0-beta.1.ts +++ b/src/migrations/cluster-store/3.6.0-beta.1.ts @@ -1,21 +1,21 @@ // Move embedded kubeconfig into separate file and add reference to it to cluster settings import path from "path" -import { app } from "electron" +import { app, remote } from "electron" import { migration } from "../migration-wrapper"; import { ensureDirSync } from "fs-extra" import { KubeConfig } from "@kubernetes/client-node"; import { writeEmbeddedKubeConfig } from "../../common/utils/kubeconfig" -import { ClusterBaseInfo } from "../../main/cluster"; +import { ClusterModel } from "../../common/cluster-store"; export default migration({ version: "3.6.0-beta.1", run(store, log: (...args: any[]) => void) { - const migratingClusters: ClusterBaseInfo[] = [] + const migratingClusters: ClusterModel[] = [] - const kubeConfigBase = path.join(app.getPath("userData"), "kubeconfigs") + const kubeConfigBase = path.join((app || remote.app).getPath("userData"), "kubeconfigs") ensureDirSync(kubeConfigBase) - const storedClusters: ClusterBaseInfo[] = store.get("clusters") + const storedClusters: ClusterModel[] = store.get("clusters") if (!storedClusters) return log("Number of clusters to migrate: ", storedClusters.length) diff --git a/src/renderer/_vue/components/MainMenu/MainMenu.vue b/src/renderer/_vue/components/MainMenu/MainMenu.vue index 5ab9391e05..127bb3274c 100644 --- a/src/renderer/_vue/components/MainMenu/MainMenu.vue +++ b/src/renderer/_vue/components/MainMenu/MainMenu.vue @@ -22,7 +22,6 @@ import ClusterMenuItem from "@/_vue/components/MainMenu/ClusterMenuItem"; import AddClusterMenuItem from "@/_vue/components/MainMenu/AddClusterMenuItem"; import draggable from 'vuedraggable' -import { clusterStore } from "../../../../common/cluster-store" import { isMac } from "../../../../common/vars" const {remote} = require('electron') @@ -47,7 +46,7 @@ export default { }, set: function (clusters) { this.$store.commit("updateClusters", clusters); - clusterStore.storeClusters(clusters); + // clusterStore.storeClusters(clusters); } } }, diff --git a/src/renderer/_vue/index.js b/src/renderer/_vue/index.js index 699647f46a..007e6bdfff 100644 --- a/src/renderer/_vue/index.js +++ b/src/renderer/_vue/index.js @@ -10,6 +10,8 @@ import App from './App' import router from './router' import store from './store' import { userStore } from "../../common/user-store" +import { workspaceStore } from "../../common/workspace-store" +import { clusterStore } from "../../common/cluster-store" const promiseIpc = new PromiseIpc({maxTimeoutMs: 6000}); @@ -29,9 +31,13 @@ Vue.mixin({ // any initialization we want to do for app state setTimeout(async () => { - await userStore.whenLoaded; - await store.dispatch('init') + await Promise.all([ + userStore.whenLoaded, + workspaceStore.whenLoaded, + clusterStore.whenLoaded, + ]); + await store.dispatch('init') new Vue({ components: {App}, store, diff --git a/src/renderer/_vue/store/modules/clusters.ts b/src/renderer/_vue/store/modules/clusters.ts index 9f5f2e89e9..08a24a639d 100644 --- a/src/renderer/_vue/store/modules/clusters.ts +++ b/src/renderer/_vue/store/modules/clusters.ts @@ -1,12 +1,29 @@ import Vue from "vue" -import { ClusterInfo } from "../../../../main/cluster" import { ActionTree, GetterTree, MutationTree } from "vuex" import { PromiseIpc } from 'electron-promise-ipc' -import { clusterStore } from "../../../../common/cluster-store" +import { ClusterModel } from "../../../../common/cluster-store" import { Workspace } from "../../../../common/workspace-store" import { tracker } from "../../../../common/tracker"; +import { FeatureStatusMap } from "../../../../main/feature"; +import { Kubectl } from "../../../../main/kubectl"; -const promiseIpc = new PromiseIpc({ maxTimeoutMs: 120000 }); +/** + * @deprecated + */ +export interface ClusterInfo extends ClusterModel { + url: string; + apiUrl: string; + online?: boolean; + accessible?: boolean; + failureReason?: string; + nodes?: number; + version?: string; + distribution?: string; + isAdmin?: boolean; + features?: FeatureStatusMap; + kubeCtl?: Kubectl; + contextName: string; +} export interface LensWebview { id: string; @@ -19,6 +36,8 @@ export interface ClusterState { clusters: ClusterInfo[]; } +const promiseIpc = new PromiseIpc({ maxTimeoutMs: 120000 }); + const state: ClusterState = { lenses: [], clusters: [] @@ -218,7 +237,7 @@ const actions: ActionTree = { }) }, storeCluster({ commit }, cluster: ClusterInfo) { - clusterStore.saveCluster(cluster); + // clusterStore.saveCluster(cluster); commit("updateCluster", cluster) promiseIpc.send("clusterStored", cluster.id) }