{
- return filesystemProvisionerStore.requestDirectory(this.id);
+ return FilesystemProvisionerStore.getInstance().requestDirectory(this.id);
}
get description() {
diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts
index 0ebb3d90f4..473f031665 100644
--- a/src/extensions/lens-main-extension.ts
+++ b/src/extensions/lens-main-extension.ts
@@ -10,7 +10,7 @@ export class LensMainExtension extends LensExtension {
appMenus: MenuRegistration[] = [];
async navigate(pageId?: string, params?: P, frameId?: number) {
- const windowManager = WindowManager.getInstance();
+ const windowManager = WindowManager.getInstance();
const pageUrl = getExtensionPageUrl({
extensionId: this.name,
pageId,
diff --git a/src/extensions/registries/__tests__/page-registry.test.ts b/src/extensions/registries/__tests__/page-registry.test.ts
index 55ba3d6d64..1d8598e451 100644
--- a/src/extensions/registries/__tests__/page-registry.test.ts
+++ b/src/extensions/registries/__tests__/page-registry.test.ts
@@ -1,6 +1,10 @@
import { getExtensionPageUrl, globalPageRegistry, PageParams } from "../page-registry";
import { LensExtension } from "../../lens-extension";
import React from "react";
+import { Console } from "console";
+import { stdout, stderr } from "process";
+
+console = new Console(stdout, stderr);
let ext: LensExtension = null;
diff --git a/src/extensions/renderer-api/theming.ts b/src/extensions/renderer-api/theming.ts
index b3da69bdbc..b2fdbfff56 100644
--- a/src/extensions/renderer-api/theming.ts
+++ b/src/extensions/renderer-api/theming.ts
@@ -1,5 +1,5 @@
-import { themeStore } from "../../renderer/theme.store";
+import { ThemeStore } from "../../renderer/theme.store";
export function getActiveTheme() {
- return themeStore.activeTheme;
+ return ThemeStore.getInstance().activeTheme;
}
diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts
index 113bb49a0c..40050e10d1 100644
--- a/src/main/__test__/cluster.test.ts
+++ b/src/main/__test__/cluster.test.ts
@@ -23,7 +23,6 @@ jest.mock("winston", () => ({
}
}));
-
jest.mock("../../common/ipc");
jest.mock("../context-handler");
jest.mock("request");
diff --git a/src/main/__test__/kube-auth-proxy.test.ts b/src/main/__test__/kube-auth-proxy.test.ts
index b161372555..dbed900fd7 100644
--- a/src/main/__test__/kube-auth-proxy.test.ts
+++ b/src/main/__test__/kube-auth-proxy.test.ts
@@ -36,6 +36,11 @@ import { bundledKubectlPath, Kubectl } from "../kubectl";
import { mock, MockProxy } from "jest-mock-extended";
import { waitUntilUsed } from "tcp-port-used";
import { Readable } from "stream";
+import { UserStore } from "../../common/user-store";
+import { Console } from "console";
+import { stdout, stderr } from "process";
+
+console = new Console(stdout, stderr);
const mockBroadcastIpc = broadcastMessage as jest.MockedFunction;
const mockSpawn = spawn as jest.MockedFunction;
@@ -44,6 +49,8 @@ const mockWaitUntilUsed = waitUntilUsed as jest.MockedFunction {
beforeEach(() => {
jest.clearAllMocks();
+ UserStore.resetInstance();
+ UserStore.getInstanceOrCreate();
});
it("calling exit multiple times shouldn't throw", async () => {
diff --git a/src/main/__test__/kubeconfig-manager.test.ts b/src/main/__test__/kubeconfig-manager.test.ts
index 1cc39a4ada..5ae7681d37 100644
--- a/src/main/__test__/kubeconfig-manager.test.ts
+++ b/src/main/__test__/kubeconfig-manager.test.ts
@@ -36,10 +36,6 @@ import * as path from "path";
console = new Console(process.stdout, process.stderr); // fix mockFS
describe("kubeconfig manager tests", () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
beforeEach(() => {
const mockOpts = {
"minikube-config.yml": JSON.stringify({
@@ -76,7 +72,7 @@ describe("kubeconfig manager tests", () => {
const cluster = new Cluster({
id: "foo",
contextName: "minikube",
- kubeConfigPath: "minikube-config.yml"
+ kubeConfigPath: "minikube-config.yml",
});
const contextHandler = new ContextHandler(cluster);
const port = await getFreePort();
@@ -98,7 +94,7 @@ describe("kubeconfig manager tests", () => {
const cluster = new Cluster({
id: "foo",
contextName: "minikube",
- kubeConfigPath: "minikube-config.yml"
+ kubeConfigPath: "minikube-config.yml",
});
const contextHandler = new ContextHandler(cluster);
const port = await getFreePort();
diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts
index 047fb0ca95..0a413584f8 100644
--- a/src/main/cluster-manager.ts
+++ b/src/main/cluster-manager.ts
@@ -2,7 +2,7 @@ import "../common/cluster-ipc";
import type http from "http";
import { ipcMain } from "electron";
import { action, autorun, observable, reaction, toJS } from "mobx";
-import { clusterStore, getClusterIdFromHost } from "../common/cluster-store";
+import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store";
import { Cluster } from "./cluster";
import logger from "./logger";
import { apiKubePrefix } from "../common/vars";
@@ -21,7 +21,7 @@ export class ClusterManager extends Singleton {
catalogEntityRegistry.addSource("lens:kubernetes-clusters", this.catalogSource);
// auto-init clusters
- reaction(() => clusterStore.enabledClustersList, (clusters) => {
+ reaction(() => ClusterStore.getInstance().enabledClustersList, (clusters) => {
clusters.forEach((cluster) => {
if (!cluster.initialized && !cluster.initializing) {
logger.info(`[CLUSTER-MANAGER]: init cluster`, cluster.getMeta());
@@ -31,8 +31,8 @@ export class ClusterManager extends Singleton {
}, { fireImmediately: true });
- reaction(() => toJS(clusterStore.enabledClustersList, { recurseEverything: true }), () => {
- this.updateCatalogSource(clusterStore.enabledClustersList);
+ reaction(() => toJS(ClusterStore.getInstance().enabledClustersList, { recurseEverything: true }), () => {
+ this.updateCatalogSource(ClusterStore.getInstance().enabledClustersList);
}, { fireImmediately: true });
reaction(() => catalogEntityRegistry.getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => {
@@ -42,14 +42,14 @@ export class ClusterManager extends Singleton {
// auto-stop removed clusters
autorun(() => {
- const removedClusters = Array.from(clusterStore.removedClusters.values());
+ const removedClusters = Array.from(ClusterStore.getInstance().removedClusters.values());
if (removedClusters.length > 0) {
const meta = removedClusters.map(cluster => cluster.getMeta());
logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
removedClusters.forEach(cluster => cluster.disconnect());
- clusterStore.removedClusters.clear();
+ ClusterStore.getInstance().removedClusters.clear();
}
}, {
delay: 250
@@ -90,10 +90,10 @@ export class ClusterManager extends Singleton {
@action syncClustersFromCatalog(entities: KubernetesCluster[]) {
entities.filter((entity) => entity.metadata.source !== "local").forEach((entity: KubernetesCluster) => {
- const cluster = clusterStore.getById(entity.metadata.uid);
+ const cluster = ClusterStore.getInstance().getById(entity.metadata.uid);
if (!cluster) {
- clusterStore.addCluster({
+ ClusterStore.getInstance().addCluster({
id: entity.metadata.uid,
enabled: true,
ownerRef: clusterOwnerRef,
@@ -145,7 +145,7 @@ export class ClusterManager extends Singleton {
protected onNetworkOffline() {
logger.info("[CLUSTER-MANAGER]: network is offline");
- clusterStore.enabledClustersList.forEach((cluster) => {
+ ClusterStore.getInstance().enabledClustersList.forEach((cluster) => {
if (!cluster.disconnected) {
cluster.online = false;
cluster.accessible = false;
@@ -156,7 +156,7 @@ export class ClusterManager extends Singleton {
protected onNetworkOnline() {
logger.info("[CLUSTER-MANAGER]: network is online");
- clusterStore.enabledClustersList.forEach((cluster) => {
+ ClusterStore.getInstance().enabledClustersList.forEach((cluster) => {
if (!cluster.disconnected) {
cluster.refreshConnectionStatus().catch((e) => e);
}
@@ -164,7 +164,7 @@ export class ClusterManager extends Singleton {
}
stop() {
- clusterStore.clusters.forEach((cluster: Cluster) => {
+ ClusterStore.getInstance().clusters.forEach((cluster: Cluster) => {
cluster.disconnect();
});
}
@@ -176,18 +176,18 @@ export class ClusterManager extends Singleton {
if (req.headers.host.startsWith("127.0.0.1")) {
const clusterId = req.url.split("/")[1];
- cluster = clusterStore.getById(clusterId);
+ cluster = ClusterStore.getInstance().getById(clusterId);
if (cluster) {
// we need to swap path prefix so that request is proxied to kube api
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix);
}
} else if (req.headers["x-cluster-id"]) {
- cluster = clusterStore.getById(req.headers["x-cluster-id"].toString());
+ cluster = ClusterStore.getInstance().getById(req.headers["x-cluster-id"].toString());
} else {
const clusterId = getClusterIdFromHost(req.headers.host);
- cluster = clusterStore.getById(clusterId);
+ cluster = ClusterStore.getInstance().getById(clusterId);
}
return cluster;
diff --git a/src/main/developer-tools.ts b/src/main/developer-tools.ts
index d0d7e2ae01..866e6fb561 100644
--- a/src/main/developer-tools.ts
+++ b/src/main/developer-tools.ts
@@ -4,11 +4,12 @@ import logger from "./logger";
* Installs Electron developer tools in the development build.
* The dependency is not bundled to the production build.
*/
-export const installDeveloperTools = async () => {
+export const installDeveloperTools = () => {
if (process.env.NODE_ENV === "development") {
logger.info("🤓 Installing developer tools");
- const { default: devToolsInstaller, REACT_DEVELOPER_TOOLS } = await import("electron-devtools-installer");
-
- return devToolsInstaller([REACT_DEVELOPER_TOOLS]);
+ import("electron-devtools-installer")
+ .then(({ default: devToolsInstaller, REACT_DEVELOPER_TOOLS }) => devToolsInstaller([REACT_DEVELOPER_TOOLS]))
+ .then((name) => logger.info(`[DEVTOOLS-INSTALLER]: installed ${name}`))
+ .catch(error => logger.error(`[DEVTOOLS-INSTALLER]: failed`, { error }));
}
};
diff --git a/src/main/exit-app.ts b/src/main/exit-app.ts
index eb035eff96..6604562842 100644
--- a/src/main/exit-app.ts
+++ b/src/main/exit-app.ts
@@ -4,10 +4,14 @@ import { appEventBus } from "../common/event-bus";
import { ClusterManager } from "./cluster-manager";
import logger from "./logger";
-
export function exitApp() {
- const windowManager = WindowManager.getInstance();
- const clusterManager = ClusterManager.getInstance();
+ console.log("before windowManager");
+ const windowManager = WindowManager.getInstance(false);
+
+ console.log("before clusterManager");
+ const clusterManager = ClusterManager.getInstance(false);
+
+ console.log("after clusterManager");
appEventBus.emit({ name: "service", action: "close" });
windowManager?.hide();
diff --git a/src/main/extension-filesystem.ts b/src/main/extension-filesystem.ts
index eddb7b747f..77bb51697a 100644
--- a/src/main/extension-filesystem.ts
+++ b/src/main/extension-filesystem.ts
@@ -14,7 +14,7 @@ interface FSProvisionModel {
export class FilesystemProvisionerStore extends BaseStore {
@observable registeredExtensions = observable.map();
- private constructor() {
+ constructor() {
super({
configName: "lens-filesystem-provisioner-store",
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
@@ -56,5 +56,3 @@ export class FilesystemProvisionerStore extends BaseStore {
});
}
}
-
-export const filesystemProvisionerStore = FilesystemProvisionerStore.getInstance();
diff --git a/src/main/helm/__tests__/helm-service.test.ts b/src/main/helm/__tests__/helm-service.test.ts
index 8c1e82ef0a..24b22c43d5 100644
--- a/src/main/helm/__tests__/helm-service.test.ts
+++ b/src/main/helm/__tests__/helm-service.test.ts
@@ -1,17 +1,24 @@
import { helmService } from "../helm-service";
-import { repoManager } from "../helm-repo-manager";
+import { HelmRepoManager } from "../helm-repo-manager";
-jest.spyOn(repoManager, "init").mockImplementation();
+const mockHelmRepoManager = jest.spyOn(HelmRepoManager, "getInstance").mockImplementation();
jest.mock("../helm-chart-manager");
describe("Helm Service tests", () => {
- test("list charts without deprecated ones", async () => {
- jest.spyOn(repoManager, "repositories").mockImplementation(async () => {
- return [
- { name: "stable", url: "stableurl" },
- { name: "experiment", url: "experimenturl" }
- ];
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it("list charts without deprecated ones", async () => {
+ mockHelmRepoManager.mockReturnValue({
+ init: jest.fn(),
+ repositories: jest.fn().mockImplementation(async () => {
+ return [
+ { name: "stable", url: "stableurl" },
+ { name: "experiment", url: "experimenturl" },
+ ];
+ }),
});
const charts = await helmService.listCharts();
@@ -55,11 +62,14 @@ describe("Helm Service tests", () => {
});
});
- test("list charts sorted by version in descending order", async () => {
- jest.spyOn(repoManager, "repositories").mockImplementation(async () => {
- return [
- { name: "bitnami", url: "bitnamiurl" }
- ];
+ it("list charts sorted by version in descending order", async () => {
+ mockHelmRepoManager.mockReturnValue({
+ init: jest.fn(),
+ repositories: jest.fn().mockImplementation(async () => {
+ return [
+ { name: "bitnami", url: "bitnamiurl" },
+ ];
+ }),
});
const charts = await helmService.listCharts();
diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts
index 822b939cf7..dcd66b3875 100644
--- a/src/main/helm/helm-release-manager.ts
+++ b/src/main/helm/helm-release-manager.ts
@@ -1,17 +1,17 @@
import * as tempy from "tempy";
-import fs from "fs";
+import fse from "fs-extra";
import * as yaml from "js-yaml";
import { promiseExec} from "../promise-exec";
import { helmCli } from "./helm-cli";
import { Cluster } from "../cluster";
import { toCamelCase } from "../../common/utils/camelCase";
-export class HelmReleaseManager {
+export async function listReleases(pathToKubeconfig: string, namespace?: string) {
+ const helm = await helmCli.binaryPath();
+ const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces";
- public async listReleases(pathToKubeconfig: string, namespace?: string) {
- const helm = await helmCli.binaryPath();
- const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces";
- const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
+ try {
+ const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`);
const output = JSON.parse(stdout);
if (output.length == 0) {
@@ -22,106 +22,132 @@ export class HelmReleaseManager {
});
return output;
- }
-
-
- public async installChart(chart: string, values: any, name: string, namespace: string, version: string, pathToKubeconfig: string){
- const helm = await helmCli.binaryPath();
- const fileName = tempy.file({name: "values.yaml"});
-
- await fs.promises.writeFile(fileName, yaml.safeDump(values));
-
- try {
- let generateName = "";
-
- if (!name) {
- generateName = "--generate-name";
- name = "";
- }
- const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`).catch((error) => { throw(error.stderr);});
- const releaseName = stdout.split("\n")[0].split(" ")[1].trim();
-
- return {
- log: stdout,
- release: {
- name: releaseName,
- namespace
- }
- };
- } finally {
- await fs.promises.unlink(fileName);
- }
- }
-
- public async upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster){
- const helm = await helmCli.binaryPath();
- const fileName = tempy.file({name: "values.yaml"});
- const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
-
- await fs.promises.writeFile(fileName, yaml.safeDump(values));
-
- try {
- const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`).catch((error) => { throw(error.stderr);});
-
- return {
- log: stdout,
- release: this.getRelease(name, namespace, cluster)
- };
- } finally {
- await fs.promises.unlink(fileName);
- }
- }
-
- public async getRelease(name: string, namespace: string, cluster: Cluster) {
- const helm = await helmCli.binaryPath();
- const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
-
- const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`).catch((error) => { throw(error.stderr);});
- const release = JSON.parse(stdout);
-
- release.resources = await this.getResources(name, namespace, cluster);
-
- return release;
- }
-
- public async deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
- const helm = await helmCli.binaryPath();
- const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
-
- return stdout;
- }
-
- public async getValues(name: string, namespace: string, all: boolean, pathToKubeconfig: string) {
- const helm = await helmCli.binaryPath();
- const { stdout, } = await promiseExec(`"${helm}" get values ${name} ${all? "--all": ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
-
- return stdout;
- }
-
- public async getHistory(name: string, namespace: string, pathToKubeconfig: string) {
- const helm = await helmCli.binaryPath();
- const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
-
- return JSON.parse(stdout);
- }
-
- public async rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
- const helm = await helmCli.binaryPath();
- const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
-
- return stdout;
- }
-
- protected async getResources(name: string, namespace: string, cluster: Cluster) {
- const helm = await helmCli.binaryPath();
- const kubectl = await cluster.kubeCtl.getPath();
- const pathToKubeconfig = await cluster.getProxyKubeconfigPath();
- const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch(() => {
- return { stdout: JSON.stringify({items: []})};
- });
-
- return stdout;
+ } catch ({ stderr }) {
+ throw stderr;
}
}
-export const releaseManager = new HelmReleaseManager();
+
+export async function installChart(chart: string, values: any, name: string | undefined, namespace: string, version: string, pathToKubeconfig: string){
+ const helm = await helmCli.binaryPath();
+ const fileName = tempy.file({name: "values.yaml"});
+
+ await fse.writeFile(fileName, yaml.safeDump(values));
+
+ try {
+ let generateName = "";
+
+ if (!name) {
+ generateName = "--generate-name";
+ name = "";
+ }
+ const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`);
+ const releaseName = stdout.split("\n")[0].split(" ")[1].trim();
+
+ return {
+ log: stdout,
+ release: {
+ name: releaseName,
+ namespace
+ }
+ };
+ } catch ({ stderr }) {
+ throw stderr;
+ } finally {
+ await fse.unlink(fileName);
+ }
+}
+
+export async function upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster) {
+ const helm = await helmCli.binaryPath();
+ const fileName = tempy.file({name: "values.yaml"});
+
+ await fse.writeFile(fileName, yaml.safeDump(values));
+
+ try {
+ const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
+ const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`);
+
+ return {
+ log: stdout,
+ release: getRelease(name, namespace, cluster)
+ };
+ } catch ({ stderr }) {
+ throw stderr;
+ } finally {
+ await fse.unlink(fileName);
+ }
+}
+
+export async function getRelease(name: string, namespace: string, cluster: Cluster) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
+
+ const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`);
+ const release = JSON.parse(stdout);
+
+ release.resources = await getResources(name, namespace, cluster);
+
+ return release;
+ } catch ({ stderr }) {
+ throw stderr;
+ }
+}
+
+export async function deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
+
+ return stdout;
+ } catch ({ stderr }) {
+ throw stderr;
+ }
+}
+
+export async function getValues(name: string, namespace: string, all: boolean, pathToKubeconfig: string) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const { stdout, } = await promiseExec(`"${helm}" get values ${name} ${all ? "--all": ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
+
+ return stdout;
+ } catch ({ stderr }) {
+ throw stderr;
+ }
+}
+
+export async function getHistory(name: string, namespace: string, pathToKubeconfig: string) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
+
+ return JSON.parse(stdout);
+ } catch ({ stderr }) {
+ throw stderr;
+ }
+}
+
+export async function rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
+
+ return stdout;
+ } catch ({ stderr }) {
+ throw stderr;
+ }
+}
+
+async function getResources(name: string, namespace: string, cluster: Cluster) {
+ try {
+ const helm = await helmCli.binaryPath();
+ const kubectl = await cluster.kubeCtl.getPath();
+ const pathToKubeconfig = await cluster.getProxyKubeconfigPath();
+ const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`);
+
+ return stdout;
+ } catch {
+ return { stdout: JSON.stringify({ items: [] }) };
+ }
+}
diff --git a/src/main/helm/helm-repo-manager.ts b/src/main/helm/helm-repo-manager.ts
index 74afb166b1..5761ac5dec 100644
--- a/src/main/helm/helm-repo-manager.ts
+++ b/src/main/helm/helm-repo-manager.ts
@@ -77,10 +77,6 @@ export class HelmRepoManager extends Singleton {
}
public async repositories(): Promise {
- if (!this.initialized) {
- await this.init();
- }
-
try {
const repoConfigFile = this.helmEnv.HELM_REPOSITORY_CONFIG;
const { repositories }: HelmRepoConfig = await readFile(repoConfigFile, "utf8")
@@ -160,5 +156,3 @@ export class HelmRepoManager extends Singleton {
return stdout;
}
}
-
-export const repoManager = HelmRepoManager.getInstance();
diff --git a/src/main/helm/helm-service.ts b/src/main/helm/helm-service.ts
index 69e8fa2ccb..9a42616c89 100644
--- a/src/main/helm/helm-service.ts
+++ b/src/main/helm/helm-service.ts
@@ -1,23 +1,21 @@
import semver from "semver";
import { Cluster } from "../cluster";
import logger from "../logger";
-import { repoManager } from "./helm-repo-manager";
+import { HelmRepoManager } from "./helm-repo-manager";
import { HelmChartManager } from "./helm-chart-manager";
-import { releaseManager } from "./helm-release-manager";
import { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api";
+import { deleteRelease, getHistory, getRelease, getValues, installChart, listReleases, rollback, upgradeRelease } from "./helm-release-manager";
class HelmService {
public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
- return await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig);
+ return installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig);
}
public async listCharts() {
const charts: HelmChartList = {};
-
- await repoManager.init();
- const repositories = await repoManager.repositories();
+ const repositories = await HelmRepoManager.getInstance().repositories();
for (const repo of repositories) {
charts[repo.name] = {};
@@ -36,7 +34,7 @@ class HelmService {
readme: "",
versions: {}
};
- const repo = await repoManager.repository(repoName);
+ const repo = await HelmRepoManager.getInstance().repository(repoName);
const chartManager = new HelmChartManager(repo);
const chart = await chartManager.chart(chartName);
@@ -47,23 +45,22 @@ class HelmService {
}
public async getChartValues(repoName: string, chartName: string, version = "") {
- const repo = await repoManager.repository(repoName);
+ const repo = await HelmRepoManager.getInstance().repository(repoName);
const chartManager = new HelmChartManager(repo);
return chartManager.getValues(chartName, version);
}
public async listReleases(cluster: Cluster, namespace: string = null) {
- await repoManager.init();
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
- return await releaseManager.listReleases(proxyKubeconfig, namespace);
+ return listReleases(proxyKubeconfig, namespace);
}
public async getRelease(cluster: Cluster, releaseName: string, namespace: string) {
logger.debug("Fetch release");
- return await releaseManager.getRelease(releaseName, namespace, cluster);
+ return getRelease(releaseName, namespace, cluster);
}
public async getReleaseValues(cluster: Cluster, releaseName: string, namespace: string, all: boolean) {
@@ -71,7 +68,7 @@ class HelmService {
logger.debug("Fetch release values");
- return await releaseManager.getValues(releaseName, namespace, all, proxyKubeconfig);
+ return getValues(releaseName, namespace, all, proxyKubeconfig);
}
public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) {
@@ -79,7 +76,7 @@ class HelmService {
logger.debug("Fetch release history");
- return await releaseManager.getHistory(releaseName, namespace, proxyKubeconfig);
+ return getHistory(releaseName, namespace, proxyKubeconfig);
}
public async deleteRelease(cluster: Cluster, releaseName: string, namespace: string) {
@@ -87,20 +84,20 @@ class HelmService {
logger.debug("Delete release");
- return await releaseManager.deleteRelease(releaseName, namespace, proxyKubeconfig);
+ return deleteRelease(releaseName, namespace, proxyKubeconfig);
}
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) {
logger.debug("Upgrade release");
- return await releaseManager.upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster);
+ return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster);
}
public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Rollback release");
- const output = await releaseManager.rollback(releaseName, namespace, revision, proxyKubeconfig);
+ const output = rollback(releaseName, namespace, revision, proxyKubeconfig);
return { message: output };
}
diff --git a/src/main/index.ts b/src/main/index.ts
index 2ba462ce0c..000678b617 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -15,15 +15,15 @@ import { getFreePort } from "./port";
import { mangleProxyEnv } from "./proxy-env";
import { registerFileProtocol } from "../common/register-protocol";
import logger from "./logger";
-import { clusterStore } from "../common/cluster-store";
-import { userStore } from "../common/user-store";
+import { ClusterStore } from "../common/cluster-store";
+import { UserStore } from "../common/user-store";
import { appEventBus } from "../common/event-bus";
-import { extensionLoader } from "../extensions/extension-loader";
-import { extensionsStore } from "../extensions/extensions-store";
-import { InstalledExtension, extensionDiscovery } from "../extensions/extension-discovery";
+import { ExtensionLoader } from "../extensions/extension-loader";
+import { ExtensionsStore } from "../extensions/extensions-store";
+import { InstalledExtension, ExtensionDiscovery } from "../extensions/extension-discovery";
import type { LensExtensionId } from "../extensions/lens-extension";
+import { FilesystemProvisionerStore } from "./extension-filesystem";
import { installDeveloperTools } from "./developer-tools";
-import { filesystemProvisionerStore } from "./extension-filesystem";
import { LensProtocolRouterMain } from "./protocol-handler";
import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
import { bindBroadcastHandlers } from "../common/ipc";
@@ -31,13 +31,9 @@ import { startUpdateChecking } from "./app-updater";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import { CatalogPusher } from "./catalog-pusher";
import { catalogEntityRegistry } from "../common/catalog-entity-registry";
-import { hotbarStore } from "../common/hotbar-store";
+import { HotbarStore } from "../common/hotbar-store";
const workingDir = path.join(app.getPath("appData"), appName);
-let proxyPort: number;
-let proxyServer: LensProxy;
-let clusterManager: ClusterManager;
-let windowManager: WindowManager;
app.setName(appName);
@@ -66,7 +62,7 @@ if (app.commandLine.getSwitchValue("proxy-server") !== "") {
if (!app.requestSingleInstanceLock()) {
app.exit();
} else {
- const lprm = LensProtocolRouterMain.getInstance();
+ const lprm = LensProtocolRouterMain.getInstanceOrCreate();
for (const arg of process.argv) {
if (arg.toLowerCase().startsWith("lens://")) {
@@ -77,7 +73,7 @@ if (!app.requestSingleInstanceLock()) {
}
app.on("second-instance", (event, argv) => {
- const lprm = LensProtocolRouterMain.getInstance();
+ const lprm = LensProtocolRouterMain.getInstanceOrCreate();
for (const arg of argv) {
if (arg.toLowerCase().startsWith("lens://")) {
@@ -86,7 +82,7 @@ app.on("second-instance", (event, argv) => {
}
}
- windowManager?.ensureMainWindow();
+ WindowManager.getInstance(false)?.ensureMainWindow();
});
app.on("ready", async () => {
@@ -102,7 +98,11 @@ app.on("ready", async () => {
registerFileProtocol("static", __static);
- await installDeveloperTools();
+ const userStore = UserStore.getInstanceOrCreate();
+ const clusterStore = ClusterStore.getInstanceOrCreate();
+ const hotbarStore = HotbarStore.getInstanceOrCreate();
+ const extensionsStore = ExtensionsStore.getInstanceOrCreate();
+ const filesystemStore = FilesystemProvisionerStore.getInstanceOrCreate();
logger.info("💾 Loading stores");
// preload
@@ -111,10 +111,12 @@ app.on("ready", async () => {
clusterStore.load(),
hotbarStore.load(),
extensionsStore.load(),
- filesystemProvisionerStore.load(),
+ filesystemStore.load(),
]);
// find free port
+ let proxyPort;
+
try {
logger.info("🔑 Getting free port for LensProxy server");
proxyPort = await getFreePort();
@@ -125,13 +127,13 @@ app.on("ready", async () => {
}
// create cluster manager
- clusterManager = ClusterManager.getInstance(proxyPort);
+ ClusterManager.getInstanceOrCreate(proxyPort);
// run proxy
try {
logger.info("🔌 Starting LensProxy");
// eslint-disable-next-line unused-imports/no-unused-vars-ts
- proxyServer = LensProxy.create(proxyPort, clusterManager);
+ LensProxy.getInstanceOrCreate(proxyPort).listen();
} catch (error) {
logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error?.message}`);
dialog.showErrorBox("Lens Error", `Could not start proxy (127.0.0:${proxyPort}): ${error?.message || "unknown error"}`);
@@ -151,7 +153,9 @@ app.on("ready", async () => {
logger.error("Checking proxy server connection failed", error);
}
- extensionLoader.init();
+ const extensionDiscovery = ExtensionDiscovery.getInstanceOrCreate();
+
+ ExtensionLoader.getInstanceOrCreate().init();
extensionDiscovery.init();
// Start the app without showing the main window when auto starting on login
@@ -159,7 +163,9 @@ app.on("ready", async () => {
const startHidden = process.argv.includes("--hidden") || (isMac && app.getLoginItemSettings().wasOpenedAsHidden);
logger.info("🖥️ Starting WindowManager");
- windowManager = WindowManager.getInstance(proxyPort);
+ const windowManager = WindowManager.getInstanceOrCreate(proxyPort);
+
+ installDeveloperTools();
if (!startHidden) {
windowManager.initMainWindow();
@@ -169,13 +175,13 @@ app.on("ready", async () => {
CatalogPusher.init(catalogEntityRegistry);
startUpdateChecking();
LensProtocolRouterMain
- .getInstance()
+ .getInstance()
.rendererLoaded = true;
});
- extensionLoader.whenLoaded.then(() => {
+ ExtensionLoader.getInstance().whenLoaded.then(() => {
LensProtocolRouterMain
- .getInstance()
+ .getInstance()
.extensionsLoaded = true;
});
@@ -189,14 +195,15 @@ app.on("ready", async () => {
extensionDiscovery.watchExtensions();
// Subscribe to extensions that are copied or deleted to/from the extensions folder
- extensionDiscovery.events.on("add", (extension: InstalledExtension) => {
- extensionLoader.addExtension(extension);
- });
- extensionDiscovery.events.on("remove", (lensExtensionId: LensExtensionId) => {
- extensionLoader.removeExtension(lensExtensionId);
- });
+ extensionDiscovery.events
+ .on("add", (extension: InstalledExtension) => {
+ ExtensionLoader.getInstance().addExtension(extension);
+ })
+ .on("remove", (lensExtensionId: LensExtensionId) => {
+ ExtensionLoader.getInstance().removeExtension(lensExtensionId);
+ });
- extensionLoader.initExtensions(extensions);
+ ExtensionLoader.getInstance().initExtensions(extensions);
} catch (error) {
dialog.showErrorBox("Lens Error", `Could not load extensions${error?.message ? `: ${error.message}` : ""}`);
console.error(error);
@@ -212,7 +219,7 @@ app.on("activate", (event, hasVisibleWindows) => {
logger.info("APP:ACTIVATE", { hasVisibleWindows });
if (!hasVisibleWindows) {
- windowManager?.initMainWindow(false);
+ WindowManager.getInstance(false)?.initMainWindow(false);
}
});
@@ -227,8 +234,7 @@ app.on("will-quit", (event) => {
// Quit app on Cmd+Q (MacOS)
logger.info("APP:QUIT");
appEventBus.emit({name: "app", action: "close"});
-
- clusterManager?.stop(); // close cluster connections
+ ClusterManager.getInstance(false)?.stop(); // close cluster connections
if (blockQuit) {
event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.)
@@ -242,7 +248,7 @@ app.on("open-url", (event, rawUrl) => {
event.preventDefault();
LensProtocolRouterMain
- .getInstance()
+ .getInstance()
.route(rawUrl)
.catch(error => logger.error(`${LensProtocolRouterMain.LoggingPrefix}: an error occured`, { error, rawUrl }));
});
diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts
index 7e0d6ed5c7..903f54ff07 100644
--- a/src/main/kubectl.ts
+++ b/src/main/kubectl.ts
@@ -6,7 +6,7 @@ import logger from "./logger";
import { ensureDir, pathExists } from "fs-extra";
import * as lockFile from "proper-lockfile";
import { helmCli } from "./helm/helm-cli";
-import { userStore } from "../common/user-store";
+import { UserStore } from "../common/user-store";
import { customRequest } from "../common/request";
import { getBundledKubectlVersion } from "../common/utils/app-version";
import { isDevelopment, isWindows, isTestEnv } from "../common/vars";
@@ -113,12 +113,12 @@ export class Kubectl {
}
public getPathFromPreferences() {
- return userStore.preferences?.kubectlBinariesPath || this.getBundledPath();
+ return UserStore.getInstance().preferences?.kubectlBinariesPath || this.getBundledPath();
}
protected getDownloadDir() {
- if (userStore.preferences?.downloadBinariesPath) {
- return path.join(userStore.preferences.downloadBinariesPath, "kubectl");
+ if (UserStore.getInstance().preferences?.downloadBinariesPath) {
+ return path.join(UserStore.getInstance().preferences.downloadBinariesPath, "kubectl");
}
return Kubectl.kubectlDir;
@@ -129,7 +129,7 @@ export class Kubectl {
return this.getBundledPath();
}
- if (userStore.preferences?.downloadKubectlBinaries === false) {
+ if (UserStore.getInstance().preferences?.downloadKubectlBinaries === false) {
return this.getPathFromPreferences();
}
@@ -223,7 +223,7 @@ export class Kubectl {
}
public async ensureKubectl(): Promise {
- if (userStore.preferences?.downloadKubectlBinaries === false) {
+ if (UserStore.getInstance().preferences?.downloadKubectlBinaries === false) {
return true;
}
@@ -273,7 +273,7 @@ export class Kubectl {
logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
- return new Promise((resolve, reject) => {
+ return new Promise((resolve, reject) => {
const stream = customRequest({
url: this.url,
gzip: true,
@@ -303,7 +303,7 @@ export class Kubectl {
}
protected async writeInitScripts() {
- const kubectlPath = userStore.preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences());
+ const kubectlPath = UserStore.getInstance().preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences());
const helmPath = helmCli.getBinaryDir();
const fsPromises = fs.promises;
const bashScriptPath = path.join(this.dirname, ".bash_set_path");
@@ -361,7 +361,7 @@ export class Kubectl {
}
protected getDownloadMirror() {
- const mirror = packageMirrors.get(userStore.preferences?.downloadMirror);
+ const mirror = packageMirrors.get(UserStore.getInstance().preferences?.downloadMirror);
if (mirror) {
return mirror;
diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts
index 98bdd97f54..aa92f4b90e 100644
--- a/src/main/lens-proxy.ts
+++ b/src/main/lens-proxy.ts
@@ -6,23 +6,22 @@ import url from "url";
import * as WebSocket from "ws";
import { apiPrefix, apiKubePrefix } from "../common/vars";
import { Router } from "./router";
-import { ClusterManager } from "./cluster-manager";
import { ContextHandler } from "./context-handler";
import logger from "./logger";
import { NodeShellSession, LocalShellSession } from "./shell-session";
+import { Singleton } from "../common/utils";
+import { ClusterManager } from "./cluster-manager";
-export class LensProxy {
+export class LensProxy extends Singleton {
protected origin: string;
protected proxyServer: http.Server;
protected router: Router;
protected closed = false;
protected retryCounters = new Map();
- static create(port: number, clusterManager: ClusterManager) {
- return new LensProxy(port, clusterManager).listen();
- }
+ constructor(protected port: number) {
+ super();
- private constructor(protected port: number, protected clusterManager: ClusterManager) {
this.origin = `http://localhost:${port}`;
this.router = new Router();
}
@@ -66,7 +65,7 @@ export class LensProxy {
}
protected async handleProxyUpgrade(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
- const cluster = this.clusterManager.getClusterForRequest(req);
+ const cluster = ClusterManager.getInstance().getClusterForRequest(req);
if (cluster) {
const proxyUrl = await cluster.contextHandler.resolveAuthProxyUrl() + req.url.replace(apiKubePrefix, "");
@@ -171,7 +170,7 @@ export class LensProxy {
const ws = new WebSocket.Server({ noServer: true });
return ws.on("connection", ((socket: WebSocket, req: http.IncomingMessage) => {
- const cluster = this.clusterManager.getClusterForRequest(req);
+ const cluster = ClusterManager.getInstance().getClusterForRequest(req);
const nodeParam = url.parse(req.url, true).query["node"]?.toString();
const shell = nodeParam
? new NodeShellSession(socket, cluster, nodeParam)
@@ -197,7 +196,7 @@ export class LensProxy {
}
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) {
- const cluster = this.clusterManager.getClusterForRequest(req);
+ const cluster = ClusterManager.getInstance().getClusterForRequest(req);
if (cluster) {
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
diff --git a/src/main/protocol-handler/__test__/router.test.ts b/src/main/protocol-handler/__test__/router.test.ts
index 6b3f668079..88cc4fec44 100644
--- a/src/main/protocol-handler/__test__/router.test.ts
+++ b/src/main/protocol-handler/__test__/router.test.ts
@@ -1,7 +1,7 @@
import { LensProtocolRouterMain } from "../router";
import { noop } from "../../../common/utils";
-import { extensionsStore } from "../../../extensions/extensions-store";
-import { extensionLoader } from "../../../extensions/extension-loader";
+import { ExtensionsStore } from "../../../extensions/extensions-store";
+import { ExtensionLoader } from "../../../extensions/extension-loader";
import * as uuid from "uuid";
import { LensMainExtension } from "../../../extensions/core-api";
import { broadcastMessage } from "../../../common/ipc";
@@ -16,20 +16,28 @@ function throwIfDefined(val: any): void {
}
describe("protocol router tests", () => {
- let lpr: LensProtocolRouterMain;
-
beforeEach(() => {
- jest.clearAllMocks();
- (extensionsStore as any).state.clear();
- (extensionLoader as any).instances.clear();
- LensProtocolRouterMain.resetInstance();
- lpr = LensProtocolRouterMain.getInstance();
+ ExtensionsStore.getInstanceOrCreate();
+ ExtensionLoader.getInstanceOrCreate();
+
+ const lpr = LensProtocolRouterMain.getInstanceOrCreate();
+
lpr.extensionsLoaded = true;
lpr.rendererLoaded = true;
});
+ afterEach(() => {
+ jest.clearAllMocks();
+
+ ExtensionsStore.resetInstance();
+ ExtensionLoader.resetInstance();
+ LensProtocolRouterMain.resetInstance();
+ });
+
it("should throw on non-lens URLS", async () => {
try {
+ const lpr = LensProtocolRouterMain.getInstance();
+
expect(await lpr.route("https://google.ca")).toBeUndefined();
} catch (error) {
expect(error).toBeInstanceOf(Error);
@@ -38,6 +46,8 @@ describe("protocol router tests", () => {
it("should throw when host not internal or extension", async () => {
try {
+ const lpr = LensProtocolRouterMain.getInstance();
+
expect(await lpr.route("lens://foobar")).toBeUndefined();
} catch (error) {
expect(error).toBeInstanceOf(Error);
@@ -57,14 +67,15 @@ describe("protocol router tests", () => {
isEnabled: true,
absolutePath: "/foo/bar",
});
+ const lpr = LensProtocolRouterMain.getInstance();
ext.protocolHandlers.push({
pathSchema: "/",
handler: noop,
});
- (extensionLoader as any).instances.set(extId, ext);
- (extensionsStore as any).state.set(extId, { enabled: true, name: "@mirantis/minikube" });
+ (ExtensionLoader.getInstance() as any).instances.set(extId, ext);
+ (ExtensionsStore.getInstance() as any).state.set(extId, { enabled: true, name: "@mirantis/minikube" });
lpr.addInternalHandler("/", noop);
@@ -86,6 +97,7 @@ describe("protocol router tests", () => {
});
it("should call handler if matches", async () => {
+ const lpr = LensProtocolRouterMain.getInstance();
let called = false;
lpr.addInternalHandler("/page", () => { called = true; });
@@ -101,6 +113,7 @@ describe("protocol router tests", () => {
});
it("should call most exact handler", async () => {
+ const lpr = LensProtocolRouterMain.getInstance();
let called: any = 0;
lpr.addInternalHandler("/page", () => { called = 1; });
@@ -119,6 +132,7 @@ describe("protocol router tests", () => {
it("should call most exact handler for an extension", async () => {
let called: any = 0;
+ const lpr = LensProtocolRouterMain.getInstance();
const extId = uuid.v4();
const ext = new LensMainExtension({
id: extId,
@@ -141,8 +155,8 @@ describe("protocol router tests", () => {
handler: params => { called = params.pathname.id; },
});
- (extensionLoader as any).instances.set(extId, ext);
- (extensionsStore as any).state.set(extId, { enabled: true, name: "@foobar/icecream" });
+ (ExtensionLoader.getInstance() as any).instances.set(extId, ext);
+ (ExtensionsStore.getInstance() as any).state.set(extId, { enabled: true, name: "@foobar/icecream" });
try {
expect(await lpr.route("lens://extension/@foobar/icecream/page/foob")).toBeUndefined();
@@ -155,6 +169,7 @@ describe("protocol router tests", () => {
});
it("should work with non-org extensions", async () => {
+ const lpr = LensProtocolRouterMain.getInstance();
let called: any = 0;
{
@@ -177,8 +192,8 @@ describe("protocol router tests", () => {
handler: params => { called = params.pathname.id; },
});
- (extensionLoader as any).instances.set(extId, ext);
- (extensionsStore as any).state.set(extId, { enabled: true, name: "@foobar/icecream" });
+ (ExtensionLoader.getInstance() as any).instances.set(extId, ext);
+ (ExtensionsStore.getInstance() as any).state.set(extId, { enabled: true, name: "@foobar/icecream" });
}
{
@@ -201,12 +216,12 @@ describe("protocol router tests", () => {
handler: () => { called = 1; },
});
- (extensionLoader as any).instances.set(extId, ext);
- (extensionsStore as any).state.set(extId, { enabled: true, name: "icecream" });
+ (ExtensionLoader.getInstance() as any).instances.set(extId, ext);
+ (ExtensionsStore.getInstance() as any).state.set(extId, { enabled: true, name: "icecream" });
}
- (extensionsStore as any).state.set("@foobar/icecream", { enabled: true, name: "@foobar/icecream" });
- (extensionsStore as any).state.set("icecream", { enabled: true, name: "icecream" });
+ (ExtensionsStore.getInstance() as any).state.set("@foobar/icecream", { enabled: true, name: "@foobar/icecream" });
+ (ExtensionsStore.getInstance() as any).state.set("icecream", { enabled: true, name: "icecream" });
try {
expect(await lpr.route("lens://extension/icecream/page")).toBeUndefined();
@@ -219,10 +234,13 @@ describe("protocol router tests", () => {
});
it("should throw if urlSchema is invalid", () => {
+ const lpr = LensProtocolRouterMain.getInstance();
+
expect(() => lpr.addInternalHandler("/:@", noop)).toThrowError();
});
it("should call most exact handler with 3 found handlers", async () => {
+ const lpr = LensProtocolRouterMain.getInstance();
let called: any = 0;
lpr.addInternalHandler("/", () => { called = 2; });
@@ -241,6 +259,7 @@ describe("protocol router tests", () => {
});
it("should call most exact handler with 2 found handlers", async () => {
+ const lpr = LensProtocolRouterMain.getInstance();
let called: any = 0;
lpr.addInternalHandler("/", () => { called = 2; });
diff --git a/src/main/shell-session/local-shell-session.ts b/src/main/shell-session/local-shell-session.ts
index c131ea651c..c500ff169f 100644
--- a/src/main/shell-session/local-shell-session.ts
+++ b/src/main/shell-session/local-shell-session.ts
@@ -1,6 +1,6 @@
import path from "path";
import { helmCli } from "../helm/helm-cli";
-import { userStore } from "../../common/user-store";
+import { UserStore } from "../../common/user-store";
import { ShellSession } from "./shell-session";
export class LocalShellSession extends ShellSession {
@@ -21,8 +21,8 @@ export class LocalShellSession extends ShellSession {
protected async getShellArgs(shell: string): Promise {
const helmpath = helmCli.getBinaryDir();
- const pathFromPreferences = userStore.preferences.kubectlBinariesPath || this.kubectl.getBundledPath();
- const kubectlPathDir = userStore.preferences.downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
+ const pathFromPreferences = UserStore.getInstance().preferences.kubectlBinariesPath || this.kubectl.getBundledPath();
+ const kubectlPathDir = UserStore.getInstance().preferences.downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
switch(path.basename(shell)) {
case "powershell.exe":
diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts
index eebaed0605..b3710aab70 100644
--- a/src/main/shell-session/shell-session.ts
+++ b/src/main/shell-session/shell-session.ts
@@ -6,7 +6,7 @@ import { app } from "electron";
import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars";
import path from "path";
import { isWindows } from "../../common/vars";
-import { userStore } from "../../common/user-store";
+import { UserStore } from "../../common/user-store";
import * as pty from "node-pty";
import { appEventBus } from "../../common/event-bus";
@@ -119,7 +119,7 @@ export abstract class ShellSession {
protected async getShellEnv() {
const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv())));
const pathStr = [...this.getPathEntries(), await this.kubectlBinDirP, process.env.PATH].join(path.delimiter);
- const shell = userStore.preferences.shell || process.env.SHELL || process.env.PTYSHELL;
+ const shell = UserStore.getInstance().preferences.shell || process.env.SHELL || process.env.PTYSHELL;
delete env.DEBUG; // don't pass DEBUG into shells
diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts
index afd6b03670..ad3cc3a2f2 100644
--- a/src/main/window-manager.ts
+++ b/src/main/window-manager.ts
@@ -61,35 +61,34 @@ export class WindowManager extends Singleton {
this.windowState.manage(this.mainWindow);
// open external links in default browser (target=_blank, window.open)
- this.mainWindow.webContents.on("new-window", (event, url) => {
- event.preventDefault();
- shell.openExternal(url);
- });
- this.mainWindow.webContents.on("dom-ready", () => {
- appEventBus.emit({ name: "app", action: "dom-ready" });
- });
- this.mainWindow.on("focus", () => {
- appEventBus.emit({ name: "app", action: "focus" });
- });
- this.mainWindow.on("blur", () => {
- appEventBus.emit({ name: "app", action: "blur" });
- });
-
- // clean up
- this.mainWindow.on("closed", () => {
- this.windowState.unmanage();
- this.mainWindow = null;
- this.splashWindow = null;
- app.dock?.hide(); // hide icon in dock (mac-os)
- });
-
- this.mainWindow.webContents.on("did-fail-load", (_event, code, desc) => {
- logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, { code, desc });
- });
-
- this.mainWindow.webContents.on("did-finish-load", () => {
- logger.info("[WINDOW-MANAGER]: Main window loaded");
- });
+ this.mainWindow
+ .on("focus", () => {
+ appEventBus.emit({ name: "app", action: "focus" });
+ })
+ .on("blur", () => {
+ appEventBus.emit({ name: "app", action: "blur" });
+ })
+ .on("closed", () => {
+ // clean up
+ this.windowState.unmanage();
+ this.mainWindow = null;
+ this.splashWindow = null;
+ app.dock?.hide(); // hide icon in dock (mac-os)
+ })
+ .webContents
+ .on("new-window", (event, url) => {
+ event.preventDefault();
+ shell.openExternal(url);
+ })
+ .on("dom-ready", () => {
+ appEventBus.emit({ name: "app", action: "dom-ready" });
+ })
+ .on("did-fail-load", (_event, code, desc) => {
+ logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, { code, desc });
+ })
+ .on("did-finish-load", () => {
+ logger.info("[WINDOW-MANAGER]: Main window loaded");
+ });
}
try {
@@ -101,8 +100,9 @@ export class WindowManager extends Singleton {
setTimeout(() => {
appEventBus.emit({ name: "app", action: "start" });
}, 1000);
- } catch (err) {
- dialog.showErrorBox("ERROR!", err.toString());
+ } catch (error) {
+ logger.error("Showing main window failed", { error });
+ dialog.showErrorBox("ERROR!", error.toString());
}
}
diff --git a/src/migrations/hotbar-store/5.0.0-alpha.0.ts b/src/migrations/hotbar-store/5.0.0-alpha.0.ts
index 22087ab569..7f2866b193 100644
--- a/src/migrations/hotbar-store/5.0.0-alpha.0.ts
+++ b/src/migrations/hotbar-store/5.0.0-alpha.0.ts
@@ -1,6 +1,6 @@
// Cleans up a store that had the state related data stored
import { Hotbar } from "../../common/hotbar-store";
-import { clusterStore } from "../../common/cluster-store";
+import { ClusterStore } from "../../common/cluster-store";
import { migration } from "../migration-wrapper";
export default migration({
@@ -8,7 +8,7 @@ export default migration({
run(store) {
const hotbars: Hotbar[] = [];
- clusterStore.enabledClustersList.forEach((cluster: any) => {
+ ClusterStore.getInstance().enabledClustersList.forEach((cluster: any) => {
const name = cluster.workspace || "default";
let hotbar = hotbars.find((h) => h.name === name);
diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx
index c9ef607871..a6fbfdee40 100644
--- a/src/renderer/bootstrap.tsx
+++ b/src/renderer/bootstrap.tsx
@@ -6,19 +6,20 @@ import * as MobxReact from "mobx-react";
import * as ReactRouter from "react-router";
import * as ReactRouterDom from "react-router-dom";
import { render, unmountComponentAtNode } from "react-dom";
-import { clusterStore } from "../common/cluster-store";
-import { userStore } from "../common/user-store";
import { delay } from "../common/utils";
import { isMac, isDevelopment } from "../common/vars";
+import { HotbarStore } from "../common/hotbar-store";
+import { ClusterStore } from "../common/cluster-store";
+import { UserStore } from "../common/user-store";
import * as LensExtensions from "../extensions/extension-api";
-import { extensionDiscovery } from "../extensions/extension-discovery";
-import { extensionLoader } from "../extensions/extension-loader";
-import { extensionsStore } from "../extensions/extensions-store";
-import { hotbarStore } from "../common/hotbar-store";
-import { filesystemProvisionerStore } from "../main/extension-filesystem";
+import { ExtensionDiscovery } from "../extensions/extension-discovery";
+import { ExtensionLoader } from "../extensions/extension-loader";
+import { ExtensionsStore } from "../extensions/extensions-store";
+import { FilesystemProvisionerStore } from "../main/extension-filesystem";
import { App } from "./components/app";
import { LensApp } from "./lens-app";
-import { themeStore } from "./theme.store";
+import { ThemeStore } from "./theme.store";
+import { HelmRepoManager } from "../main/helm/helm-repo-manager";
/**
* If this is a development buid, wait a second to attach
@@ -50,8 +51,16 @@ export async function bootstrap(App: AppComponent) {
await attachChromeDebugger();
rootElem.classList.toggle("is-mac", isMac);
- extensionLoader.init();
- extensionDiscovery.init();
+ ExtensionLoader.getInstanceOrCreate().init();
+ ExtensionDiscovery.getInstanceOrCreate().init();
+
+ const userStore = UserStore.getInstanceOrCreate();
+ const clusterStore = ClusterStore.getInstanceOrCreate();
+ const extensionsStore = ExtensionsStore.getInstanceOrCreate();
+ const filesystemStore = FilesystemProvisionerStore.getInstanceOrCreate();
+ const themeStore = ThemeStore.getInstanceOrCreate();
+ const hotbarStore = HotbarStore.getInstanceOrCreate();
+ const helmRepoManager = HelmRepoManager.getInstanceOrCreate();
// preload common stores
await Promise.all([
@@ -59,8 +68,9 @@ export async function bootstrap(App: AppComponent) {
hotbarStore.load(),
clusterStore.load(),
extensionsStore.load(),
- filesystemProvisionerStore.load(),
+ filesystemStore.load(),
themeStore.init(),
+ helmRepoManager.init(),
]);
// Register additional store listeners
@@ -72,8 +82,8 @@ export async function bootstrap(App: AppComponent) {
}
window.addEventListener("message", (ev: MessageEvent) => {
if (ev.data === "teardown") {
- userStore.unregisterIpcListener();
- clusterStore.unregisterIpcListener();
+ UserStore.getInstance(false)?.unregisterIpcListener();
+ ClusterStore.getInstance(false)?.unregisterIpcListener();
unmountComponentAtNode(rootElem);
window.location.href = "about:blank";
}
diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx
index 11681f76fa..9a662636b0 100644
--- a/src/renderer/components/+add-cluster/add-cluster.tsx
+++ b/src/renderer/components/+add-cluster/add-cluster.tsx
@@ -11,10 +11,10 @@ import { AceEditor } from "../ace-editor";
import { Button } from "../button";
import { Icon } from "../icon";
import { kubeConfigDefaultPath, loadConfig, splitConfig, validateConfig, validateKubeConfig } from "../../../common/kube-helpers";
-import { ClusterModel, ClusterStore, clusterStore } from "../../../common/cluster-store";
+import { ClusterModel, ClusterStore } from "../../../common/cluster-store";
import { v4 as uuid } from "uuid";
import { navigate } from "../../navigation";
-import { userStore } from "../../../common/user-store";
+import { UserStore } from "../../../common/user-store";
import { cssNames } from "../../utils";
import { Notifications } from "../notifications";
import { Tab, Tabs } from "../tabs";
@@ -44,13 +44,13 @@ export class AddCluster extends React.Component {
@observable showSettings = false;
componentDidMount() {
- clusterStore.setActive(null);
- this.setKubeConfig(userStore.kubeConfigPath);
+ ClusterStore.getInstance().setActive(null);
+ this.setKubeConfig(UserStore.getInstance().kubeConfigPath);
appEventBus.emit({ name: "cluster-add", action: "start" });
}
componentWillUnmount() {
- userStore.markNewContextsAsSeen();
+ UserStore.getInstance().markNewContextsAsSeen();
}
@action
@@ -60,9 +60,9 @@ export class AddCluster extends React.Component {
validateConfig(this.kubeConfigLocal);
this.refreshContexts();
this.kubeConfigPath = filePath;
- userStore.kubeConfigPath = filePath; // save to store
+ UserStore.getInstance().kubeConfigPath = filePath; // save to store
} catch (err) {
- if (!userStore.isDefaultKubeConfigPath) {
+ if (!UserStore.getInstance().isDefaultKubeConfigPath) {
Notifications.error(
Can't setup {filePath} as kubeconfig: {String(err)}
);
@@ -181,7 +181,7 @@ export class AddCluster extends React.Component {
});
runInAction(() => {
- clusterStore.addClusters(...newClusters);
+ ClusterStore.getInstance().addClusters(...newClusters);
Notifications.ok(
<>Successfully imported {newClusters.length} cluster(s)>
@@ -308,7 +308,7 @@ export class AddCluster extends React.Component {
}
onKubeConfigInputBlur = () => {
- const isChanged = this.kubeConfigPath !== userStore.kubeConfigPath;
+ const isChanged = this.kubeConfigPath !== UserStore.getInstance().kubeConfigPath;
if (isChanged) {
this.kubeConfigPath = this.kubeConfigPath.replace("~", os.homedir());
@@ -316,7 +316,7 @@ export class AddCluster extends React.Component {
try {
this.setKubeConfig(this.kubeConfigPath, { throwError: true });
} catch (err) {
- this.setKubeConfig(userStore.kubeConfigPath); // revert to previous valid path
+ this.setKubeConfig(UserStore.getInstance().kubeConfigPath); // revert to previous valid path
}
}
};
@@ -328,7 +328,7 @@ export class AddCluster extends React.Component {
};
protected formatContextLabel = ({ value: context }: SelectOption) => {
- const isNew = userStore.newContexts.has(context);
+ const isNew = UserStore.getInstance().newContexts.has(context);
const isSelected = this.selectedContexts.includes(context);
return (
diff --git a/src/renderer/components/+apps-releases/release-details.tsx b/src/renderer/components/+apps-releases/release-details.tsx
index f6b8327151..e326df2dc3 100644
--- a/src/renderer/components/+apps-releases/release-details.tsx
+++ b/src/renderer/components/+apps-releases/release-details.tsx
@@ -19,7 +19,7 @@ import { Button } from "../button";
import { releaseStore } from "./release.store";
import { Notifications } from "../notifications";
import { createUpgradeChartTab } from "../dock/upgrade-chart.store";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { apiManager } from "../../api/api-manager";
import { SubTitle } from "../layout/sub-title";
import { secretsStore } from "../+config-secrets/secrets.store";
@@ -259,7 +259,7 @@ export class ReleaseDetails extends Component {
return (
{
sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }}
sortSyncWithUrl={false}
getTableRow={this.getTableRow}
- className={cssNames("box grow", themeStore.activeTheme.type)}
+ className={cssNames("box grow", ThemeStore.getInstance().activeTheme.type)}
>
Message
diff --git a/src/renderer/components/+cluster/cluster-overview.tsx b/src/renderer/components/+cluster/cluster-overview.tsx
index 23d3214340..6e6fd5d58e 100644
--- a/src/renderer/components/+cluster/cluster-overview.tsx
+++ b/src/renderer/components/+cluster/cluster-overview.tsx
@@ -5,7 +5,7 @@ import { reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { nodesStore } from "../+nodes/nodes.store";
import { podsStore } from "../+workloads-pods/pods.store";
-import { clusterStore, getHostedCluster } from "../../../common/cluster-store";
+import { ClusterStore, getHostedCluster } from "../../../common/cluster-store";
import { interval } from "../../utils";
import { TabLayout } from "../layout/tab-layout";
import { Spinner } from "../spinner";
@@ -66,7 +66,7 @@ export class ClusterOverview extends React.Component {
render() {
const isLoaded = nodesStore.isLoaded && podsStore.isLoaded;
- const isMetricsHidden = clusterStore.isMetricHidden(ResourceType.Cluster);
+ const isMetricsHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Cluster);
return (
diff --git a/src/renderer/components/+cluster/cluster-pie-charts.tsx b/src/renderer/components/+cluster/cluster-pie-charts.tsx
index d64efb6b36..e5e3bc6397 100644
--- a/src/renderer/components/+cluster/cluster-pie-charts.tsx
+++ b/src/renderer/components/+cluster/cluster-pie-charts.tsx
@@ -9,7 +9,7 @@ import { nodesStore } from "../+nodes/nodes.store";
import { ChartData, PieChart } from "../chart";
import { ClusterNoMetrics } from "./cluster-no-metrics";
import { bytesToUnits } from "../../utils";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { getMetricLastPoints } from "../../api/endpoints/metrics.api";
export const ClusterPieCharts = observer(() => {
@@ -29,7 +29,7 @@ export const ClusterPieCharts = observer(() => {
const { podUsage, podCapacity } = data;
const cpuLimitsOverload = cpuLimits > cpuCapacity;
const memoryLimitsOverload = memoryLimits > memoryCapacity;
- const defaultColor = themeStore.activeTheme.colors.pieChartDefaultColor;
+ const defaultColor = ThemeStore.getInstance().activeTheme.colors.pieChartDefaultColor;
if (!memoryCapacity || !cpuCapacity || !podCapacity) return null;
const cpuData: ChartData = {
diff --git a/src/renderer/components/+extensions/__tests__/extensions.test.tsx b/src/renderer/components/+extensions/__tests__/extensions.test.tsx
index 8899d9d74c..d51b1223f0 100644
--- a/src/renderer/components/+extensions/__tests__/extensions.test.tsx
+++ b/src/renderer/components/+extensions/__tests__/extensions.test.tsx
@@ -2,10 +2,12 @@ import "@testing-library/jest-dom/extend-expect";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import fse from "fs-extra";
import React from "react";
-import { extensionDiscovery } from "../../../../extensions/extension-discovery";
+import { UserStore } from "../../../../common/user-store";
+import { ExtensionDiscovery } from "../../../../extensions/extension-discovery";
+import { ExtensionLoader } from "../../../../extensions/extension-loader";
+import { ThemeStore } from "../../../theme.store";
import { ConfirmDialog } from "../../confirm-dialog";
import { Notifications } from "../../notifications";
-import { ExtensionStateStore } from "../extension-install.store";
import { Extensions } from "../extensions";
jest.mock("fs-extra");
@@ -18,46 +20,52 @@ jest.mock("../../../../common/utils", () => ({
extractTar: jest.fn(() => Promise.resolve())
}));
-jest.mock("../../../../extensions/extension-discovery", () => ({
- ...jest.requireActual("../../../../extensions/extension-discovery"),
- extensionDiscovery: {
- localFolderPath: "/fake/path",
- uninstallExtension: jest.fn(() => Promise.resolve()),
- isLoaded: true
- }
-}));
-
-jest.mock("../../../../extensions/extension-loader", () => ({
- ...jest.requireActual("../../../../extensions/extension-loader"),
- extensionLoader: {
- userExtensions: new Map([
- ["extensionId", {
- id: "extensionId",
- manifest: {
- name: "test",
- version: "1.2.3"
- },
- absolutePath: "/absolute/path",
- manifestPath: "/symlinked/path/package.json",
- isBundled: false,
- isEnabled: true
- }]
- ])
- }
-}));
-
jest.mock("../../notifications", () => ({
ok: jest.fn(),
error: jest.fn(),
info: jest.fn()
}));
+jest.mock("electron", () => {
+ return {
+ app: {
+ getVersion: () => "99.99.99",
+ getPath: () => "tmp",
+ getLocale: () => "en",
+ setLoginItemSettings: (): void => void 0,
+ }
+ };
+});
+
describe("Extensions", () => {
- beforeEach(() => {
- ExtensionStateStore.resetInstance();
+ beforeEach(async () => {
+ UserStore.resetInstance();
+ ThemeStore.resetInstance();
+
+ await UserStore.getInstanceOrCreate().load();
+ await ThemeStore.getInstanceOrCreate().init();
+
+ ExtensionLoader.resetInstance();
+ ExtensionDiscovery.resetInstance();
+ Extensions.installStates.clear();
+
+ ExtensionDiscovery.getInstanceOrCreate().uninstallExtension = jest.fn(() => Promise.resolve());
+
+ ExtensionLoader.getInstanceOrCreate().addExtension({
+ id: "extensionId",
+ manifest: {
+ name: "test",
+ version: "1.2.3"
+ },
+ absolutePath: "/absolute/path",
+ manifestPath: "/symlinked/path/package.json",
+ isBundled: false,
+ isEnabled: true
+ });
});
it("disables uninstall and disable buttons while uninstalling", async () => {
+ ExtensionDiscovery.getInstance().isLoaded = true;
render(<>>);
expect(screen.getByText("Disable").closest("button")).not.toBeDisabled();
@@ -68,13 +76,14 @@ describe("Extensions", () => {
// Approve confirm dialog
fireEvent.click(screen.getByText("Yes"));
- expect(extensionDiscovery.uninstallExtension).toHaveBeenCalled();
+ expect(ExtensionDiscovery.getInstance().uninstallExtension).toHaveBeenCalled();
expect(screen.getByText("Disable").closest("button")).toBeDisabled();
expect(screen.getByText("Uninstall").closest("button")).toBeDisabled();
});
it("displays error notification on uninstall error", () => {
- (extensionDiscovery.uninstallExtension as any).mockImplementationOnce(() =>
+ ExtensionDiscovery.getInstance().isLoaded = true;
+ (ExtensionDiscovery.getInstance().uninstallExtension as any).mockImplementationOnce(() =>
Promise.reject()
);
render(<>>);
@@ -115,14 +124,14 @@ describe("Extensions", () => {
});
it("displays spinner while extensions are loading", () => {
- extensionDiscovery.isLoaded = false;
+ ExtensionDiscovery.getInstance().isLoaded = false;
const { container } = render();
expect(container.querySelector(".Spinner")).toBeInTheDocument();
- extensionDiscovery.isLoaded = true;
+ ExtensionDiscovery.getInstance().isLoaded = true;
- waitFor(() =>
+ waitFor(() =>
expect(container.querySelector(".Spinner")).not.toBeInTheDocument()
);
});
diff --git a/src/renderer/components/+extensions/extension-install.store.ts b/src/renderer/components/+extensions/extension-install.store.ts
deleted file mode 100644
index c4a8ed6690..0000000000
--- a/src/renderer/components/+extensions/extension-install.store.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { observable } from "mobx";
-import { autobind, Singleton } from "../../utils";
-
-interface ExtensionState {
- displayName: string;
- // Possible states the extension can be
- state: "installing" | "uninstalling";
-}
-
-@autobind()
-export class ExtensionStateStore extends Singleton {
- extensionState = observable.map();
-}
diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx
index 9b69689f76..b2202a6050 100644
--- a/src/renderer/components/+extensions/extensions.tsx
+++ b/src/renderer/components/+extensions/extensions.tsx
@@ -7,8 +7,8 @@ import path from "path";
import React from "react";
import { downloadFile, extractTar, listTarEntries, readFileFromTar } from "../../../common/utils";
import { docsUrl } from "../../../common/vars";
-import { extensionDiscovery, InstalledExtension, manifestFilename } from "../../../extensions/extension-discovery";
-import { extensionLoader } from "../../../extensions/extension-loader";
+import { ExtensionDiscovery, InstalledExtension, manifestFilename } from "../../../extensions/extension-discovery";
+import { ExtensionLoader } from "../../../extensions/extension-loader";
import { extensionDisplayName, LensExtensionManifest, sanitizeExtensionName } from "../../../extensions/lens-extension";
import logger from "../../../main/logger";
import { prevDefault } from "../../utils";
@@ -21,7 +21,6 @@ import { SubTitle } from "../layout/sub-title";
import { Notifications } from "../notifications";
import { Spinner } from "../spinner/spinner";
import { TooltipPosition } from "../tooltip";
-import { ExtensionStateStore } from "./extension-install.store";
import "./extensions.scss";
interface InstallRequest {
@@ -39,6 +38,12 @@ interface InstallRequestValidated extends InstallRequestPreloaded {
tempFile: string; // temp system path to packed extension for unpacking
}
+interface ExtensionState {
+ displayName: string;
+ // Possible states the extension can be
+ state: "installing" | "uninstalling";
+}
+
@observer
export class Extensions extends React.Component {
private static supportedFormats = ["tar", "tgz"];
@@ -50,9 +55,7 @@ export class Extensions extends React.Component {
}
};
- get extensionStateStore() {
- return ExtensionStateStore.getInstance();
- }
+ static installStates = observable.map();
@observable search = "";
@observable installPath = "";
@@ -64,7 +67,7 @@ export class Extensions extends React.Component {
* Extensions that were removed from extensions but are still in "uninstalling" state
*/
@computed get removedUninstalling() {
- return Array.from(this.extensionStateStore.extensionState.entries())
+ return Array.from(Extensions.installStates.entries())
.filter(([id, extension]) =>
extension.state === "uninstalling"
&& !this.extensions.find(extension => extension.id === id)
@@ -76,7 +79,7 @@ export class Extensions extends React.Component {
* Extensions that were added to extensions but are still in "installing" state
*/
@computed get addedInstalling() {
- return Array.from(this.extensionStateStore.extensionState.entries())
+ return Array.from(Extensions.installStates.entries())
.filter(([id, extension]) =>
extension.state === "installing"
&& this.extensions.find(extension => extension.id === id)
@@ -91,7 +94,7 @@ export class Extensions extends React.Component {
Notifications.ok(
Extension {displayName} successfully uninstalled!
);
- this.extensionStateStore.extensionState.delete(id);
+ Extensions.installStates.delete(id);
});
this.addedInstalling.forEach(({ id, displayName }) => {
@@ -104,7 +107,7 @@ export class Extensions extends React.Component {
Notifications.ok(
Extension {displayName} successfully installed!
);
- this.extensionStateStore.extensionState.delete(id);
+ Extensions.installStates.delete(id);
this.installPath = "";
// Enable installed extensions by default.
@@ -117,7 +120,7 @@ export class Extensions extends React.Component {
@computed get extensions() {
const searchText = this.search.toLowerCase();
- return Array.from(extensionLoader.userExtensions.values())
+ return Array.from(ExtensionLoader.getInstance().userExtensions.values())
.filter(({ manifest: { name, description }}) => (
name.toLowerCase().includes(searchText)
|| description?.toLowerCase().includes(searchText)
@@ -125,7 +128,7 @@ export class Extensions extends React.Component {
}
get extensionsPath() {
- return extensionDiscovery.localFolderPath;
+ return ExtensionDiscovery.getInstance().localFolderPath;
}
getExtensionPackageTemp(fileName = "") {
@@ -342,11 +345,11 @@ export class Extensions extends React.Component {
async unpackExtension({ fileName, tempFile, manifest: { name, version } }: InstallRequestValidated) {
const displayName = extensionDisplayName(name, version);
- const extensionId = path.join(extensionDiscovery.nodeModulesPath, name, "package.json");
+ const extensionId = path.join(ExtensionDiscovery.getInstance().nodeModulesPath, name, "package.json");
logger.info(`Unpacking extension ${displayName}`, { fileName, tempFile });
- this.extensionStateStore.extensionState.set(extensionId, {
+ Extensions.installStates.set(extensionId, {
state: "installing",
displayName
});
@@ -381,8 +384,8 @@ export class Extensions extends React.Component {
);
// Remove install state on install failure
- if (this.extensionStateStore.extensionState.get(extensionId)?.state === "installing") {
- this.extensionStateStore.extensionState.delete(extensionId);
+ if (Extensions.installStates.get(extensionId)?.state === "installing") {
+ Extensions.installStates.delete(extensionId);
}
} finally {
// clean up
@@ -406,20 +409,20 @@ export class Extensions extends React.Component {
const displayName = extensionDisplayName(extension.manifest.name, extension.manifest.version);
try {
- this.extensionStateStore.extensionState.set(extension.id, {
+ Extensions.installStates.set(extension.id, {
state: "uninstalling",
displayName
});
- await extensionDiscovery.uninstallExtension(extension);
+ await ExtensionDiscovery.getInstance().uninstallExtension(extension);
} catch (error) {
Notifications.error(
Uninstalling extension {displayName} has failed: {error?.message ?? ""}
);
// Remove uninstall state on uninstall failure
- if (this.extensionStateStore.extensionState.get(extension.id)?.state === "uninstalling") {
- this.extensionStateStore.extensionState.delete(extension.id);
+ if (Extensions.installStates.get(extension.id)?.state === "uninstalling") {
+ Extensions.installStates.delete(extension.id);
}
}
}
@@ -445,7 +448,7 @@ export class Extensions extends React.Component {
return extensions.map(extension => {
const { id, isEnabled, manifest } = extension;
const { name, description, version } = manifest;
- const isUninstalling = this.extensionStateStore.extensionState.get(id)?.state === "uninstalling";
+ const isUninstalling = Extensions.installStates.get(id)?.state === "uninstalling";
return (
@@ -478,7 +481,7 @@ export class Extensions extends React.Component {
* True if at least one extension is in installing state
*/
@computed get isInstalling() {
- return [...this.extensionStateStore.extensionState.values()].some(extension => extension.state === "installing");
+ return [...Extensions.installStates.values()].some(extension => extension.state === "installing");
}
render() {
@@ -536,7 +539,11 @@ export class Extensions extends React.Component {
value={this.search}
onChange={(value) => this.search = value}
/>
- {extensionDiscovery.isLoaded ? this.renderExtensions() :
}
+ {
+ ExtensionDiscovery.getInstance().isLoaded
+ ? this.renderExtensions()
+ :
+ }
diff --git a/src/renderer/components/+network-ingresses/ingress-details.tsx b/src/renderer/components/+network-ingresses/ingress-details.tsx
index 244738a537..6af1fa582f 100644
--- a/src/renderer/components/+network-ingresses/ingress-details.tsx
+++ b/src/renderer/components/+network-ingresses/ingress-details.tsx
@@ -15,7 +15,7 @@ import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { getBackendServiceNamePort } from "../../api/endpoints/ingress.api";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps {
}
@@ -101,6 +101,7 @@ export class IngressDetails extends React.Component {
if (!ingress) {
return null;
}
+
const { spec, status } = ingress;
const ingressPoints = status?.loadBalancer?.ingress;
const { metrics } = ingressStore;
@@ -108,8 +109,7 @@ export class IngressDetails extends React.Component {
"Network",
"Duration",
];
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.Ingress);
-
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Ingress);
const { serviceName, servicePort } = ingress.getServiceNamePort();
return (
diff --git a/src/renderer/components/+nodes/node-charts.tsx b/src/renderer/components/+nodes/node-charts.tsx
index d6e048b910..382917b61b 100644
--- a/src/renderer/components/+nodes/node-charts.tsx
+++ b/src/renderer/components/+nodes/node-charts.tsx
@@ -6,7 +6,7 @@ import { NoMetrics } from "../resource-metrics/no-metrics";
import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics";
import { observer } from "mobx-react";
import { ChartOptions, ChartPoint } from "chart.js";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { mapValues } from "lodash";
type IContext = IResourceMetricsValue;
@@ -14,7 +14,7 @@ type IContext = IResourceMetricsValue;
export const NodeCharts = observer(() => {
const { params: { metrics }, tabId, object } = useContext(ResourceMetricsContext);
const id = object.getId();
- const { chartCapacityColor } = themeStore.activeTheme.colors;
+ const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
if (!metrics) {
return null;
diff --git a/src/renderer/components/+nodes/node-details.tsx b/src/renderer/components/+nodes/node-details.tsx
index 349bf59051..53bedafde2 100644
--- a/src/renderer/components/+nodes/node-details.tsx
+++ b/src/renderer/components/+nodes/node-details.tsx
@@ -18,7 +18,7 @@ import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { KubeEventDetails } from "../+events/kube-event-details";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps {
}
@@ -54,7 +54,7 @@ export class NodeDetails extends React.Component {
"Disk",
"Pods",
];
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.Node);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Node);
return (
diff --git a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx
index a0ede38ece..ae2a8f444c 100644
--- a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx
+++ b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx
@@ -13,7 +13,7 @@ import { systemName, isUrl, isPath } from "../input/input_validators";
import { SubTitle } from "../layout/sub-title";
import { Icon } from "../icon";
import { Notifications } from "../notifications";
-import { HelmRepo, repoManager } from "../../../main/helm/helm-repo-manager";
+import { HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
interface Props extends Partial
{
onAddRepo: Function
@@ -79,7 +79,7 @@ export class AddHelmRepoDialog extends React.Component {
async addCustomRepo() {
try {
- await repoManager.addСustomRepo(this.helmRepo);
+ await HelmRepoManager.getInstance().addСustomRepo(this.helmRepo);
Notifications.ok(<>Helm repository {this.helmRepo.name} has added>);
this.props.onAddRepo();
this.close();
diff --git a/src/renderer/components/+preferences/helm-charts.tsx b/src/renderer/components/+preferences/helm-charts.tsx
index 1b0d80bf26..caccf271be 100644
--- a/src/renderer/components/+preferences/helm-charts.tsx
+++ b/src/renderer/components/+preferences/helm-charts.tsx
@@ -3,7 +3,7 @@ import "./helm-charts.scss";
import React from "react";
import { action, computed, observable } from "mobx";
-import { HelmRepo, repoManager } from "../../../main/helm/helm-repo-manager";
+import { HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
import { Button } from "../button";
import { Icon } from "../icon";
import { Notifications } from "../notifications";
@@ -34,9 +34,9 @@ export class HelmCharts extends React.Component {
try {
if (!this.repos.length) {
- this.repos = await repoManager.loadAvailableRepos(); // via https://helm.sh
+ this.repos = await HelmRepoManager.getInstance().loadAvailableRepos(); // via https://helm.sh
}
- const repos = await repoManager.repositories(); // via helm-cli
+ const repos = await HelmRepoManager.getInstance().repositories(); // via helm-cli
this.addedRepos.clear();
repos.forEach(repo => this.addedRepos.set(repo.name, repo));
@@ -49,7 +49,7 @@ export class HelmCharts extends React.Component {
async addRepo(repo: HelmRepo) {
try {
- await repoManager.addRepo(repo);
+ await HelmRepoManager.getInstance().addRepo(repo);
this.addedRepos.set(repo.name, repo);
} catch (err) {
Notifications.error(<>Adding helm branch {repo.name} has failed: {String(err)}>);
@@ -58,7 +58,7 @@ export class HelmCharts extends React.Component {
async removeRepo(repo: HelmRepo) {
try {
- await repoManager.removeRepo(repo);
+ await HelmRepoManager.getInstance().removeRepo(repo);
this.addedRepos.delete(repo.name);
} catch (err) {
Notifications.error(
diff --git a/src/renderer/components/+preferences/kubectl-binaries.tsx b/src/renderer/components/+preferences/kubectl-binaries.tsx
index 0eace69c10..eb1dfca1c5 100644
--- a/src/renderer/components/+preferences/kubectl-binaries.tsx
+++ b/src/renderer/components/+preferences/kubectl-binaries.tsx
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Input, InputValidators } from "../input";
import { SubTitle } from "../layout/sub-title";
-import { UserPreferences, userStore } from "../../../common/user-store";
+import { getDefaultKubectlPath, UserPreferences } from "../../../common/user-store";
import { observer } from "mobx-react";
import { bundledKubectlPath } from "../../../main/kubectl";
import { SelectOption, Select } from "../select";
@@ -59,7 +59,7 @@ export const KubectlBinaries = observer(({ preferences }: { preferences: UserPre
[] {
- return themeStore.themes.map(theme => ({
+ return ThemeStore.getInstance().themes.map(theme => ({
label: theme.name,
value: theme.id,
}));
@@ -98,18 +98,16 @@ export class Preferences extends React.Component {
}
render() {
- const { preferences } = userStore;
const extensions = appPreferenceRegistry.getItems();
const telemetryExtensions = extensions.filter(e => e.showInPreferencesTab == Pages.Telemetry);
- let defaultShell = process.env.SHELL || process.env.PTYSHELL;
-
- if (!defaultShell) {
- if (isWindows) {
- defaultShell = "powershell.exe";
- } else {
- defaultShell = "System default shell";
- }
- }
+ const { preferences } = UserStore.getInstance();
+ const defaultShell = process.env.SHELL
+ || process.env.PTYSHELL
+ || (
+ isWindows
+ ? "powershell.exe"
+ : "System default shell"
+ );
return (
userStore.setLocaleTimezone(value)}
+ onChange={({ value }: SelectOption) => UserStore.getInstance().setLocaleTimezone(value)}
themeName="lens"
/>
diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx
index 99642249ab..61380f1e46 100644
--- a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx
+++ b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx
@@ -15,7 +15,7 @@ import { getDetailsUrl, KubeObjectDetailsProps, KubeObjectMeta } from "../kube-o
import { PersistentVolumeClaim } from "../../api/endpoints";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps {
}
@@ -43,7 +43,7 @@ export class PersistentVolumeClaimDetails extends React.Component {
const metricTabs = [
"Disk"
];
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.VolumeClaim);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.VolumeClaim);
return (
diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx
index 11f165622c..4bc12a9200 100644
--- a/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx
+++ b/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx
@@ -5,13 +5,13 @@ import { BarChart, ChartDataSets, memoryOptions } from "../chart";
import { isMetricsEmpty, normalizeMetrics } from "../../api/endpoints/metrics.api";
import { NoMetrics } from "../resource-metrics/no-metrics";
import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
type IContext = IResourceMetricsValue
;
export const VolumeClaimDiskChart = observer(() => {
const { params: { metrics }, object } = useContext(ResourceMetricsContext);
- const { chartCapacityColor } = themeStore.activeTheme.colors;
+ const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
const id = object.getId();
if (!metrics) return null;
diff --git a/src/renderer/components/+whats-new/whats-new.tsx b/src/renderer/components/+whats-new/whats-new.tsx
index aa132cc178..95542195bb 100644
--- a/src/renderer/components/+whats-new/whats-new.tsx
+++ b/src/renderer/components/+whats-new/whats-new.tsx
@@ -3,7 +3,7 @@ import fs from "fs";
import path from "path";
import React from "react";
import { observer } from "mobx-react";
-import { userStore } from "../../../common/user-store";
+import { UserStore } from "../../../common/user-store";
import { navigate } from "../../navigation";
import { Button } from "../button";
import marked from "marked";
@@ -14,7 +14,7 @@ export class WhatsNew extends React.Component {
ok = () => {
navigate("/");
- userStore.saveLastSeenAppVersion();
+ UserStore.getInstance().saveLastSeenAppVersion();
};
render() {
diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx
index be56f5f477..5b9852a992 100644
--- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx
+++ b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx
@@ -19,7 +19,7 @@ import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps {
}
@@ -49,7 +49,7 @@ export class DaemonSetDetails extends React.Component {
const nodeSelector = daemonSet.getNodeSelectors();
const childPods = daemonSetStore.getChildPods(daemonSet);
const metrics = daemonSetStore.metrics;
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.DaemonSet);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.DaemonSet);
return (
diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx
index 778fcd9bc2..6a2b9f92a2 100644
--- a/src/renderer/components/+workloads-deployments/deployment-details.tsx
+++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx
@@ -20,7 +20,7 @@ import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps
{
}
@@ -49,7 +49,7 @@ export class DeploymentDetails extends React.Component {
const selectors = deployment.getSelectors();
const childPods = deploymentStore.getChildPods(deployment);
const metrics = deploymentStore.metrics;
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.Deployment);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Deployment);
return (
diff --git a/src/renderer/components/+workloads-overview/overview-workload-status.tsx b/src/renderer/components/+workloads-overview/overview-workload-status.tsx
index cb2accb09c..acab487e91 100644
--- a/src/renderer/components/+workloads-overview/overview-workload-status.tsx
+++ b/src/renderer/components/+workloads-overview/overview-workload-status.tsx
@@ -8,7 +8,7 @@ import { observer } from "mobx-react";
import { PieChart } from "../chart";
import { cssVar } from "../../utils";
import { ChartData, ChartDataSets } from "chart.js";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
interface SimpleChartDataSets extends ChartDataSets {
backgroundColor?: string[];
@@ -41,7 +41,7 @@ export class OverviewWorkloadStatus extends React.Component
{
labels: [] as string[],
datasets: [{
data: [1],
- backgroundColor: [themeStore.activeTheme.colors.pieChartDefaultColor],
+ backgroundColor: [ThemeStore.getInstance().activeTheme.colors.pieChartDefaultColor],
label: "Empty"
}]
};
diff --git a/src/renderer/components/+workloads-pods/container-charts.tsx b/src/renderer/components/+workloads-pods/container-charts.tsx
index ccea4ec788..8cd22b0cbb 100644
--- a/src/renderer/components/+workloads-pods/container-charts.tsx
+++ b/src/renderer/components/+workloads-pods/container-charts.tsx
@@ -5,13 +5,13 @@ import { BarChart, cpuOptions, memoryOptions } from "../chart";
import { isMetricsEmpty, normalizeMetrics } from "../../api/endpoints/metrics.api";
import { NoMetrics } from "../resource-metrics/no-metrics";
import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
type IContext = IResourceMetricsValue;
export const ContainerCharts = observer(() => {
const { params: { metrics }, tabId } = useContext(ResourceMetricsContext);
- const { chartCapacityColor } = themeStore.activeTheme.colors;
+ const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
if (!metrics) return null;
if (isMetricsEmpty(metrics)) return ;
diff --git a/src/renderer/components/+workloads-pods/pod-charts.tsx b/src/renderer/components/+workloads-pods/pod-charts.tsx
index c505126acb..62eb8b3a2e 100644
--- a/src/renderer/components/+workloads-pods/pod-charts.tsx
+++ b/src/renderer/components/+workloads-pods/pod-charts.tsx
@@ -6,7 +6,7 @@ import { isMetricsEmpty, normalizeMetrics } from "../../api/endpoints/metrics.ap
import { NoMetrics } from "../resource-metrics/no-metrics";
import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics";
import { WorkloadKubeObject } from "../../api/workload-kube-object";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
export const podMetricTabs = [
"CPU",
@@ -19,7 +19,7 @@ type IContext = IResourceMetricsValue {
const { params: { metrics }, tabId, object } = useContext(ResourceMetricsContext);
- const { chartCapacityColor } = themeStore.activeTheme.colors;
+ const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
const id = object.getId();
if (!metrics) return null;
diff --git a/src/renderer/components/+workloads-pods/pod-details-container.tsx b/src/renderer/components/+workloads-pods/pod-details-container.tsx
index 779a0e0de2..a2874aac60 100644
--- a/src/renderer/components/+workloads-pods/pod-details-container.tsx
+++ b/src/renderer/components/+workloads-pods/pod-details-container.tsx
@@ -12,8 +12,8 @@ import { ResourceMetrics } from "../resource-metrics";
import { IMetrics } from "../../api/endpoints/metrics.api";
import { ContainerCharts } from "./container-charts";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
import { LocaleDate } from "../locale-date";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props {
pod: Pod;
@@ -66,7 +66,7 @@ export class PodDetailsContainer extends React.Component {
"Memory",
"Filesystem",
];
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.Container);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Container);
return (
diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx
index f643d599a5..4150f5807d 100644
--- a/src/renderer/components/+workloads-pods/pod-details.tsx
+++ b/src/renderer/components/+workloads-pods/pod-details.tsx
@@ -23,7 +23,7 @@ import { PodCharts, podMetricTabs } from "./pod-charts";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps
{
}
@@ -68,7 +68,7 @@ export class PodDetails extends React.Component {
const nodeSelector = pod.getNodeSelectors();
const volumes = pod.getVolumes();
const metrics = podsStore.metrics;
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.Pod);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.Pod);
return (
diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx
index b104340612..0f4732fdc2 100644
--- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx
+++ b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx
@@ -18,7 +18,7 @@ import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps
{
}
@@ -49,7 +49,7 @@ export class ReplicaSetDetails extends React.Component {
const nodeSelector = replicaSet.getNodeSelectors();
const images = replicaSet.getImages();
const childPods = replicaSetStore.getChildPods(replicaSet);
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.ReplicaSet);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.ReplicaSet);
return (
diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx
index f21f9efc5d..b6f5812e21 100644
--- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx
+++ b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx
@@ -19,7 +19,7 @@ import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends KubeObjectDetailsProps
{
}
@@ -48,7 +48,7 @@ export class StatefulSetDetails extends React.Component {
const nodeSelector = statefulSet.getNodeSelectors();
const childPods = statefulSetStore.getChildPods(statefulSet);
const metrics = statefulSetStore.metrics;
- const isMetricHidden = clusterStore.isMetricHidden(ResourceType.StatefulSet);
+ const isMetricHidden = ClusterStore.getInstance().isMetricHidden(ResourceType.StatefulSet);
return (
diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx
index 2110898885..41fc63cf4d 100755
--- a/src/renderer/components/app.tsx
+++ b/src/renderer/components/app.tsx
@@ -34,7 +34,7 @@ import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store
import logger from "../../main/logger";
import { webFrame } from "electron";
import { clusterPageRegistry, getExtensionPageUrl } from "../../extensions/registries/page-registry";
-import { extensionLoader } from "../../extensions/extension-loader";
+import { ExtensionLoader } from "../../extensions/extension-loader";
import { appEventBus } from "../../common/event-bus";
import { requestMain } from "../../common/ipc";
import whatInput from "what-input";
@@ -62,7 +62,7 @@ export class App extends React.Component {
await requestMain(clusterSetFrameIdHandler, clusterId);
await getHostedCluster().whenReady; // cluster.activate() is done at this point
- extensionLoader.loadOnClusterRenderer();
+ ExtensionLoader.getInstance().loadOnClusterRenderer();
setTimeout(() => {
appEventBus.emit({
name: "cluster",
diff --git a/src/renderer/components/chart/bar-chart.tsx b/src/renderer/components/chart/bar-chart.tsx
index 74ee203d4c..3ba1807190 100644
--- a/src/renderer/components/chart/bar-chart.tsx
+++ b/src/renderer/components/chart/bar-chart.tsx
@@ -7,7 +7,7 @@ import { ChartData, ChartOptions, ChartPoint, ChartTooltipItem, Scriptable } fro
import { Chart, ChartKind, ChartProps } from "./chart";
import { bytesToUnits, cssNames } from "../../utils";
import { ZebraStripes } from "./zebra-stripes.plugin";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { NoMetrics } from "../resource-metrics/no-metrics";
interface Props extends ChartProps {
@@ -26,7 +26,7 @@ export class BarChart extends React.Component
{
render() {
const { name, data, className, timeLabelStep, plugins, options: customOptions, ...settings } = this.props;
- const { textColorPrimary, borderFaintColor, chartStripesColor } = themeStore.activeTheme.colors;
+ const { textColorPrimary, borderFaintColor, chartStripesColor } = ThemeStore.getInstance().activeTheme.colors;
const getBarColor: Scriptable = ({ dataset }) => {
const color = dataset.borderColor;
diff --git a/src/renderer/components/chart/pie-chart.tsx b/src/renderer/components/chart/pie-chart.tsx
index 939d6bb612..63bd9a209e 100644
--- a/src/renderer/components/chart/pie-chart.tsx
+++ b/src/renderer/components/chart/pie-chart.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import ChartJS, { ChartOptions } from "chart.js";
import { Chart, ChartProps } from "./chart";
import { cssNames } from "../../utils";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
interface Props extends ChartProps {
}
@@ -13,7 +13,7 @@ interface Props extends ChartProps {
export class PieChart extends React.Component {
render() {
const { data, className, options, ...chartProps } = this.props;
- const { contentColor } = themeStore.activeTheme.colors;
+ const { contentColor } = ThemeStore.getInstance().activeTheme.colors;
const cutouts = [88, 76, 63];
const opts: ChartOptions = this.props.showChart === false ? {} : {
maintainAspectRatio: false,
diff --git a/src/renderer/components/cluster-manager/bottom-bar.test.tsx b/src/renderer/components/cluster-manager/bottom-bar.test.tsx
index f13121f3a5..7f8c833c11 100644
--- a/src/renderer/components/cluster-manager/bottom-bar.test.tsx
+++ b/src/renderer/components/cluster-manager/bottom-bar.test.tsx
@@ -7,7 +7,6 @@ jest.mock("../../../extensions/registries");
import { statusBarRegistry } from "../../../extensions/registries";
describe("", () => {
-
it("renders w/o errors", () => {
const { container } = render();
diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx
index 7b9a7be0ea..489ffae284 100644
--- a/src/renderer/components/cluster-manager/cluster-manager.tsx
+++ b/src/renderer/components/cluster-manager/cluster-manager.tsx
@@ -10,7 +10,7 @@ import { Preferences, preferencesRoute } from "../+preferences";
import { AddCluster, addClusterRoute } from "../+add-cluster";
import { ClusterView } from "./cluster-view";
import { clusterViewRoute } from "./cluster-view.route";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
import { globalPageRegistry } from "../../../extensions/registries/page-registry";
import { Extensions, extensionsRoute } from "../+extensions";
@@ -21,7 +21,7 @@ import { EntitySettings, entitySettingsRoute } from "../+entity-settings";
@observer
export class ClusterManager extends React.Component {
componentDidMount() {
- const getMatchedCluster = () => clusterStore.getById(getMatchedClusterId());
+ const getMatchedCluster = () => ClusterStore.getInstance().getById(getMatchedClusterId());
disposeOnUnmount(this, [
reaction(getMatchedClusterId, initView, {
@@ -59,9 +59,12 @@ export class ClusterManager extends React.Component {
- {globalPageRegistry.getItems().map(({ url, components: { Page } }) => {
- return ;
- })}
+ {
+ globalPageRegistry.getItems()
+ .map(({ url, components: { Page } }) => (
+
+ ))
+ }
diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx
index 3e17eb3e4c..7fb68ceb36 100644
--- a/src/renderer/components/cluster-manager/cluster-status.tsx
+++ b/src/renderer/components/cluster-manager/cluster-status.tsx
@@ -10,7 +10,7 @@ import { Icon } from "../icon";
import { Button } from "../button";
import { cssNames, IClassName } from "../../utils";
import { Cluster } from "../../../main/cluster";
-import { ClusterId, clusterStore } from "../../../common/cluster-store";
+import { ClusterId, ClusterStore } from "../../../common/cluster-store";
import { CubeSpinner } from "../spinner";
import { clusterActivateHandler } from "../../../common/cluster-ipc";
@@ -25,7 +25,7 @@ export class ClusterStatus extends React.Component {
@observable isReconnecting = false;
get cluster(): Cluster {
- return clusterStore.getById(this.props.clusterId);
+ return ClusterStore.getInstance().getById(this.props.clusterId);
}
@computed get hasErrors(): boolean {
diff --git a/src/renderer/components/cluster-manager/cluster-view.tsx b/src/renderer/components/cluster-manager/cluster-view.tsx
index 8d89b7a81d..fbc55d8db3 100644
--- a/src/renderer/components/cluster-manager/cluster-view.tsx
+++ b/src/renderer/components/cluster-manager/cluster-view.tsx
@@ -7,9 +7,9 @@ import { IClusterViewRouteParams } from "./cluster-view.route";
import { ClusterStatus } from "./cluster-status";
import { hasLoadedView } from "./lens-views";
import { Cluster } from "../../../main/cluster";
-import { clusterStore } from "../../../common/cluster-store";
import { navigate } from "../../navigation";
import { catalogURL } from "../+catalog";
+import { ClusterStore } from "../../../common/cluster-store";
interface Props extends RouteComponentProps {
}
@@ -21,12 +21,12 @@ export class ClusterView extends React.Component {
}
get cluster(): Cluster {
- return clusterStore.getById(this.clusterId);
+ return ClusterStore.getInstance().getById(this.clusterId);
}
async componentDidMount() {
disposeOnUnmount(this, [
- reaction(() => this.clusterId, clusterId => clusterStore.setActive(clusterId), {
+ reaction(() => this.clusterId, clusterId => ClusterStore.getInstance().setActive(clusterId), {
fireImmediately: true,
}),
reaction(() => this.cluster.online, (online) => {
diff --git a/src/renderer/components/cluster-manager/lens-views.ts b/src/renderer/components/cluster-manager/lens-views.ts
index a3a92e4f64..30fae018cc 100644
--- a/src/renderer/components/cluster-manager/lens-views.ts
+++ b/src/renderer/components/cluster-manager/lens-views.ts
@@ -1,5 +1,5 @@
import { observable, when } from "mobx";
-import { ClusterId, clusterStore, getClusterFrameUrl } from "../../../common/cluster-store";
+import { ClusterId, ClusterStore, getClusterFrameUrl } from "../../../common/cluster-store";
import { getMatchedClusterId } from "../../navigation";
import logger from "../../../main/logger";
@@ -19,7 +19,7 @@ export async function initView(clusterId: ClusterId) {
if (!clusterId || lensViews.has(clusterId)) {
return;
}
- const cluster = clusterStore.getById(clusterId);
+ const cluster = ClusterStore.getInstance().getById(clusterId);
if (!cluster) {
return;
@@ -44,13 +44,9 @@ export async function initView(clusterId: ClusterId) {
export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrameElement) {
await when(() => {
- const cluster = clusterStore.getById(clusterId);
+ const cluster = ClusterStore.getInstance().getById(clusterId);
- if (!cluster) return true;
-
- const view = lensViews.get(clusterId);
-
- return cluster.disconnected && view?.isLoaded;
+ return !cluster || (cluster.disconnected && lensViews.get(clusterId)?.isLoaded);
});
logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`);
lensViews.delete(clusterId);
@@ -64,7 +60,7 @@ export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrame
}
export function refreshViews() {
- const cluster = clusterStore.getById(getMatchedClusterId());
+ const cluster = ClusterStore.getInstance().getById(getMatchedClusterId());
lensViews.forEach(({ clusterId, view, isLoaded }) => {
const isCurrent = clusterId === cluster?.id;
diff --git a/src/renderer/components/cluster-settings/cluster-settings.command.ts b/src/renderer/components/cluster-settings/cluster-settings.command.ts
index 061d135d67..419fcbabe4 100644
--- a/src/renderer/components/cluster-settings/cluster-settings.command.ts
+++ b/src/renderer/components/cluster-settings/cluster-settings.command.ts
@@ -1,7 +1,7 @@
import { navigate } from "../../navigation";
import { commandRegistry } from "../../../extensions/registries/command-registry";
-import { clusterStore } from "../../../common/cluster-store";
import { entitySettingsURL } from "../+entity-settings";
+import { ClusterStore } from "../../../common/cluster-store";
commandRegistry.add({
id: "cluster.viewCurrentClusterSettings",
@@ -9,7 +9,7 @@ commandRegistry.add({
scope: "global",
action: () => navigate(entitySettingsURL({
params: {
- entityId: clusterStore.active.id
+ entityId: ClusterStore.getInstance().active.id
}
})),
isActive: (context) => !!context.entity
diff --git a/src/renderer/components/cluster-settings/cluster-settings.tsx b/src/renderer/components/cluster-settings/cluster-settings.tsx
index 86297e800e..247e098126 100644
--- a/src/renderer/components/cluster-settings/cluster-settings.tsx
+++ b/src/renderer/components/cluster-settings/cluster-settings.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
import { ClusterProxySetting } from "./components/cluster-proxy-setting";
import { ClusterNameSetting } from "./components/cluster-name-setting";
import { ClusterHomeDirSetting } from "./components/cluster-home-dir-setting";
@@ -13,7 +13,7 @@ import { CatalogEntity } from "../../api/catalog-entity";
function getClusterForEntity(entity: CatalogEntity) {
- const cluster = clusterStore.getById(entity.metadata.uid);
+ const cluster = ClusterStore.getInstance().getById(entity.metadata.uid);
if (!cluster?.enabled) {
return null;
diff --git a/src/renderer/components/cluster-settings/components/remove-cluster-button.tsx b/src/renderer/components/cluster-settings/components/remove-cluster-button.tsx
index 2d509c490f..20c1b2b62c 100644
--- a/src/renderer/components/cluster-settings/components/remove-cluster-button.tsx
+++ b/src/renderer/components/cluster-settings/components/remove-cluster-button.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { observer } from "mobx-react";
-import { clusterStore } from "../../../../common/cluster-store";
+import { ClusterStore } from "../../../../common/cluster-store";
import { Cluster } from "../../../../main/cluster";
import { autobind } from "../../../utils";
import { Button } from "../../button";
@@ -21,7 +21,7 @@ export class RemoveClusterButton extends React.Component {
labelOk: "Yes",
labelCancel: "No",
ok: async () => {
- await clusterStore.removeById(cluster.id);
+ await ClusterStore.getInstance().removeById(cluster.id);
}
});
}
diff --git a/src/renderer/components/command-palette/command-dialog.tsx b/src/renderer/components/command-palette/command-dialog.tsx
index c784612345..2ffa56d898 100644
--- a/src/renderer/components/command-palette/command-dialog.tsx
+++ b/src/renderer/components/command-palette/command-dialog.tsx
@@ -4,7 +4,7 @@ import { computed, observable, toJS } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import { commandRegistry } from "../../../extensions/registries/command-registry";
-import { clusterStore } from "../../../common/cluster-store";
+import { ClusterStore } from "../../../common/cluster-store";
import { CommandOverlay } from "./command-container";
import { broadcastMessage } from "../../../common/ipc";
import { navigate } from "../../navigation";
@@ -20,7 +20,7 @@ export class CommandDialog extends React.Component {
};
return commandRegistry.getItems().filter((command) => {
- if (command.scope === "entity" && !clusterStore.active) {
+ if (command.scope === "entity" && !ClusterStore.getInstance().active) {
return false;
}
diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx
index 1e8a18c6b5..a11c9f34de 100644
--- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx
+++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx
@@ -16,6 +16,20 @@ const getComponent = () => (
/>
);
+Object.defineProperty(window, "matchMedia", {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // Deprecated
+ removeListener: jest.fn(), // Deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+});
+
const renderTabs = () => render(getComponent());
const getTabKinds = () => dockStore.tabs.map(tab => tab.kind);
diff --git a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx
index d373d0fbab..90f4b79972 100644
--- a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx
+++ b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx
@@ -7,6 +7,8 @@ import { Pod } from "../../../api/endpoints";
import { LogResourceSelector } from "../log-resource-selector";
import { LogTabData } from "../log-tab.store";
import { dockerPod, deploymentPod1 } from "./pod.mock";
+import { ThemeStore } from "../../../theme.store";
+import { UserStore } from "../../../../common/user-store";
const getComponent = (tabData: LogTabData) => {
return (
@@ -41,6 +43,16 @@ const getFewPodsTabData = (): LogTabData => {
};
describe("", () => {
+ beforeEach(() => {
+ UserStore.getInstanceOrCreate();
+ ThemeStore.getInstanceOrCreate();
+ });
+
+ afterEach(() => {
+ UserStore.resetInstance();
+ ThemeStore.resetInstance();
+ });
+
it("renders w/o errors", () => {
const tabData = getOnePodTabData();
const { container } = render(getComponent(tabData));
diff --git a/src/renderer/components/dock/log-list.tsx b/src/renderer/components/dock/log-list.tsx
index a4167fdcb6..9c5accf1ef 100644
--- a/src/renderer/components/dock/log-list.tsx
+++ b/src/renderer/components/dock/log-list.tsx
@@ -10,7 +10,7 @@ import moment from "moment-timezone";
import { Align, ListOnScrollProps } from "react-window";
import { SearchStore, searchStore } from "../../../common/search-store";
-import { userStore } from "../../../common/user-store";
+import { UserStore } from "../../../common/user-store";
import { cssNames } from "../../utils";
import { Button } from "../button";
import { Icon } from "../icon";
@@ -81,7 +81,7 @@ export class LogList extends React.Component {
@computed
get logs() {
const showTimestamps = logTabStore.getData(this.props.id).showTimestamps;
- const { preferences } = userStore;
+ const { preferences } = UserStore.getInstance();
if (!showTimestamps) {
return logStore.logsWithoutTimestamps;
diff --git a/src/renderer/components/dock/terminal-window.tsx b/src/renderer/components/dock/terminal-window.tsx
index 094ed50521..7ce69443ed 100644
--- a/src/renderer/components/dock/terminal-window.tsx
+++ b/src/renderer/components/dock/terminal-window.tsx
@@ -7,7 +7,7 @@ import { cssNames } from "../../utils";
import { IDockTab } from "./dock.store";
import { Terminal } from "./terminal";
import { terminalStore } from "./terminal.store";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
interface Props {
className?: string;
@@ -38,7 +38,7 @@ export class TerminalWindow extends React.Component {
return (
this.elem = e}
/>
);
diff --git a/src/renderer/components/dock/terminal.ts b/src/renderer/components/dock/terminal.ts
index 7d8c19b8f6..1eb53b6929 100644
--- a/src/renderer/components/dock/terminal.ts
+++ b/src/renderer/components/dock/terminal.ts
@@ -4,9 +4,10 @@ import { Terminal as XTerm } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import { dockStore, TabId } from "./dock.store";
import { TerminalApi } from "../../api/terminal-api";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { autobind } from "../../utils";
import { isMac } from "../../../common/vars";
+import { camelCase } from "lodash";
export class Terminal {
static spawningPool: HTMLElement;
@@ -40,16 +41,10 @@ export class Terminal {
// Replacing keys stored in styles to format accepted by terminal
// E.g. terminalBrightBlack -> brightBlack
const colorPrefix = "terminal";
- const terminalColors = Object.entries(colors)
+ const terminalColorEntries = Object.entries(colors)
.filter(([name]) => name.startsWith(colorPrefix))
- .reduce
((colors, [name, color]) => {
- const colorName = name.split("").slice(colorPrefix.length);
-
- colorName[0] = colorName[0].toLowerCase();
- colors[colorName.join("")] = color;
-
- return colors;
- }, {});
+ .map(([name, color]) => [camelCase(name.slice(colorPrefix.length)), color]);
+ const terminalColors = Object.fromEntries(terminalColorEntries);
this.xterm.setOption("theme", terminalColors);
}
@@ -109,7 +104,7 @@ export class Terminal {
window.addEventListener("resize", this.onResize);
this.disposers.push(
- reaction(() => toJS(themeStore.activeTheme.colors), this.setTheme, {
+ reaction(() => toJS(ThemeStore.getInstance().activeTheme.colors), this.setTheme, {
fireImmediately: true
}),
dockStore.onResize(this.onResize),
diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx
index 29902bc6a0..1a7398c55d 100644
--- a/src/renderer/components/hotbar/hotbar-icon.tsx
+++ b/src/renderer/components/hotbar/hotbar-icon.tsx
@@ -10,7 +10,7 @@ import { Menu, MenuItem } from "../menu";
import { Icon } from "../icon";
import { observable } from "mobx";
import { navigate } from "../../navigation";
-import { hotbarStore } from "../../../common/hotbar-store";
+import { HotbarStore } from "../../../common/hotbar-store";
import { ConfirmDialog } from "../confirm-dialog";
interface Props extends DOMAttributes {
@@ -60,7 +60,7 @@ export class HotbarIcon extends React.Component {
}
removeFromHotbar(item: CatalogEntity) {
- const hotbar = hotbarStore.getByName("default"); // FIXME
+ const hotbar = HotbarStore.getInstance().getByName("default"); // FIXME
if (!hotbar) {
return;
diff --git a/src/renderer/components/hotbar/hotbar-menu.tsx b/src/renderer/components/hotbar/hotbar-menu.tsx
index 05a95c606c..056494338d 100644
--- a/src/renderer/components/hotbar/hotbar-menu.tsx
+++ b/src/renderer/components/hotbar/hotbar-menu.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
import { HotbarIcon } from "./hotbar-icon";
import { cssNames, IClassName } from "../../utils";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
-import { hotbarStore } from "../../../common/hotbar-store";
+import { HotbarStore } from "../../../common/hotbar-store";
import { catalogEntityRunContext } from "../../api/catalog-entity";
interface Props {
@@ -16,7 +16,7 @@ interface Props {
export class HotbarMenu extends React.Component {
get hotbarItems() {
- const hotbar = hotbarStore.getByName("default"); // FIXME
+ const hotbar = HotbarStore.getInstance().getByName("default"); // FIXME
if (!hotbar) {
return [];
@@ -47,4 +47,3 @@ export class HotbarMenu extends React.Component {
);
}
}
-
diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx
index b609b046ea..95d9667a56 100644
--- a/src/renderer/components/item-object-list/item-list-layout.tsx
+++ b/src/renderer/components/item-object-list/item-list-layout.tsx
@@ -16,11 +16,11 @@ import { Filter, FilterType, pageFilters } from "./page-filters.store";
import { PageFiltersList } from "./page-filters-list";
import { PageFiltersSelect } from "./page-filters-select";
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
import { MenuActions } from "../menu/menu-actions";
import { MenuItem } from "../menu";
import { Checkbox } from "../checkbox";
-import { userStore } from "../../../common/user-store";
+import { UserStore } from "../../../common/user-store";
import { namespaceStore } from "../+namespaces/namespace.store";
// todo: refactor, split to small re-usable components
@@ -440,7 +440,7 @@ export class ItemListLayout extends React.Component {
const { removeItemsDialog, items } = this;
const { selectedItems } = store;
const selectedItemId = detailsItem && detailsItem.getId();
- const classNames = cssNames(className, "box", "grow", themeStore.activeTheme.type);
+ const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
return (
@@ -469,7 +469,7 @@ export class ItemListLayout extends React.Component
{
}
@computed get hiddenColumns() {
- return userStore.getHiddenTableColumns(this.props.tableId);
+ return UserStore.getInstance().getHiddenTableColumns(this.props.tableId);
}
isHiddenColumn({ id: columnId, showWithColumn }: TableCellProps): boolean {
@@ -491,7 +491,7 @@ export class ItemListLayout extends React.Component {
hiddenColumns.delete(columnId);
}
- userStore.setHiddenTableColumns(this.props.tableId, hiddenColumns);
+ UserStore.getInstance().setHiddenTableColumns(this.props.tableId, hiddenColumns);
}
renderColumnVisibilityMenu() {
diff --git a/src/renderer/components/layout/__test__/main-layout-header.test.tsx b/src/renderer/components/layout/__test__/main-layout-header.test.tsx
index 04e455b33d..d639db92ab 100644
--- a/src/renderer/components/layout/__test__/main-layout-header.test.tsx
+++ b/src/renderer/components/layout/__test__/main-layout-header.test.tsx
@@ -6,6 +6,7 @@ import "@testing-library/jest-dom/extend-expect";
import { MainLayoutHeader } from "../main-layout-header";
import { Cluster } from "../../../../main/cluster";
+import { ClusterStore } from "../../../../common/cluster-store";
const cluster: Cluster = new Cluster({
id: "foo",
@@ -14,6 +15,14 @@ const cluster: Cluster = new Cluster({
});
describe("", () => {
+ beforeEach(() => {
+ ClusterStore.getInstanceOrCreate();
+ });
+
+ afterEach(() => {
+ ClusterStore.resetInstance();
+ });
+
it("renders w/o errors", () => {
const { container } = render();
diff --git a/src/renderer/components/locale-date/locale-date.tsx b/src/renderer/components/locale-date/locale-date.tsx
index 0477150c00..42223bbf69 100644
--- a/src/renderer/components/locale-date/locale-date.tsx
+++ b/src/renderer/components/locale-date/locale-date.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { observer } from "mobx-react";
import moment from "moment-timezone";
-import { userStore } from "../../../common/user-store";
+import { UserStore } from "../../../common/user-store";
interface Props {
date: string
@@ -10,7 +10,7 @@ interface Props {
@observer
export class LocaleDate extends React.Component {
render() {
- const { preferences } = userStore;
+ const { preferences } = UserStore.getInstance();
const { date } = this.props;
return <>{moment.tz(date, preferences.localeTimezone).format()}>;
diff --git a/src/renderer/components/select/select.tsx b/src/renderer/components/select/select.tsx
index e816633804..c0491cf54e 100644
--- a/src/renderer/components/select/select.tsx
+++ b/src/renderer/components/select/select.tsx
@@ -8,7 +8,7 @@ import { observer } from "mobx-react";
import { autobind, cssNames } from "../../utils";
import ReactSelect, { ActionMeta, components, Props as ReactSelectProps, Styles } from "react-select";
import Creatable, { CreatableProps } from "react-select/creatable";
-import { themeStore } from "../../theme.store";
+import { ThemeStore } from "../../theme.store";
const { Menu } = components;
@@ -40,7 +40,7 @@ export class Select extends React.Component {
};
@computed get theme() {
- return this.props.themeName || themeStore.activeTheme.type;
+ return this.props.themeName || ThemeStore.getInstance().activeTheme.type;
}
private styles: Styles = {
diff --git a/src/renderer/ipc/index.tsx b/src/renderer/ipc/index.tsx
index 119f13a0dc..924e313880 100644
--- a/src/renderer/ipc/index.tsx
+++ b/src/renderer/ipc/index.tsx
@@ -5,7 +5,7 @@ import { Notifications, notificationsStore } from "../components/notifications";
import { Button } from "../components/button";
import { isMac } from "../../common/vars";
import { invalidKubeconfigHandler } from "./invalid-kubeconfig-handler";
-import { clusterStore } from "../../common/cluster-store";
+import { ClusterStore } from "../../common/cluster-store";
import { navigate } from "../navigation";
import { entitySettingsURL } from "../components/+entity-settings";
@@ -76,7 +76,7 @@ function ListNamespacesForbiddenHandler(event: IpcRendererEvent, ...[clusterId]:
(
Add Accessible Namespaces
-
Cluster {clusterStore.active.name} does not have permissions to list namespaces. Please add the namespaces you have access to.
+
Cluster {ClusterStore.getInstance().active.name} does not have permissions to list namespaces. Please add the namespaces you have access to.