diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 608862c2af..cddb90fd43 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -27,7 +27,7 @@ import { IReactionOptions, observable, reaction, runInAction, when } from "mobx" import Singleton from "./utils/singleton"; import { getAppVersion } from "./utils/app-version"; import logger from "../main/logger"; -import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc"; +import { broadcastMessage, subscribeToBroadcast } from "./ipc"; import isEqual from "lodash/isEqual"; export interface BaseStoreParams extends ConfOptions { @@ -123,8 +123,7 @@ export abstract class BaseStore extends Singleton { this.onSync(model); }; - subscribeToBroadcast(this.syncMainChannel, callback); - this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncMainChannel, callback)); + this.syncDisposers.push(subscribeToBroadcast(this.syncMainChannel, callback)); } if (ipcRenderer) { @@ -133,8 +132,7 @@ export abstract class BaseStore extends Singleton { this.onSyncFromMain(model); }; - subscribeToBroadcast(this.syncRendererChannel, callback); - this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncRendererChannel, callback)); + this.syncDisposers.push(subscribeToBroadcast(this.syncRendererChannel, callback)); } } diff --git a/src/common/catalog/catalog-entity-registry.ts b/src/common/catalog/catalog-entity-registry.ts index 80a6c66a4c..2bb5017a22 100644 --- a/src/common/catalog/catalog-entity-registry.ts +++ b/src/common/catalog/catalog-entity-registry.ts @@ -21,9 +21,9 @@ import { action, computed, observable, IComputedValue, IObservableArray } from "mobx"; import type { CatalogEntity } from "./catalog-entity"; -import { iter } from "../utils"; +import { iter, Singleton } from "../utils"; -export class CatalogEntityRegistry { +export class CatalogEntityRegistry extends Singleton { protected sources = observable.map>([], { deep: true }); @action addObservableSource(id: string, source: IObservableArray) { @@ -48,5 +48,3 @@ export class CatalogEntityRegistry { return items as T[]; } } - -export const catalogEntityRegistry = new CatalogEntityRegistry(); diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index f20f2f6a05..17150a098c 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -19,7 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { handleRequest } from "./ipc"; import { ClusterId, ClusterStore } from "./cluster-store"; import { appEventBus } from "./event-bus"; import { ResourceApplier } from "../main/resource-applier"; @@ -33,13 +32,13 @@ export const clusterDisconnectHandler = "cluster:disconnect"; export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all"; if (ipcMain) { - handleRequest(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { + ipcMain.handle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { return ClusterStore.getInstance() .getById(clusterId) ?.activate(force); }); - handleRequest(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => { + ipcMain.handle(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => { const cluster = ClusterStore.getInstance().getById(clusterId); if (cluster) { @@ -48,13 +47,13 @@ if (ipcMain) { } }); - handleRequest(clusterRefreshHandler, (event, clusterId: ClusterId) => { + ipcMain.handle(clusterRefreshHandler, (event, clusterId: ClusterId) => { return ClusterStore.getInstance() .getById(clusterId) ?.refresh({ refreshMetadata: true }); }); - handleRequest(clusterDisconnectHandler, (event, clusterId: ClusterId) => { + ipcMain.handle(clusterDisconnectHandler, (event, clusterId: ClusterId) => { appEventBus.emit({name: "cluster", action: "stop"}); const cluster = ClusterStore.getInstance().getById(clusterId); @@ -64,7 +63,7 @@ if (ipcMain) { } }); - handleRequest(clusterKubectlApplyAllHandler, (event, clusterId: ClusterId, resources: string[]) => { + ipcMain.handle(clusterKubectlApplyAllHandler, (event, clusterId: ClusterId, resources: string[]) => { appEventBus.emit({name: "cluster", action: "kubectl-apply-all"}); const cluster = ClusterStore.getInstance().getById(clusterId); diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 89c71255c2..9dfba9db8e 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -31,7 +31,7 @@ import { appEventBus } from "./event-bus"; import { dumpConfigYaml } from "./kube-helpers"; import { saveToAppFiles } from "./utils/saveToAppFiles"; import type { KubeConfig } from "@kubernetes/client-node"; -import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc"; +import { requestMain, subscribeToBroadcast } from "./ipc"; import type { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting"; import { disposer, noop } from "./utils"; @@ -170,7 +170,7 @@ export class ClusterStore extends BaseStore { } }); } else if (ipcMain) { - handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => { + ipcMain.handle(ClusterStore.stateRequestChannel, (): clusterStateSync[] => { const states: clusterStateSync[] = []; this.clustersList.forEach((cluster) => { @@ -191,7 +191,7 @@ export class ClusterStore extends BaseStore { reaction(() => this.connectedClustersList, () => { this.pushState(); }), - () => unsubscribeAllFromBroadcast("cluster:state"), + () => ipcMain.removeAllListeners("cluster:state"), ); } } diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index 74305e0484..5efe225007 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -27,13 +27,10 @@ import { ipcMain, ipcRenderer, webContents, remote } from "electron"; import { toJS } from "mobx"; import logger from "../../main/logger"; import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames"; +import type { Disposer } from "../utils"; const subFramesChannel = "ipc:get-sub-frames"; -export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { - ipcMain.handle(channel, listener); -} - export async function requestMain(channel: string, ...args: any[]) { return ipcRenderer.invoke(channel, ...args); } @@ -73,34 +70,14 @@ export async function broadcastMessage(channel: string, ...args: any[]) { } } -export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) { - if (ipcRenderer) { - ipcRenderer.on(channel, listener); - } else if (ipcMain) { - ipcMain.on(channel, listener); - } +export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any): Disposer { + const source: NodeJS.EventEmitter = ipcRenderer ?? ipcMain; - return listener; -} + source?.on(channel, listener); -export function unsubscribeFromBroadcast(channel: string, listener: (...args: any[]) => any) { - if (ipcRenderer) { - ipcRenderer.off(channel, listener); - } else if (ipcMain) { - ipcMain.off(channel, listener); - } -} - -export function unsubscribeAllFromBroadcast(channel: string) { - if (ipcRenderer) { - ipcRenderer.removeAllListeners(channel); - } else if (ipcMain) { - ipcMain.removeAllListeners(channel); - } + return () => source?.off(channel, listener); } export function bindBroadcastHandlers() { - handleRequest(subFramesChannel, () => { - return getSubFrames(); - }); + ipcMain.handle(subFramesChannel, getSubFrames); } diff --git a/src/extensions/core-api/catalog.ts b/src/extensions/core-api/catalog.ts index 304ab7bbd0..262f5a430d 100644 --- a/src/extensions/core-api/catalog.ts +++ b/src/extensions/core-api/catalog.ts @@ -20,15 +20,13 @@ */ -import { CatalogEntity, catalogEntityRegistry as registry } from "../../common/catalog"; +import { CatalogEntity, CatalogEntityRegistry as InternalRegistry } from "../../common/catalog"; export { catalogCategoryRegistry as catalogCategories } from "../../common/catalog/catalog-category-registry"; export * from "../../common/catalog-entities"; export class CatalogEntityRegistry { - getItemsForApiKind(apiVersion: string, kind: string): T[] { - return registry.getItemsForApiKind(apiVersion, kind); + static getItemsForApiKind(apiVersion: string, kind: string): T[] { + return InternalRegistry.getInstance().getItemsForApiKind(apiVersion, kind); } } - -export const catalogEntities = new CatalogEntityRegistry(); diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index fbeac75adc..fb919889b7 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -20,13 +20,13 @@ */ import { watch } from "chokidar"; -import { ipcRenderer } from "electron"; +import { ipcMain, ipcRenderer } from "electron"; import { EventEmitter } from "events"; import fse from "fs-extra"; import { observable, reaction, toJS, when } from "mobx"; import os from "os"; import path from "path"; -import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc"; +import { broadcastMessage, requestMain, subscribeToBroadcast } from "../common/ipc"; import { Singleton } from "../common/utils"; import logger from "../main/logger"; import { ExtensionInstallationStateStore } from "../renderer/components/+extensions/extension-install.store"; @@ -136,7 +136,7 @@ export class ExtensionDiscovery extends Singleton { } async initMain() { - handleRequest(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON()); + ipcMain.handle(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON()); reaction(() => this.toJSON(), () => { this.broadcast(); diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index df230601cb..373f4e374c 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -19,13 +19,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { app, ipcRenderer, remote } from "electron"; +import { app, ipcMain, ipcRenderer, remote } from "electron"; import { EventEmitter } from "events"; import { isEqual } from "lodash"; import { action, computed, observable, reaction, toJS, when } from "mobx"; import path from "path"; import { getHostedCluster } from "../common/cluster-store"; -import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc"; +import { broadcastMessage, requestMain, subscribeToBroadcast } from "../common/ipc"; import { Singleton } from "../common/utils"; import logger from "../main/logger"; import type { InstalledExtension } from "./extension-discovery"; @@ -164,7 +164,7 @@ export class ExtensionLoader extends Singleton { this.broadcastExtensions(); }); - handleRequest(ExtensionLoader.extensionsMainChannel, () => { + ipcMain.handle(ExtensionLoader.extensionsMainChannel, () => { return Array.from(this.toJSON()); }); diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index 88676cadc0..30a5122fa8 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -23,7 +23,7 @@ import type { MenuRegistration } from "./registries/menu-registry"; import { LensExtension } from "./lens-extension"; import { WindowManager } from "../main/window-manager"; import { getExtensionPageUrl } from "./registries/page-registry"; -import { CatalogEntity, catalogEntityRegistry } from "../common/catalog"; +import { CatalogEntity, CatalogEntityRegistry } from "../common/catalog"; import type { IObservableArray } from "mobx"; export class LensMainExtension extends LensExtension { @@ -41,10 +41,10 @@ export class LensMainExtension extends LensExtension { } addCatalogSource(id: string, source: IObservableArray) { - catalogEntityRegistry.addObservableSource(`${this.name}:${id}`, source); + CatalogEntityRegistry.getInstance().addObservableSource(`${this.name}:${id}`, source); } removeCatalogSource(id: string) { - catalogEntityRegistry.removeSource(`${this.name}:${id}`); + CatalogEntityRegistry.getInstance().removeSource(`${this.name}:${id}`); } } diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index 362ba9a770..7650ac48ab 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -20,33 +20,19 @@ */ import { reaction, toJS } from "mobx"; -import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc"; -import type { CatalogEntityRegistry} from "../common/catalog"; -import "../common/catalog-entities/kubernetes-cluster"; -import type { Disposer } from "../common/utils"; +import { broadcastMessage, subscribeToBroadcast } from "../common/ipc"; +import { CatalogEntityRegistry} from "../common/catalog"; +import { disposer, Disposer } from "../common/utils"; -export class CatalogPusher { - static init(catalog: CatalogEntityRegistry) { - new CatalogPusher(catalog).init(); - } - - private constructor(private catalog: CatalogEntityRegistry) {} - - init() { - const disposers: Disposer[] = []; - - disposers.push(reaction(() => toJS(this.catalog.items, { recurseEverything: true }), (items) => { +export function pushCatalogToRenderer(): Disposer { + return disposer( + reaction(() => toJS(CatalogEntityRegistry.getInstance().items, { recurseEverything: true }), (items) => { broadcastMessage("catalog:items", items); }, { fireImmediately: true, - })); - - const listener = subscribeToBroadcast("catalog:broadcast", () => { - broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true })); - }); - - disposers.push(() => unsubscribeFromBroadcast("catalog:broadcast", listener)); - - return disposers; - } + }), + subscribeToBroadcast("catalog:broadcast", () => { + broadcastMessage("catalog:items", toJS(CatalogEntityRegistry.getInstance().items, { recurseEverything: true })); + }) + ); } diff --git a/src/main/catalog-sources/kubeconfig-sync.ts b/src/main/catalog-sources/kubeconfig-sync.ts index c4a8b0a514..ff960ce14d 100644 --- a/src/main/catalog-sources/kubeconfig-sync.ts +++ b/src/main/catalog-sources/kubeconfig-sync.ts @@ -20,7 +20,7 @@ */ import { action, observable, IComputedValue, computed, ObservableMap, runInAction } from "mobx"; -import { CatalogEntity, catalogEntityRegistry } from "../../common/catalog"; +import { CatalogEntity, CatalogEntityRegistry } from "../../common/catalog"; import { watch } from "chokidar"; import fs from "fs"; import fse from "fs-extra"; @@ -54,7 +54,7 @@ export class KubeconfigSyncManager extends Singleton { logger.info(`${logPrefix} starting requested syncs`); - catalogEntityRegistry.addComputedSource(KubeconfigSyncManager.syncName, computed(() => ( + CatalogEntityRegistry.getInstance().addComputedSource(KubeconfigSyncManager.syncName, computed(() => ( Array.from(iter.flatMap( this.sources.values(), ([entities]) => entities.get() @@ -88,7 +88,7 @@ export class KubeconfigSyncManager extends Singleton { this.stopOldSync(filePath); } - catalogEntityRegistry.removeSource(KubeconfigSyncManager.syncName); + CatalogEntityRegistry.getInstance().removeSource(KubeconfigSyncManager.syncName); this.syncing = false; } diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 6b8712c9d8..2b6de9dcff 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -28,7 +28,7 @@ import type { Cluster } from "./cluster"; import logger from "./logger"; import { apiKubePrefix } from "../common/vars"; import { Singleton } from "../common/utils"; -import { catalogEntityRegistry } from "../common/catalog"; +import { CatalogEntityRegistry } from "../common/catalog"; import { KubernetesCluster } from "../common/catalog-entities/kubernetes-cluster"; export class ClusterManager extends Singleton { @@ -39,7 +39,7 @@ export class ClusterManager extends Singleton { this.updateCatalog(ClusterStore.getInstance().clustersList); }, { fireImmediately: true }); - reaction(() => catalogEntityRegistry.getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => { + reaction(() => CatalogEntityRegistry.getInstance().getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => { this.syncClustersFromCatalog(entities); }); @@ -64,11 +64,13 @@ export class ClusterManager extends Singleton { } @action protected updateCatalog(clusters: Cluster[]) { + const registry = CatalogEntityRegistry.getInstance(); + for (const cluster of clusters) { - const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id); + const index = registry.items.findIndex((entity) => entity.metadata.uid === cluster.id); if (index !== -1) { - const entity = catalogEntityRegistry.items[index]; + const entity = registry.items[index]; entity.status.phase = cluster.disconnected ? "disconnected" : "connected"; entity.status.active = !cluster.disconnected; @@ -76,7 +78,7 @@ export class ClusterManager extends Singleton { if (cluster.preferences?.clusterName) { entity.metadata.name = cluster.preferences.clusterName; } - catalogEntityRegistry.items.splice(index, 1, entity); + registry.items.splice(index, 1, entity); } } } diff --git a/src/main/index.ts b/src/main/index.ts index 25a87bf30d..2fad1c3d52 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -45,19 +45,20 @@ import type { LensExtensionId } from "../extensions/lens-extension"; import { FilesystemProvisionerStore } from "./extension-filesystem"; import { installDeveloperTools } from "./developer-tools"; import { LensProtocolRouterMain } from "./protocol-handler"; -import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; +import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; import { bindBroadcastHandlers } from "../common/ipc"; import { startUpdateChecking } from "./app-updater"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; -import { CatalogPusher } from "./catalog-pusher"; -import { catalogEntityRegistry } from "../common/catalog"; +import { pushCatalogToRenderer } from "./catalog-pusher"; import { HotbarStore } from "../common/hotbar-store"; import { HelmRepoManager } from "./helm/helm-repo-manager"; import { KubeconfigSyncManager } from "./catalog-sources"; import { handleWsUpgrade } from "./proxy/ws-upgrade"; import { initRegistries } from "./initializers"; +import { CatalogEntityRegistry } from "../common/catalog"; const workingDir = path.join(app.getPath("appData"), appName); +const cleanup = disposer(); app.setName(appName); @@ -110,6 +111,8 @@ app.on("second-instance", (event, argv) => { }); app.on("ready", async () => { + CatalogEntityRegistry.createInstance(); + logger.info(`🚀 Starting ${productName} from "${workingDir}"`); logger.info("🐚 Syncing shell environment"); await shellSync(); @@ -143,7 +146,6 @@ app.on("ready", async () => { const lensProxy = LensProxy.createInstance(handleWsUpgrade); ClusterManager.createInstance(); - KubeconfigSyncManager.createInstance().startSync(); try { logger.info("🔌 Starting LensProxy"); @@ -189,17 +191,14 @@ app.on("ready", async () => { } ipcMain.on(IpcRendererNavigationEvents.LOADED, () => { - CatalogPusher.init(catalogEntityRegistry); + KubeconfigSyncManager.createInstance().startSync(); + cleanup.push(pushCatalogToRenderer()); startUpdateChecking(); - LensProtocolRouterMain - .getInstance() - .rendererLoaded = true; + LensProtocolRouterMain.getInstance().rendererLoaded = true; }); ExtensionLoader.getInstance().whenLoaded.then(() => { - LensProtocolRouterMain - .getInstance() - .extensionsLoaded = true; + LensProtocolRouterMain.getInstance().extensionsLoaded = true; }); logger.info("🧩 Initializing extensions"); @@ -253,6 +252,7 @@ app.on("will-quit", (event) => { appEventBus.emit({name: "app", action: "close"}); ClusterManager.getInstance(false)?.stop(); // close cluster connections KubeconfigSyncManager.getInstance(false)?.stopSync(); + cleanup(); if (blockQuit) { event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.) diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index ae575066e8..d227cceb34 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -44,6 +44,7 @@ import { HelmRepoManager } from "../main/helm/helm-repo-manager"; import { ExtensionInstallationStateStore } from "./components/+extensions/extension-install.store"; import { DefaultProps } from "./mui-base-theme"; import { initCommandRegistry, initEntitySettingsRegistry, initKubeObjectMenuRegistry, initRegistries, initWelcomeMenuRegistry, intiKubeObjectDetailRegistry } from "./initializers"; +import { CatalogEntityRegistry } from "../common/catalog"; /** * If this is a development buid, wait a second to attach @@ -75,6 +76,8 @@ export async function bootstrap(App: AppComponent) { await attachChromeDebugger(); rootElem.classList.toggle("is-mac", isMac); + CatalogEntityRegistry.createInstance(); + initRegistries(); initCommandRegistry(); initEntitySettingsRegistry();