diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 893a09b965..842e0304b4 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -22,11 +22,11 @@ import path from "path"; import Config from "conf"; import type { Options as ConfOptions } from "conf/dist/source/types"; -import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron"; +import { app, ipcMain, ipcRenderer, remote } from "electron"; import { IReactionOptions, makeObservable, observable, reaction, runInAction, when } from "mobx"; import { getAppVersion, Singleton, toJS, Disposer } from "./utils"; import logger from "../main/logger"; -import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc"; +import { broadcastMessage, ipcMainOn, ipcRendererOn } from "./ipc"; import isEqual from "lodash/isEqual"; export interface BaseStoreParams extends ConfOptions { @@ -126,23 +126,17 @@ export abstract class BaseStore extends Singleton { ); if (ipcMain) { - const callback = (event: IpcMainEvent, model: T) => { + this.syncDisposers.push(ipcMainOn(this.syncMainChannel, (event, model: T) => { logger.silly(`[STORE]: SYNC ${this.name} from renderer`, { model }); this.onSync(model); - }; - - subscribeToBroadcast(this.syncMainChannel, callback); - this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncMainChannel, callback)); + })); } if (ipcRenderer) { - const callback = (event: IpcRendererEvent, model: T) => { + this.syncDisposers.push(ipcRendererOn(this.syncRendererChannel, (event, model: T) => { logger.silly(`[STORE]: SYNC ${this.name} from main`, { model }); this.onSyncFromMain(model); - }; - - subscribeToBroadcast(this.syncRendererChannel, callback); - this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncRendererChannel, callback)); + })); } } diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index b1cced3edf..13725b37dc 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -19,15 +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"; -import { ipcMain, IpcMainInvokeEvent } from "electron"; -import { clusterFrameMap } from "./cluster-frames"; -import { catalogEntityRegistry } from "../main/catalog"; -import type { KubernetesCluster } from "./catalog-entities"; - export const clusterActivateHandler = "cluster:activate"; export const clusterSetFrameIdHandler = "cluster:set-frame-id"; export const clusterVisibilityHandler = "cluster:visibility"; @@ -35,86 +26,3 @@ export const clusterRefreshHandler = "cluster:refresh"; export const clusterDisconnectHandler = "cluster:disconnect"; export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all"; export const clusterKubectlDeleteAllHandler = "cluster:kubectl-delete-all"; - -if (ipcMain) { - handleRequest(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { - return ClusterStore.getInstance() - .getById(clusterId) - ?.activate(force); - }); - - handleRequest(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => { - const cluster = ClusterStore.getInstance().getById(clusterId); - - if (cluster) { - clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId }); - cluster.pushState(); - } - }); - - handleRequest(clusterVisibilityHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId, visible: boolean) => { - const entity = catalogEntityRegistry.getById(clusterId); - - for (const kubeEntity of catalogEntityRegistry.getItemsForApiKind(entity.apiVersion, entity.kind)) { - kubeEntity.status.active = false; - } - - if (entity) { - entity.status.active = visible; - } - }); - - handleRequest(clusterRefreshHandler, (event, clusterId: ClusterId) => { - return ClusterStore.getInstance() - .getById(clusterId) - ?.refresh({ refreshMetadata: true }); - }); - - handleRequest(clusterDisconnectHandler, (event, clusterId: ClusterId) => { - appEventBus.emit({name: "cluster", action: "stop"}); - const cluster = ClusterStore.getInstance().getById(clusterId); - - if (cluster) { - cluster.disconnect(); - clusterFrameMap.delete(cluster.id); - } - }); - - handleRequest(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => { - appEventBus.emit({name: "cluster", action: "kubectl-apply-all"}); - const cluster = ClusterStore.getInstance().getById(clusterId); - - if (cluster) { - const applier = new ResourceApplier(cluster); - - try { - const stdout = await applier.kubectlApplyAll(resources, extraArgs); - - return { stdout }; - } catch (error: any) { - return { stderr: error }; - } - } else { - throw `${clusterId} is not a valid cluster id`; - } - }); - - handleRequest(clusterKubectlDeleteAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => { - appEventBus.emit({name: "cluster", action: "kubectl-delete-all"}); - const cluster = ClusterStore.getInstance().getById(clusterId); - - if (cluster) { - const applier = new ResourceApplier(cluster); - - try { - const stdout = await applier.kubectlDeleteAll(resources, extraArgs); - - return { stdout }; - } catch (error: any) { - return { stderr: error }; - } - } else { - throw `${clusterId} is not a valid cluster id`; - } - }); -} diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 60968efd06..d2fcd616ed 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -29,7 +29,7 @@ import migrations from "../migrations/cluster-store"; import * as uuid from "uuid"; import logger from "../main/logger"; import { appEventBus } from "./event-bus"; -import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc"; +import { ipcMainHandle, ipcMainOn, ipcRendererOn, requestMain } from "./ipc"; import { disposer, noop, toJS } from "./utils"; export interface ClusterIconUpload { @@ -110,6 +110,8 @@ export interface ClusterPrometheusPreferences { } export class ClusterStore extends BaseStore { + private static StateChannel = "cluster:state"; + static get storedKubeConfigFolder(): string { return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs"); } @@ -158,7 +160,7 @@ export class ClusterStore extends BaseStore { } }); } else if (ipcMain) { - handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => { + ipcMainHandle(ClusterStore.stateRequestChannel, (): clusterStateSync[] => { const clusterStates: clusterStateSync[] = []; this.clustersList.forEach((cluster) => { @@ -179,17 +181,25 @@ export class ClusterStore extends BaseStore { reaction(() => this.connectedClustersList, () => { this.pushState(); }), - () => unsubscribeAllFromBroadcast("cluster:state"), ); } } + handleStateChange = (event: any, clusterId: string, state: ClusterState) => { + logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, clusterId, state); + this.getById(clusterId)?.setState(state); + }; + registerIpcListener() { logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`); - subscribeToBroadcast("cluster:state", (event, clusterId: string, state: ClusterState) => { - logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, clusterId, state); - this.getById(clusterId)?.setState(state); - }); + + if (ipcMain) { + this.disposer.push(ipcMainOn(ClusterStore.StateChannel, this.handleStateChange)); + } + + if (ipcRenderer) { + this.disposer.push(ipcRendererOn(ClusterStore.StateChannel, this.handleStateChange)); + } } unregisterIpcListener() { diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index f541e0d276..074d73176c 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -26,22 +26,21 @@ import { ipcMain, ipcRenderer, remote, webContents } from "electron"; import { toJS } from "../utils/toJS"; import logger from "../../main/logger"; -import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames"; +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, async (event, ...args) => { - const payload = await listener(event, ...args); - - return sanitizePayload(payload); - }); -} - export async function requestMain(channel: string, ...args: any[]) { return ipcRenderer.invoke(channel, ...args.map(sanitizePayload)); } +export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { + ipcMain.handle(channel, async (event, ...args) => { + return sanitizePayload(await listener(event, ...args)); + }); +} + function getSubFrames(): ClusterFrameInfo[] { return Array.from(clusterFrameMap.values()); } @@ -76,34 +75,20 @@ export 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 ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): Disposer { + ipcMain.on(channel, listener); - return listener; + return () => ipcMain.off(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 ipcRendererOn(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { + ipcRenderer.on(channel, listener); -export function unsubscribeAllFromBroadcast(channel: string) { - if (ipcRenderer) { - ipcRenderer.removeAllListeners(channel); - } else if (ipcMain) { - ipcMain.removeAllListeners(channel); - } + return () => ipcRenderer.off(channel, listener); } export function bindBroadcastHandlers() { - handleRequest(subFramesChannel, () => getSubFrames()); + ipcMainHandle(subFramesChannel, () => getSubFrames()); } /** diff --git a/src/common/ipc/type-enforced-ipc.ts b/src/common/ipc/type-enforced-ipc.ts index a490b1d384..95d4327b3e 100644 --- a/src/common/ipc/type-enforced-ipc.ts +++ b/src/common/ipc/type-enforced-ipc.ts @@ -23,6 +23,7 @@ import type { EventEmitter } from "events"; import { ipcMain } from "electron"; import logger from "../../main/logger"; import type { Disposer } from "../utils"; +import { ipcMainHandle } from "./ipc"; export type ListenerEvent = Parameters[1]>[0]; export type ListVerifier = (args: unknown[]) => args is T; @@ -116,7 +117,7 @@ export function handleCorrect< throw new TypeError(`Invalid args for invoke on channel: ${channel}`); } - ipcMain.handle(channel, wrappedHandler); + ipcMainHandle(channel, wrappedHandler); return () => ipcMain.removeHandler(channel); } diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index aebc44704d..ea34798271 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -26,7 +26,7 @@ import fse from "fs-extra"; import { observable, reaction, when, makeObservable } from "mobx"; import os from "os"; import path from "path"; -import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc"; +import { broadcastMessage, ipcMainHandle, ipcRendererOn, requestMain } from "../common/ipc"; import { Singleton, toJS } from "../common/utils"; import logger from "../main/logger"; import { ExtensionInstallationStateStore } from "../renderer/components/+extensions/extension-install.store"; @@ -34,7 +34,6 @@ import { extensionInstaller } from "./extension-installer"; import { ExtensionsStore } from "./extensions-store"; import { ExtensionLoader } from "./extension-loader"; import type { LensExtensionId, LensExtensionManifest } from "./lens-extension"; -import type { PackageJson } from "type-fest"; import semver from "semver"; import { appSemVer } from "../common/vars"; import { isProduction } from "../common/vars"; @@ -144,13 +143,13 @@ export class ExtensionDiscovery extends Singleton { }; requestMain(ExtensionDiscovery.extensionDiscoveryChannel).then(onMessage); - subscribeToBroadcast(ExtensionDiscovery.extensionDiscoveryChannel, (_event, message: ExtensionDiscoveryChannelMessage) => { + ipcRendererOn(ExtensionDiscovery.extensionDiscoveryChannel, (_event, message: ExtensionDiscoveryChannelMessage) => { onMessage(message); }); } async initMain(): Promise { - handleRequest(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON()); + ipcMainHandle(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON()); reaction(() => this.toJSON(), () => { this.broadcast(); diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 20830630fb..a01fed787c 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -25,7 +25,7 @@ import { isEqual } from "lodash"; import { action, computed, makeObservable, observable, reaction, when } from "mobx"; import path from "path"; import { getHostedCluster } from "../common/cluster-store"; -import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc"; +import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../common/ipc"; import { Disposer, Singleton, toJS } from "../common/utils"; import logger from "../main/logger"; import type { InstalledExtension } from "./extension-discovery"; @@ -174,11 +174,11 @@ export class ExtensionLoader extends Singleton { this.isLoaded = true; this.loadOnMain(); - handleRequest(ExtensionLoader.extensionsMainChannel, () => { + ipcMainHandle(ExtensionLoader.extensionsMainChannel, () => { return Array.from(this.toJSON()); }); - subscribeToBroadcast(ExtensionLoader.extensionsRendererChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => { + ipcMainOn(ExtensionLoader.extensionsRendererChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { this.syncExtensions(extensions); }); } @@ -199,7 +199,7 @@ export class ExtensionLoader extends Singleton { }; requestMain(ExtensionLoader.extensionsMainChannel).then(extensionListHandler); - subscribeToBroadcast(ExtensionLoader.extensionsMainChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => { + ipcRendererOn(ExtensionLoader.extensionsMainChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { extensionListHandler(extensions); }); } diff --git a/src/extensions/ipc/ipc-main.ts b/src/extensions/ipc/ipc-main.ts index 094ed48a1c..eec89e9966 100644 --- a/src/extensions/ipc/ipc-main.ts +++ b/src/extensions/ipc/ipc-main.ts @@ -24,6 +24,7 @@ import { Disposers } from "../lens-extension"; import type { LensMainExtension } from "../lens-main-extension"; import type { Disposer } from "../../common/utils"; import { once } from "lodash"; +import { ipcMainHandle } from "../../common/ipc"; export abstract class IpcMain extends IpcRegistrar { constructor(extension: LensMainExtension) { @@ -55,7 +56,7 @@ export abstract class IpcMain extends IpcRegistrar { handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - ipcMain.handle(prefixedChannel, handler); + ipcMainHandle(prefixedChannel, handler); this.extension[Disposers].push(() => ipcMain.removeHandler(prefixedChannel)); } } diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 2daefb689a..f3859e5fb5 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -21,7 +21,6 @@ import "../common/cluster-ipc"; import type http from "http"; -import { ipcMain } from "electron"; import { action, autorun, makeObservable, reaction } from "mobx"; import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store"; import type { Cluster } from "./cluster"; @@ -30,6 +29,7 @@ import { apiKubePrefix } from "../common/vars"; import { Singleton } from "../common/utils"; import { catalogEntityRegistry } from "./catalog"; import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster"; +import { ipcMainOn } from "../common/ipc"; export class ClusterManager extends Singleton { private store = ClusterStore.getInstance(); @@ -67,8 +67,8 @@ export class ClusterManager extends Singleton { delay: 250 }); - ipcMain.on("network:offline", this.onNetworkOffline); - ipcMain.on("network:online", this.onNetworkOnline); + ipcMainOn("network:offline", this.onNetworkOffline); + ipcMainOn("network:online", this.onNetworkOnline); } @action diff --git a/src/main/index.ts b/src/main/index.ts index 9a628a3bab..389078b4c4 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -25,7 +25,7 @@ import "../common/system-ca"; import * as Mobx from "mobx"; import * as LensExtensionsCommonApi from "../extensions/common-api"; import * as LensExtensionsMainApi from "../extensions/main-api"; -import { app, autoUpdater, ipcMain, dialog, powerMonitor } from "electron"; +import { app, autoUpdater, dialog, powerMonitor } from "electron"; import { appName, isMac, productName } from "../common/vars"; import path from "path"; import { LensProxy } from "./proxy/lens-proxy"; @@ -46,7 +46,7 @@ import { FilesystemProvisionerStore } from "./extension-filesystem"; import { installDeveloperTools } from "./developer-tools"; import { LensProtocolRouterMain } from "./protocol-handler"; import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; -import { bindBroadcastHandlers } from "../common/ipc"; +import { bindBroadcastHandlers, ipcMainOn } from "../common/ipc"; import { startUpdateChecking } from "./app-updater"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import { pushCatalogToRenderer } from "./catalog-pusher"; @@ -57,7 +57,7 @@ import { KubeconfigSyncManager } from "./catalog-sources"; import { handleWsUpgrade } from "./proxy/ws-upgrade"; import configurePackages from "../common/configure-packages"; import { PrometheusProviderRegistry } from "./prometheus"; -import { initRegistries, initPrometheusProviderRegistry } from "./initializers"; +import * as initializers from "./initializers"; const workingDir = path.join(app.getPath("appData"), appName); const cleanup = disposer(); @@ -82,6 +82,7 @@ if (process.env.LENS_DISABLE_GPU) { configurePackages(); mangleProxyEnv(); +initializers.initIpcMainHandlers(); if (app.commandLine.getSwitchValue("proxy-server") !== "") { process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server"); @@ -125,7 +126,7 @@ app.on("ready", async () => { registerFileProtocol("static", __static); PrometheusProviderRegistry.createInstance(); - initPrometheusProviderRegistry(); + initializers.initPrometheusProviderRegistry(); const userStore = UserStore.createInstance(); const clusterStore = ClusterStore.createInstance(); @@ -174,7 +175,7 @@ app.on("ready", async () => { app.exit(); } - initRegistries(); + initializers.initRegistries(); const extensionDiscovery = ExtensionDiscovery.createInstance(); ExtensionLoader.createInstance().init(); @@ -193,7 +194,7 @@ app.on("ready", async () => { windowManager.ensureMainWindow(); } - ipcMain.on(IpcRendererNavigationEvents.LOADED, () => { + ipcMainOn(IpcRendererNavigationEvents.LOADED, () => { cleanup.push(pushCatalogToRenderer(catalogEntityRegistry)); KubeconfigSyncManager.getInstance().startSync(); startUpdateChecking(); diff --git a/src/main/initializers/index.ts b/src/main/initializers/index.ts index a0835ab834..526b61d4ed 100644 --- a/src/main/initializers/index.ts +++ b/src/main/initializers/index.ts @@ -21,3 +21,4 @@ export * from "./registries"; export * from "./metrics-providers"; +export * from "./ipc"; diff --git a/src/main/initializers/ipc.ts b/src/main/initializers/ipc.ts new file mode 100644 index 0000000000..e3bb4f5ff7 --- /dev/null +++ b/src/main/initializers/ipc.ts @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import type { IpcMainInvokeEvent } from "electron"; +import type { KubernetesCluster } from "../../common/catalog-entities"; +import { clusterFrameMap } from "../../common/cluster-frames"; +import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../../common/cluster-ipc"; +import { ClusterId, ClusterStore } from "../../common/cluster-store"; +import { appEventBus } from "../../common/event-bus"; +import { ipcMainHandle } from "../../common/ipc"; +import { catalogEntityRegistry } from "../catalog"; +import { ResourceApplier } from "../resource-applier"; + +export function initIpcMainHandlers() { + ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => { + return ClusterStore.getInstance() + .getById(clusterId) + ?.activate(force); + }); + + ipcMainHandle(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => { + const cluster = ClusterStore.getInstance().getById(clusterId); + + if (cluster) { + clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId }); + cluster.pushState(); + } + }); + + ipcMainHandle(clusterVisibilityHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId, visible: boolean) => { + const entity = catalogEntityRegistry.getById(clusterId); + + for (const kubeEntity of catalogEntityRegistry.getItemsForApiKind(entity.apiVersion, entity.kind)) { + kubeEntity.status.active = false; + } + + if (entity) { + entity.status.active = visible; + } + }); + + ipcMainHandle(clusterRefreshHandler, (event, clusterId: ClusterId) => { + return ClusterStore.getInstance() + .getById(clusterId) + ?.refresh({ refreshMetadata: true }); + }); + + ipcMainHandle(clusterDisconnectHandler, (event, clusterId: ClusterId) => { + appEventBus.emit({ name: "cluster", action: "stop" }); + const cluster = ClusterStore.getInstance().getById(clusterId); + + if (cluster) { + cluster.disconnect(); + clusterFrameMap.delete(cluster.id); + } + }); + + ipcMainHandle(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => { + appEventBus.emit({ name: "cluster", action: "kubectl-apply-all" }); + const cluster = ClusterStore.getInstance().getById(clusterId); + + if (cluster) { + const applier = new ResourceApplier(cluster); + + try { + const stdout = await applier.kubectlApplyAll(resources, extraArgs); + + return { stdout }; + } catch (error: any) { + return { stderr: error }; + } + } else { + throw `${clusterId} is not a valid cluster id`; + } + }); + + ipcMainHandle(clusterKubectlDeleteAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => { + appEventBus.emit({ name: "cluster", action: "kubectl-delete-all" }); + const cluster = ClusterStore.getInstance().getById(clusterId); + + if (cluster) { + const applier = new ResourceApplier(cluster); + + try { + const stdout = await applier.kubectlDeleteAll(resources, extraArgs); + + return { stdout }; + } catch (error: any) { + return { stderr: error }; + } + } else { + throw `${clusterId} is not a valid cluster id`; + } + }); +} diff --git a/src/main/menu.ts b/src/main/menu.ts index 5b60541baa..b1ffff6a57 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -19,14 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { app, BrowserWindow, dialog, ipcMain, IpcMainEvent, Menu, MenuItem, MenuItemConstructorOptions, webContents, shell } from "electron"; +import { app, BrowserWindow, dialog, IpcMainEvent, Menu, MenuItem, MenuItemConstructorOptions, webContents, shell } from "electron"; import { autorun } from "mobx"; import type { WindowManager } from "./window-manager"; import { appName, isMac, isWindows, isTestEnv, docsUrl, supportUrl, productName } from "../common/vars"; import { MenuRegistry } from "../extensions/registries/menu-registry"; import logger from "./logger"; import { exitApp } from "./exit-app"; -import { broadcastMessage } from "../common/ipc"; +import { broadcastMessage, ipcMainOn } from "../common/ipc"; import * as packageJson from "../../package.json"; import { preferencesURL, extensionsURL, addClusterURL, catalogURL, welcomeURL } from "../common/routes"; @@ -276,7 +276,7 @@ export function buildMenu(windowManager: WindowManager) { if (isTestEnv) { // this is a workaround for the test environment (spectron) not being able to directly access // the application menus (https://github.com/electron-userland/spectron/issues/21) - ipcMain.on("test-menu-item-click", (event: IpcMainEvent, ...names: string[]) => { + ipcMainOn("test-menu-item-click", (event: IpcMainEvent, ...names: string[]) => { let menu: Menu = Menu.getApplicationMenu(); const parentLabels: string[] = []; let menuItem: MenuItem; diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 25b9e4ca88..b30d26d519 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -24,7 +24,7 @@ import { makeObservable, observable } from "mobx"; import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"; import windowStateKeeper from "electron-window-state"; import { appEventBus } from "../common/event-bus"; -import { subscribeToBroadcast } from "../common/ipc"; +import { ipcMainOn } from "../common/ipc"; import { initMenu } from "./menu"; import { initTray } from "./tray"; import { delay, Singleton } from "../common/utils"; @@ -140,7 +140,7 @@ export class WindowManager extends Singleton { protected bindEvents() { // track visible cluster from ui - subscribeToBroadcast(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => { + ipcMainOn(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => { this.activeClusterId = clusterId; }); } diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index 0a30db31a2..a067b520d7 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -20,8 +20,8 @@ */ import { computed, observable, makeObservable, action } from "mobx"; -import { subscribeToBroadcast } from "../../common/ipc"; -import { CatalogCategory, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../../common/catalog"; +import { ipcRendererOn } from "../../common/ipc"; +import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog"; import "../../common/catalog-entities"; import type { Cluster } from "../../main/cluster"; import { ClusterStore } from "../../common/cluster-store"; @@ -40,7 +40,7 @@ export class CatalogEntityRegistry { } init() { - subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => { + ipcRendererOn("catalog:items", (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.updateItems(items); }); } diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 2257f94df6..f9c3f5602c 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -27,7 +27,7 @@ import { observer } from "mobx-react"; import React from "react"; import { clusterActivateHandler } from "../../../common/cluster-ipc"; import { ClusterId, ClusterStore } from "../../../common/cluster-store"; -import { requestMain, subscribeToBroadcast } from "../../../common/ipc"; +import { ipcRendererOn, requestMain } from "../../../common/ipc"; import type { Cluster } from "../../../main/cluster"; import { cssNames, IClassName } from "../../utils"; import { Button } from "../button"; @@ -61,7 +61,7 @@ export class ClusterStatus extends React.Component { } async componentDidMount() { - subscribeToBroadcast(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyLog) => { + ipcRendererOn(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyLog) => { this.authOutput.push({ data: res.data.trimRight(), error: res.error, diff --git a/src/renderer/components/command-palette/command-container.tsx b/src/renderer/components/command-palette/command-container.tsx index b41edd42bb..b95d177891 100644 --- a/src/renderer/components/command-palette/command-container.tsx +++ b/src/renderer/components/command-palette/command-container.tsx @@ -26,7 +26,7 @@ import { observer } from "mobx-react"; import React from "react"; import { Dialog } from "../dialog"; import { EventEmitter } from "../../../common/event-emitter"; -import { subscribeToBroadcast } from "../../../common/ipc"; +import { ipcRendererOn } from "../../../common/ipc"; import { CommandDialog } from "./command-dialog"; import type { ClusterId } from "../../../common/cluster-store"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; @@ -85,7 +85,7 @@ export class CommandContainer extends React.Component { componentDidMount() { if (this.props.clusterId) { - subscribeToBroadcast(`command-palette:run-action:${this.props.clusterId}`, (event, commandId: string) => { + ipcRendererOn(`command-palette:run-action:${this.props.clusterId}`, (event, commandId: string) => { const command = this.findCommandById(commandId); if (command) { @@ -93,8 +93,8 @@ export class CommandContainer extends React.Component { } }); } else { - subscribeToBroadcast("command-palette:open", () => { - this.commandComponent = ; + ipcRendererOn("command-palette:open", () => { + CommandOverlay.open(); }); } window.addEventListener("keyup", (e) => this.escHandler(e), true); diff --git a/src/renderer/navigation/events.ts b/src/renderer/navigation/events.ts index 0adc53d7ae..f5a9968f4a 100644 --- a/src/renderer/navigation/events.ts +++ b/src/renderer/navigation/events.ts @@ -22,7 +22,7 @@ import { ipcRenderer } from "electron"; import { reaction } from "mobx"; import { getMatchedClusterId, navigate } from "./helpers"; -import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc"; +import { broadcastMessage, ipcRendererOn } from "../../common/ipc"; import logger from "../../main/logger"; export const enum IpcRendererNavigationEvents { @@ -45,7 +45,7 @@ export function bindEvents() { } // Reload dashboard window - subscribeToBroadcast(IpcRendererNavigationEvents.RELOAD_PAGE, () => { + ipcRendererOn(IpcRendererNavigationEvents.RELOAD_PAGE, () => { location.reload(); }); } @@ -60,7 +60,7 @@ function bindClusterManagerRouteEvents() { }); // Handle navigation via IPC - subscribeToBroadcast(IpcRendererNavigationEvents.NAVIGATE_IN_APP, (event, url: string) => { + ipcRendererOn(IpcRendererNavigationEvents.NAVIGATE_IN_APP, (event, url: string) => { logger.info(`[IPC]: ${event.type}: ${url}`, { currentLocation: location.href }); navigate(url); }); @@ -68,7 +68,7 @@ function bindClusterManagerRouteEvents() { // Handle cluster-view renderer process events within iframes function bindClusterFrameRouteEvents() { - subscribeToBroadcast(IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER, (event, url: string) => { + ipcRendererOn(IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER, (event, url: string) => { logger.info(`[IPC]: ${event.type}: ${url}`, { currentLocation: location.href }); navigate(url); });