mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
refactoring, fixes
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
25dd645724
commit
ffa0f8814e
@ -3,14 +3,10 @@ import { ClusterId, clusterStore } from "./cluster-store";
|
|||||||
import { tracker } from "./tracker";
|
import { tracker } from "./tracker";
|
||||||
|
|
||||||
export const clusterIpc = {
|
export const clusterIpc = {
|
||||||
refresh: createIpcChannel({
|
activate: createIpcChannel({
|
||||||
channel: "cluster:refresh",
|
channel: "cluster:activate",
|
||||||
handle: async (clusterId: ClusterId = clusterStore.activeClusterId) => {
|
handle: async (clusterId: ClusterId = clusterStore.activeClusterId) => {
|
||||||
const cluster = clusterStore.getById(clusterId);
|
return clusterStore.getById(clusterId)?.activate();
|
||||||
if (cluster) {
|
|
||||||
await cluster.refreshStatus();
|
|
||||||
return cluster.pushState();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type http from "http"
|
import type http from "http"
|
||||||
import { autorun, reaction } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
import { ClusterId, clusterStore } from "../common/cluster-store"
|
import { ClusterId, clusterStore } from "../common/cluster-store"
|
||||||
import { Cluster } from "./cluster"
|
import { Cluster } from "./cluster"
|
||||||
@ -7,52 +7,36 @@ import { clusterIpc } from "../common/cluster-ipc";
|
|||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
|
|
||||||
export class ClusterManager {
|
export class ClusterManager {
|
||||||
protected activeClusterId: ClusterId;
|
|
||||||
|
|
||||||
constructor(public readonly port: number) {
|
constructor(public readonly port: number) {
|
||||||
this.activeClusterId = clusterStore.activeClusterId;
|
|
||||||
|
|
||||||
// auto-init clusters
|
// auto-init clusters
|
||||||
autorun(() => {
|
autorun(() => {
|
||||||
clusterStore.clusters.forEach(cluster => {
|
clusterStore.clusters.forEach(cluster => {
|
||||||
if (!cluster.initialized) {
|
if (!cluster.initialized) {
|
||||||
logger.info(`[CLUSTER-MANAGER]: initializing cluster`, cluster.getMeta());
|
logger.info(`[CLUSTER-MANAGER]: initializing cluster`, cluster.getMeta());
|
||||||
cluster.init(port);
|
cluster.init(port); // connect to kube-auth-proxy, context handling
|
||||||
|
cluster.bindEvents(); // send push-updates to renderer
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// auto-bind events for active cluster
|
|
||||||
reaction(() => clusterStore.activeCluster, activeCluster => {
|
|
||||||
const prevCluster = clusterStore.getById(this.activeClusterId);
|
|
||||||
if (prevCluster) {
|
|
||||||
prevCluster.unbindEvents();
|
|
||||||
}
|
|
||||||
if (activeCluster) {
|
|
||||||
this.activeClusterId = activeCluster.id;
|
|
||||||
activeCluster.bindEvents();
|
|
||||||
activeCluster.refreshStatus();
|
|
||||||
activeCluster.pushState();
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
fireImmediately: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// auto-stop removed clusters
|
// auto-stop removed clusters
|
||||||
autorun(() => {
|
autorun(() => {
|
||||||
const { removedClusters } = clusterStore;
|
const removedClusters = Array.from(clusterStore.removedClusters.values());
|
||||||
if (removedClusters.size > 0) {
|
if (removedClusters.length > 0) {
|
||||||
const meta = Array.from(removedClusters.values()).map(cluster => cluster.getMeta());
|
const meta = removedClusters.map(cluster => cluster.getMeta());
|
||||||
logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
|
logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
|
||||||
removedClusters.forEach(cluster => cluster.disconnect());
|
removedClusters.forEach(cluster => {
|
||||||
removedClusters.clear();
|
cluster.disconnect();
|
||||||
|
cluster.unbindEvents();
|
||||||
|
});
|
||||||
|
clusterStore.removedClusters.clear();
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
delay: 250
|
delay: 250
|
||||||
});
|
});
|
||||||
|
|
||||||
// listen for ipc-events that must/can be handled *only* in main-process (nodeIntegration=true)
|
// listen for ipc-events that must/can be handled *only* in main-process (nodeIntegration=true)
|
||||||
clusterIpc.refresh.handleInMain();
|
clusterIpc.activate.handleInMain();
|
||||||
clusterIpc.disconnect.handleInMain();
|
clusterIpc.disconnect.handleInMain();
|
||||||
clusterIpc.reconnect.handleInMain();
|
clusterIpc.reconnect.handleInMain();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,7 @@ export class Cluster implements ClusterModel {
|
|||||||
@observable webContentUrl: string; // page content url for loading in renderer
|
@observable webContentUrl: string; // page content url for loading in renderer
|
||||||
@observable online: boolean;
|
@observable online: boolean;
|
||||||
@observable accessible: boolean;
|
@observable accessible: boolean;
|
||||||
|
@observable disconnected: boolean;
|
||||||
@observable failureReason: string;
|
@observable failureReason: string;
|
||||||
@observable nodes = 0;
|
@observable nodes = 0;
|
||||||
@observable version: string;
|
@observable version: string;
|
||||||
@ -75,6 +76,9 @@ export class Cluster implements ClusterModel {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
async init(port: number) {
|
async init(port: number) {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this.contextHandler = new ContextHandler(this);
|
this.contextHandler = new ContextHandler(this);
|
||||||
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler);
|
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler);
|
||||||
@ -97,11 +101,11 @@ export class Cluster implements ClusterModel {
|
|||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
logger.info(`[CLUSTER]: bind events`, this.getMeta());
|
logger.info(`[CLUSTER]: bind events`, this.getMeta());
|
||||||
const refreshStatusTimer = setInterval(() => this.refreshStatus(), 30000); // every 30s
|
const refreshTimer = setInterval(() => this.online && this.refresh(), 30000); // every 30s
|
||||||
const refreshEventsTimer = setInterval(() => this.refreshEvents(), 3000); // every 3s
|
const refreshEventsTimer = setInterval(() => this.online && this.refreshEvents(), 3000); // every 3s
|
||||||
|
|
||||||
this.disposers.push(
|
this.disposers.push(
|
||||||
() => clearInterval(refreshStatusTimer),
|
() => clearInterval(refreshTimer),
|
||||||
() => clearInterval(refreshEventsTimer),
|
() => clearInterval(refreshEventsTimer),
|
||||||
reaction(this.getState, this.pushState, {
|
reaction(this.getState, this.pushState, {
|
||||||
fireImmediately: true
|
fireImmediately: true
|
||||||
@ -115,22 +119,32 @@ export class Cluster implements ClusterModel {
|
|||||||
this.disposers.length = 0;
|
this.disposers.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: possibly doesn't work as expected
|
async activate() {
|
||||||
|
await when(() => this.initialized);
|
||||||
|
if (this.disconnected) await this.reconnect();
|
||||||
|
await this.refresh();
|
||||||
|
return this.pushState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check, possibly doesn't work as expected
|
||||||
async reconnect() {
|
async reconnect() {
|
||||||
logger.info(`[CLUSTER]: reconnect`, this.getMeta());
|
logger.info(`[CLUSTER]: reconnect`, this.getMeta());
|
||||||
|
this.disconnected = false;
|
||||||
await this.contextHandler.stopServer();
|
await this.contextHandler.stopServer();
|
||||||
await this.contextHandler.ensureServer();
|
await this.contextHandler.ensureServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
disconnect() {
|
disconnect() {
|
||||||
logger.info(`[CLUSTER]: disconnect`, this.getMeta());
|
logger.info(`[CLUSTER]: disconnect`, this.getMeta());
|
||||||
|
this.disconnected = true;
|
||||||
|
this.online = false;
|
||||||
|
this.accessible = false;
|
||||||
this.contextHandler.stopServer();
|
this.contextHandler.stopServer();
|
||||||
this.unbindEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async refreshStatus() {
|
async refresh() {
|
||||||
await when(() => this.initialized);
|
|
||||||
logger.info(`[CLUSTER]: refreshing status`, this.getMeta());
|
logger.info(`[CLUSTER]: refreshing status`, this.getMeta());
|
||||||
const connectionStatus = await this.getConnectionStatus();
|
const connectionStatus = await this.getConnectionStatus();
|
||||||
this.online = connectionStatus > ClusterStatus.Offline;
|
this.online = connectionStatus > ClusterStatus.Offline;
|
||||||
@ -351,6 +365,7 @@ export class Cluster implements ClusterModel {
|
|||||||
name: this.contextName,
|
name: this.contextName,
|
||||||
initialized: this.initialized,
|
initialized: this.initialized,
|
||||||
accessible: this.accessible,
|
accessible: this.accessible,
|
||||||
|
online: this.online,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import type { ClusterId } from "../common/cluster-store";
|
|||||||
import { clusterStore } from "../common/cluster-store";
|
import { clusterStore } from "../common/cluster-store";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
|
|
||||||
// fixme: error when removing active cluster (can't activate next view => empty window)
|
|
||||||
// fixme: remove switching view delay on first load
|
// fixme: remove switching view delay on first load
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
@ -81,7 +80,7 @@ export class WindowManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const activeView = this.activeView;
|
const prevActiveView = this.activeView;
|
||||||
const isLoadedBefore = !!this.getView(clusterId);
|
const isLoadedBefore = !!this.getView(clusterId);
|
||||||
const view = this.initView(clusterId);
|
const view = this.initView(clusterId);
|
||||||
logger.info(`[WINDOW-MANAGER]: activating cluster view`, {
|
logger.info(`[WINDOW-MANAGER]: activating cluster view`, {
|
||||||
@ -90,7 +89,8 @@ export class WindowManager {
|
|||||||
contextName: cluster.contextName,
|
contextName: cluster.contextName,
|
||||||
isLoadedBefore: isLoadedBefore,
|
isLoadedBefore: isLoadedBefore,
|
||||||
});
|
});
|
||||||
if (activeView !== view) {
|
if (prevActiveView !== view) {
|
||||||
|
cluster.activate(); // refresh + reconnect when required
|
||||||
this.activeView = view;
|
this.activeView = view;
|
||||||
if (!isLoadedBefore) {
|
if (!isLoadedBefore) {
|
||||||
await when(() => cluster.initialized);
|
await when(() => cluster.initialized);
|
||||||
@ -98,9 +98,9 @@ export class WindowManager {
|
|||||||
this.hideSplash();
|
this.hideSplash();
|
||||||
}
|
}
|
||||||
// refresh position and hide previous active window
|
// refresh position and hide previous active window
|
||||||
if (activeView) {
|
if (prevActiveView) {
|
||||||
view.setBounds(activeView.getBounds());
|
view.setBounds(prevActiveView.getBounds());
|
||||||
activeView.hide();
|
prevActiveView.hide();
|
||||||
}
|
}
|
||||||
view.show();
|
view.show();
|
||||||
return view.id;
|
return view.id;
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import { tracker } from "../../../common/tracker";
|
|||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
import { workspaceStore } from "../../../common/workspace-store";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from "uuid"
|
||||||
import { navigate } from "../../navigation";
|
import { navigation } from "../../navigation";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class AddCluster extends React.Component {
|
export class AddCluster extends React.Component {
|
||||||
@ -94,7 +94,7 @@ export class AddCluster extends React.Component {
|
|||||||
httpsProxy: proxyServer || undefined,
|
httpsProxy: proxyServer || undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
navigate("/");
|
navigation.goBack(); // return to previous opened page for the cluster view
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error = String(err);
|
this.error = String(err);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export class ClusterManager extends React.Component<Props> {
|
|||||||
@observable isReady = false;
|
@observable isReady = false;
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
clusterIpc.refresh.invokeFromRenderer();
|
await clusterIpc.activate.invokeFromRenderer();
|
||||||
await App.init();
|
await App.init();
|
||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { clusterIpc } from "../../../common/cluster-ipc";
|
|||||||
@observer
|
@observer
|
||||||
export class ClusterStatus extends React.Component {
|
export class ClusterStatus extends React.Component {
|
||||||
@observable authOutput: string[] = [];
|
@observable authOutput: string[] = [];
|
||||||
|
@observable hasErrors = false;
|
||||||
|
|
||||||
get cluster() {
|
get cluster() {
|
||||||
return clusterStore.activeCluster;
|
return clusterStore.activeCluster;
|
||||||
@ -26,6 +27,9 @@ export class ClusterStatus extends React.Component {
|
|||||||
this.authOutput = ["Connecting ...\n"];
|
this.authOutput = ["Connecting ...\n"];
|
||||||
ipcRenderer.on(`kube-auth:${this.clusterId}`, (evt, { data, stream }: KubeAuthProxyResponse) => {
|
ipcRenderer.on(`kube-auth:${this.clusterId}`, (evt, { data, stream }: KubeAuthProxyResponse) => {
|
||||||
this.authOutput.push(`[${stream}]: ${data}`);
|
this.authOutput.push(`[${stream}]: ${data}`);
|
||||||
|
if (stream === "stderr") {
|
||||||
|
this.hasErrors = true;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,20 +43,21 @@ export class ClusterStatus extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { authOutput, cluster } = this;
|
const { authOutput, cluster, hasErrors } = this;
|
||||||
const isError = cluster?.accessible === false;
|
|
||||||
return (
|
return (
|
||||||
<div className="ClusterStatus flex column gaps">
|
<div className="ClusterStatus flex column gaps">
|
||||||
{!isError && <Icon material="cloud_queue"/>}
|
{!hasErrors && <Icon material="cloud_queue"/>}
|
||||||
{isError && <Icon material="cloud_off" className="error"/>}
|
{hasErrors && <Icon material="cloud_off" className="error"/>}
|
||||||
<h2>{cluster?.contextName}</h2>
|
<h2>
|
||||||
|
{cluster?.contextName}
|
||||||
|
</h2>
|
||||||
<pre className="kube-auth-out">
|
<pre className="kube-auth-out">
|
||||||
{authOutput.map((data, index) => {
|
{authOutput.map((data, index) => {
|
||||||
const error = data.startsWith("[stderr]");
|
const error = data.startsWith("[stderr]");
|
||||||
return <p key={index} className={cssNames({ error })}>{data}</p>
|
return <p key={index} className={cssNames({ error })}>{data}</p>
|
||||||
})}
|
})}
|
||||||
</pre>
|
</pre>
|
||||||
{isError && (
|
{hasErrors && (
|
||||||
<Button
|
<Button
|
||||||
primary className="box center"
|
primary className="box center"
|
||||||
label="Reconnect"
|
label="Reconnect"
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import { Tooltip, TooltipContent } from "../tooltip";
|
|||||||
import { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
|
|
||||||
// fixme: refresh all cluster-icon badges in background (events-count per cluster)
|
|
||||||
// fixme: allow to rearrange clusters with drag&drop
|
// fixme: allow to rearrange clusters with drag&drop
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -101,13 +100,12 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
)}
|
)}
|
||||||
<div className="clusters flex column gaps">
|
<div className="clusters flex column gaps">
|
||||||
{clusters.map(cluster => {
|
{clusters.map(cluster => {
|
||||||
const isActive = cluster.isReady && cluster.id === clusterStore.activeClusterId;
|
|
||||||
return (
|
return (
|
||||||
<ClusterIcon
|
<ClusterIcon
|
||||||
key={cluster.id}
|
key={cluster.id}
|
||||||
showErrors={true}
|
showErrors={true}
|
||||||
cluster={cluster}
|
cluster={cluster}
|
||||||
isActive={isActive}
|
isActive={cluster.id === clusterStore.activeClusterId}
|
||||||
onClick={() => this.showCluster(cluster.id)}
|
onClick={() => this.showCluster(cluster.id)}
|
||||||
onContextMenu={() => this.showContextMenu(cluster)}
|
onContextMenu={() => this.showContextMenu(cluster)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user