diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 0b77a370b1..f476965736 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -93,6 +93,10 @@ export class BaseStore extends Singleton { } } + unregisterIpcListener() { + ipcRenderer.removeAllListeners(this.syncChannel) + } + disableSync() { this.syncDisposers.forEach(dispose => dispose()); this.syncDisposers.length = 0; diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index f48ce0f9c4..e11a232ea3 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -9,7 +9,7 @@ export const clusterIpc = { const cluster = clusterStore.getById(clusterId); if (cluster) { if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates - return cluster.activate(true); + return cluster.activate(); } }, }), @@ -58,4 +58,4 @@ export const clusterIpc = { return clusterStore.getById(clusterId)?.upgradeFeature(feature, config) } }), -} \ No newline at end of file +} diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index d75de14609..6cfc4fb7db 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -1,5 +1,5 @@ import path from "path"; -import { app, ipcRenderer, remote } from "electron"; +import { app, ipcRenderer, remote, webFrame, webContents } from "electron"; import { unlink } from "fs-extra"; import { action, computed, observable, toJS } from "mobx"; import { BaseStore } from "./base-store"; @@ -73,20 +73,27 @@ export class ClusterStore extends BaseStore { accessPropertiesByDotNotation: false, // To make dots safe in cluster context names migrations: migrations, }); - if (ipcRenderer) { - ipcRenderer.on("cluster:state", (event, model: ClusterState) => { - this.applyWithoutSync(() => { - logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host}`, model); - this.getById(model.id)?.updateModel(model); - }) - }) - } } @observable activeClusterId: ClusterId; @observable removedClusters = observable.map(); @observable clusters = observable.map(); + registerIpcListener() { + logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`) + ipcRenderer.on("cluster:state", (event, model: ClusterState) => { + this.applyWithoutSync(() => { + logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, model); + this.getById(model.id)?.updateModel(model); + }) + }) + } + + unregisterIpcListener() { + super.unregisterIpcListener() + ipcRenderer.removeAllListeners("cluster:state") + } + @computed get activeCluster(): Cluster | null { return this.getById(this.activeClusterId); } diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 9fc9172296..12acb793ab 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -56,10 +56,10 @@ export class Cluster implements ClusterModel { @observable kubeConfigPath: string; @observable apiUrl: string; // cluster server url @observable kubeProxyUrl: string; // lens-proxy to kube-api url - @observable online: boolean; - @observable accessible: boolean; - @observable ready: boolean; - @observable disconnected: boolean; + @observable online = false; + @observable accessible = false; + @observable ready = false; + @observable disconnected = true; @observable failureReason: string; @observable nodes = 0; @observable version: string; @@ -124,13 +124,14 @@ export class Cluster implements ClusterModel { this.eventDisposers.length = 0; } - async activate(init = false) { + @action + async activate() { logger.info(`[CLUSTER]: activate`, this.getMeta()); await this.whenInitialized; if (!this.eventDisposers.length) { this.bindEvents(); } - if (this.disconnected || (!init && !this.accessible)) { + if (this.disconnected || !this.accessible) { await this.reconnect(); } await this.refreshConnectionStatus() @@ -143,6 +144,7 @@ export class Cluster implements ClusterModel { return this.pushState(); } + @action async reconnect() { logger.info(`[CLUSTER]: reconnect`, this.getMeta()); this.contextHandler.stopServer(); diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts index 765b3f4d1a..9801f776d0 100644 --- a/src/main/lens-proxy.ts +++ b/src/main/lens-proxy.ts @@ -134,7 +134,6 @@ export class LensProxy { protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) { const cluster = this.clusterManager.getClusterForRequest(req) if (cluster) { - await cluster.contextHandler.ensureServer(); const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler) if (proxyTarget) { // allow to fetch apis in "clusterId.localhost:port" from "localhost:port" diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index e42a456884..37fdc35e03 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -1,6 +1,6 @@ import "./components/app.scss" import React from "react"; -import { render } from "react-dom"; +import { render, unmountComponentAtNode } from "react-dom"; import { isMac } from "../common/vars"; import { userStore } from "../common/user-store"; import { workspaceStore } from "../common/workspace-store"; @@ -27,10 +27,22 @@ export async function bootstrap(App: AppComponent) { themeStore.init(), ]); + // Register additional store listeners + clusterStore.registerIpcListener(); + // init app's dependencies if any if (App.init) { await App.init(); } + window.addEventListener("message", (ev: MessageEvent) => { + if (ev.data === "teardown") { + userStore.unregisterIpcListener() + workspaceStore.unregisterIpcListener() + clusterStore.unregisterIpcListener() + unmountComponentAtNode(rootElem) + window.location.href = "about:blank" + } + }) render(<> {isMac &&
} diff --git a/src/renderer/components/cluster-manager/lens-views.ts b/src/renderer/components/cluster-manager/lens-views.ts index 370ed665c3..d23bdd6072 100644 --- a/src/renderer/components/cluster-manager/lens-views.ts +++ b/src/renderer/components/cluster-manager/lens-views.ts @@ -19,8 +19,11 @@ export async function initView(clusterId: ClusterId) { if (!clusterId || lensViews.has(clusterId)) { return; } - logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`) const cluster = clusterStore.getById(clusterId); + if (!cluster) { + return; + } + logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`) const parentElem = document.getElementById("lens-views"); const iframe = document.createElement("iframe"); iframe.name = cluster.contextName; @@ -42,9 +45,9 @@ export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrame // Keep frame in DOM to avoid possible bugs when same cluster re-created after being removed. // In that case for some reasons `webFrame.routingId` returns some previous frameId (usage in app.tsx) // Issue: https://github.com/lensapp/lens/issues/811 - iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}`; - iframe.removeAttribute("src") + iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}` iframe.removeAttribute("name") + iframe.contentWindow.postMessage("teardown", "*") } export function refreshViews() {