1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix iframe ipc flakyness after cluster is removed (#954)

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2020-09-24 15:24:39 +03:00 committed by GitHub
parent 386e7c63bb
commit 11ea9d2098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 22 deletions

View File

@ -93,6 +93,10 @@ export class BaseStore<T = any> extends Singleton {
} }
} }
unregisterIpcListener() {
ipcRenderer.removeAllListeners(this.syncChannel)
}
disableSync() { disableSync() {
this.syncDisposers.forEach(dispose => dispose()); this.syncDisposers.forEach(dispose => dispose());
this.syncDisposers.length = 0; this.syncDisposers.length = 0;

View File

@ -9,7 +9,7 @@ export const clusterIpc = {
const cluster = clusterStore.getById(clusterId); const cluster = clusterStore.getById(clusterId);
if (cluster) { if (cluster) {
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates 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) return clusterStore.getById(clusterId)?.upgradeFeature(feature, config)
} }
}), }),
} }

View File

@ -1,5 +1,5 @@
import path from "path"; import path from "path";
import { app, ipcRenderer, remote } from "electron"; import { app, ipcRenderer, remote, webFrame, webContents } from "electron";
import { unlink } from "fs-extra"; import { unlink } from "fs-extra";
import { action, computed, observable, toJS } from "mobx"; import { action, computed, observable, toJS } from "mobx";
import { BaseStore } from "./base-store"; import { BaseStore } from "./base-store";
@ -73,20 +73,27 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: migrations, 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 activeClusterId: ClusterId;
@observable removedClusters = observable.map<ClusterId, Cluster>(); @observable removedClusters = observable.map<ClusterId, Cluster>();
@observable clusters = observable.map<ClusterId, Cluster>(); @observable clusters = observable.map<ClusterId, Cluster>();
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 { @computed get activeCluster(): Cluster | null {
return this.getById(this.activeClusterId); return this.getById(this.activeClusterId);
} }

View File

@ -56,10 +56,10 @@ export class Cluster implements ClusterModel {
@observable kubeConfigPath: string; @observable kubeConfigPath: string;
@observable apiUrl: string; // cluster server url @observable apiUrl: string; // cluster server url
@observable kubeProxyUrl: string; // lens-proxy to kube-api url @observable kubeProxyUrl: string; // lens-proxy to kube-api url
@observable online: boolean; @observable online = false;
@observable accessible: boolean; @observable accessible = false;
@observable ready: boolean; @observable ready = false;
@observable disconnected: boolean; @observable disconnected = true;
@observable failureReason: string; @observable failureReason: string;
@observable nodes = 0; @observable nodes = 0;
@observable version: string; @observable version: string;
@ -124,13 +124,14 @@ export class Cluster implements ClusterModel {
this.eventDisposers.length = 0; this.eventDisposers.length = 0;
} }
async activate(init = false) { @action
async activate() {
logger.info(`[CLUSTER]: activate`, this.getMeta()); logger.info(`[CLUSTER]: activate`, this.getMeta());
await this.whenInitialized; await this.whenInitialized;
if (!this.eventDisposers.length) { if (!this.eventDisposers.length) {
this.bindEvents(); this.bindEvents();
} }
if (this.disconnected || (!init && !this.accessible)) { if (this.disconnected || !this.accessible) {
await this.reconnect(); await this.reconnect();
} }
await this.refreshConnectionStatus() await this.refreshConnectionStatus()
@ -143,6 +144,7 @@ export class Cluster implements ClusterModel {
return this.pushState(); return this.pushState();
} }
@action
async reconnect() { async reconnect() {
logger.info(`[CLUSTER]: reconnect`, this.getMeta()); logger.info(`[CLUSTER]: reconnect`, this.getMeta());
this.contextHandler.stopServer(); this.contextHandler.stopServer();

View File

@ -134,7 +134,6 @@ export class LensProxy {
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) { protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) {
const cluster = this.clusterManager.getClusterForRequest(req) const cluster = this.clusterManager.getClusterForRequest(req)
if (cluster) { if (cluster) {
await cluster.contextHandler.ensureServer();
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler) const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler)
if (proxyTarget) { if (proxyTarget) {
// allow to fetch apis in "clusterId.localhost:port" from "localhost:port" // allow to fetch apis in "clusterId.localhost:port" from "localhost:port"

View File

@ -1,6 +1,6 @@
import "./components/app.scss" import "./components/app.scss"
import React from "react"; import React from "react";
import { render } from "react-dom"; import { render, unmountComponentAtNode } from "react-dom";
import { isMac } from "../common/vars"; import { isMac } from "../common/vars";
import { userStore } from "../common/user-store"; import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store"; import { workspaceStore } from "../common/workspace-store";
@ -27,10 +27,22 @@ export async function bootstrap(App: AppComponent) {
themeStore.init(), themeStore.init(),
]); ]);
// Register additional store listeners
clusterStore.registerIpcListener();
// init app's dependencies if any // init app's dependencies if any
if (App.init) { if (App.init) {
await 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(<> render(<>
{isMac && <div id="draggable-top" />} {isMac && <div id="draggable-top" />}
<App /> <App />

View File

@ -19,8 +19,11 @@ export async function initView(clusterId: ClusterId) {
if (!clusterId || lensViews.has(clusterId)) { if (!clusterId || lensViews.has(clusterId)) {
return; return;
} }
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`)
const cluster = clusterStore.getById(clusterId); const cluster = clusterStore.getById(clusterId);
if (!cluster) {
return;
}
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`)
const parentElem = document.getElementById("lens-views"); const parentElem = document.getElementById("lens-views");
const iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
iframe.name = cluster.contextName; 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. // 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) // In that case for some reasons `webFrame.routingId` returns some previous frameId (usage in app.tsx)
// Issue: https://github.com/lensapp/lens/issues/811 // Issue: https://github.com/lensapp/lens/issues/811
iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}`; iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}`
iframe.removeAttribute("src")
iframe.removeAttribute("name") iframe.removeAttribute("name")
iframe.contentWindow.postMessage("teardown", "*")
} }
export function refreshViews() { export function refreshViews() {