diff --git a/src/common/ipc/catalog.ts b/src/common/ipc/catalog.ts new file mode 100644 index 0000000000..7477648594 --- /dev/null +++ b/src/common/ipc/catalog.ts @@ -0,0 +1,32 @@ +/** + * 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. + */ + +export enum CatalogIpcEvents { + /** + * This is broadcast on whenever there is an update to any catalog item + */ + ITEMS = "catalog:items", + + /** + * This can be sent from renderer to main to initialize a broadcast of ITEMS + */ + INIT = "catalog:init", +} diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 43d3eece63..b7d53402ed 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -25,10 +25,10 @@ import { isEqual } from "lodash"; import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx"; import path from "path"; import { AppPaths } from "../../common/app-paths"; -import { ClusterStore } from "../../common/cluster-store"; import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../../common/ipc"; -import { Disposer, getHostedClusterId, toJS } from "../../common/utils"; +import { Disposer, toJS } from "../../common/utils"; import logger from "../../main/logger"; +import type { KubernetesCluster } from "../common-api/catalog"; import type { InstalledExtension } from "../extension-discovery"; import { ExtensionsStore } from "../extensions-store"; import type { LensExtension, LensExtensionConstructor, LensExtensionId } from "../lens-extension"; @@ -280,12 +280,11 @@ export class ExtensionLoader { }); } - loadOnClusterRenderer() { + loadOnClusterRenderer(entity: KubernetesCluster) { logger.debug(`${logModule}: load on cluster renderer (dashboard)`); - const cluster = ClusterStore.getInstance().getById(getHostedClusterId()); this.autoInitExtensions(async (extension: LensRendererExtension) => { - if ((await extension.isEnabledForCluster(cluster)) === false) { + if ((await extension.isEnabledForCluster(entity)) === false) { return []; } diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 4a5abadbad..67567969ee 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -20,13 +20,13 @@ */ import type * as registries from "./registries"; -import type { Cluster } from "../main/cluster"; import { Disposers, LensExtension } from "./lens-extension"; import { getExtensionPageUrl } from "./registries/page-registry"; import type { CatalogEntity } from "../common/catalog"; import type { Disposer } from "../common/utils"; import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-entity-registry"; import { catalogCategoryRegistry, CategoryFilter } from "../renderer/api/catalog-category-registry"; +import type { KubernetesCluster } from "../common/catalog-entities"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -57,9 +57,12 @@ export class LensRendererExtension extends LensExtension { } /** - * Defines if extension is enabled for a given cluster. Defaults to `true`. + * Defines if extension is enabled for a given cluster. This method is only + * called when the extension is created within a cluster frame. + * + * The default implementation is to return `true` */ - async isEnabledForCluster(cluster: Cluster): Promise { + async isEnabledForCluster(cluster: KubernetesCluster): Promise { return (void cluster) || true; } diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index b662c36f69..b86a03c7b1 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -20,22 +20,25 @@ */ import { reaction } from "mobx"; -import { broadcastMessage } from "../common/ipc"; +import { broadcastMessage, ipcMainOn } from "../common/ipc"; import type { CatalogEntityRegistry } from "./catalog"; import "../common/catalog-entities/kubernetes-cluster"; -import { toJS } from "../common/utils"; +import { disposer, toJS } from "../common/utils"; import { debounce } from "lodash"; import type { CatalogEntity } from "../common/catalog"; - +import { CatalogIpcEvents } from "../common/ipc/catalog"; const broadcaster = debounce((items: CatalogEntity[]) => { - broadcastMessage("catalog:items", items); + broadcastMessage(CatalogIpcEvents.ITEMS, items); }, 1_000, { leading: true, trailing: true }); export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) { - return reaction(() => toJS(catalog.items), (items) => { - broadcaster(items); - }, { - fireImmediately: true, - }); + return disposer( + ipcMainOn(CatalogIpcEvents.INIT, () => broadcaster(toJS(catalog.items))), + reaction(() => toJS(catalog.items), (items) => { + broadcaster(items); + }, { + fireImmediately: true, + }), + ); } diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index abeaae6f68..4885c8f265 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -30,6 +30,8 @@ import { once } from "lodash"; import logger from "../../common/logger"; import { catalogEntityRunContext } from "./catalog-entity"; import { CatalogRunEvent } from "../../common/catalog/catalog-run-event"; +import { ipcRenderer } from "electron"; +import { CatalogIpcEvents } from "../../common/ipc/catalog"; export type EntityFilter = (entity: CatalogEntity) => any; export type CatalogEntityOnBeforeRun = (event: CatalogRunEvent) => void | Promise; @@ -70,9 +72,12 @@ export class CatalogEntityRegistry { } init() { - ipcRendererOn("catalog:items", (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { + ipcRendererOn(CatalogIpcEvents.ITEMS, (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.updateItems(items); }); + + // Make sure that we get items ASAP and not the next time one of them changes + ipcRenderer.send(CatalogIpcEvents.INIT); } @action updateItems(items: (CatalogEntityData & CatalogEntityKindData)[]) { diff --git a/src/renderer/cluster-frame.tsx b/src/renderer/cluster-frame.tsx index fe867da04a..5113377116 100755 --- a/src/renderer/cluster-frame.tsx +++ b/src/renderer/cluster-frame.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import React from "react"; -import { observable, makeObservable } from "mobx"; +import { observable, makeObservable, when } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { Redirect, Route, Router, Switch } from "react-router"; import { history } from "./navigation"; @@ -74,6 +74,7 @@ import { PortForwardDialog } from "./port-forward"; import { DeleteClusterDialog } from "./components/delete-cluster-dialog"; import { WorkloadsOverview } from "./components/+workloads-overview/overview"; import { KubeObjectListLayout } from "./components/kube-object-list-layout"; +import type { KubernetesCluster } from "../common/catalog-entities"; @observer export class ClusterFrame extends React.Component { @@ -101,7 +102,18 @@ export class ClusterFrame extends React.Component { catalogEntityRegistry.activeEntity = ClusterFrame.clusterId; - extensionLoader.loadOnClusterRenderer(); + // Only load the extensions once the catalog has been populated + when( + () => Boolean(catalogEntityRegistry.activeEntity), + () => extensionLoader.loadOnClusterRenderer(catalogEntityRegistry.activeEntity as KubernetesCluster), + { + timeout: 15_000, + onError: (error) => { + console.warn("[CLUSTER-FRAME]: error from activeEntity when()", error); + Notifications.error("Failed to get KubernetesCluster for this view. Extensions will not be loaded."); + }, + }, + ); setTimeout(() => { appEventBus.emit({