mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fix reconnect, better handling no-clusters state
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
1 parent
0490dc1d12
commit
408f64c184
12 files changed
+145
-138
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import "./app.scss";
|
||||
import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { computed, observable, reaction } from "mobx";
|
||||
import { autorun, computed, observable } from "mobx";
|
||||
import { Redirect, Route, Switch } from "react-router";
|
||||
import { Notifications } from "./notifications";
|
||||
import { NotFound } from "./+404";
|
||||
@@ -28,45 +28,45 @@ import { CustomResources } from "./+custom-resources/custom-resources";
|
||||
import { crdRoute } from "./+custom-resources";
|
||||
import { isAllowedResource } from "../api/rbac";
|
||||
import { AddCluster, addClusterRoute } from "./+add-cluster";
|
||||
import { LandingPage, landingRoute, landingURL } from "./+landing-page";
|
||||
import { LandingPage, landingRoute } from "./+landing-page";
|
||||
import { ClusterSettings, clusterSettingsRoute } from "./+cluster-settings";
|
||||
import { Workspaces, workspacesRoute } from "./+workspaces";
|
||||
import { ErrorBoundary } from "./error-boundary";
|
||||
import { navigation } from "../navigation";
|
||||
import { clusterIpc } from "../../common/cluster-ipc";
|
||||
import { clusterStore } from "../../common/cluster-store";
|
||||
import { getHostedCluster } from "../../common/cluster-store";
|
||||
import { clusterStatusRoute, clusterStatusURL } from "./cluster-manager/cluster-status.route";
|
||||
import { Preferences, preferencesRoute } from "./+preferences";
|
||||
import { ClusterStatus } from "./cluster-manager/cluster-status";
|
||||
import { CubeSpinner } from "./spinner";
|
||||
import { navigate, navigation } from "../navigation";
|
||||
|
||||
@observer
|
||||
export class App extends React.Component {
|
||||
@observable isReady = false;
|
||||
|
||||
@computed get clusterReady() {
|
||||
const clusterId = location.hostname.split(".")[0];
|
||||
return !!clusterStore.getById(clusterId)?.isReady;
|
||||
@computed get clusterReady(): boolean {
|
||||
const cluster = getHostedCluster();
|
||||
if (cluster) {
|
||||
return cluster.initialized && cluster.accessible;
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await clusterIpc.activate.invokeFromRenderer();
|
||||
await clusterIpc.activate.invokeFromRenderer(); // refresh state, reconnect, etc.
|
||||
this.isReady = true;
|
||||
|
||||
disposeOnUnmount(this, [
|
||||
reaction(() => this.startURL, url => {
|
||||
const redirect = !this.clusterReady || !clusterStore.hasClusters();
|
||||
if (redirect) navigation.replace(url);
|
||||
}, {
|
||||
fireImmediately: true
|
||||
autorun(() => {
|
||||
if (!this.clusterReady) {
|
||||
navigate(clusterStatusURL());
|
||||
} else if (clusterStatusURL() == navigation.getPath()) {
|
||||
navigate("/"); // redirect when cluster accessible
|
||||
}
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
get startURL() {
|
||||
if (!clusterStore.hasClusters()) {
|
||||
return landingURL();
|
||||
}
|
||||
if (!this.clusterReady) {
|
||||
return clusterStatusURL();
|
||||
}
|
||||
|
||||
@@ -1,65 +1,76 @@
|
||||
import type { KubeAuthProxyResponse } from "../../../main/kube-auth-proxy";
|
||||
|
||||
import "./cluster-status.scss"
|
||||
import React from "react";
|
||||
import type { KubeAuthProxyResponse } from "../../../main/kube-auth-proxy";
|
||||
import { clusterStore } from "../../../common/cluster-store";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { computed, observable } from "mobx";
|
||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||
import { getHostedCluster } from "../../../common/cluster-store";
|
||||
import { Icon } from "../icon";
|
||||
import { Button } from "../button";
|
||||
import { cssNames } from "../../utils";
|
||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||
|
||||
@observer
|
||||
export class ClusterStatus extends React.Component {
|
||||
@observable authOutput: string[] = [];
|
||||
@observable hasErrors = false;
|
||||
@observable authOutput: KubeAuthProxyResponse[] = [];
|
||||
@observable isReconnecting = false;
|
||||
|
||||
get cluster() {
|
||||
return clusterStore.activeCluster;
|
||||
@computed get hasErrors() {
|
||||
return this.authOutput.some(({ error }) => error)
|
||||
}
|
||||
|
||||
get clusterId() {
|
||||
return clusterStore.activeClusterId;
|
||||
@computed get cluster() {
|
||||
return getHostedCluster()
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.authOutput = ["Connecting ...\n"];
|
||||
ipcRenderer.on(`kube-auth:${this.clusterId}`, (evt, { data, stream }: KubeAuthProxyResponse) => {
|
||||
this.authOutput.push(`[${stream}]: ${data}`);
|
||||
if (stream === "stderr") {
|
||||
this.hasErrors = true;
|
||||
}
|
||||
async componentDidMount() {
|
||||
this.authOutput = [{ data: "Connecting ...\n" }];
|
||||
ipcRenderer.on(`kube-auth:${this.cluster.id}`, (evt, res) => {
|
||||
this.authOutput.push(res);
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ipcRenderer.removeAllListeners(`kube-auth:${this.clusterId}`);
|
||||
ipcRenderer.removeAllListeners(`kube-auth:${this.cluster.id}`);
|
||||
}
|
||||
|
||||
reconnect = () => {
|
||||
this.authOutput = ["Reconnecting ...\n"];
|
||||
clusterIpc.reconnect.invokeFromRenderer(this.clusterId);
|
||||
reconnect = async () => {
|
||||
this.authOutput = [{ data: "Reconnecting ...\n" }];
|
||||
this.isReconnecting = true;
|
||||
await clusterIpc.activate.invokeFromRenderer();
|
||||
this.isReconnecting = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { authOutput, cluster, hasErrors } = this;
|
||||
const isDisconnected = !!cluster.disconnected;
|
||||
const isInactive = hasErrors || isDisconnected;
|
||||
return (
|
||||
<div className="ClusterStatus flex column gaps">
|
||||
{!hasErrors && <Icon material="cloud_queue"/>}
|
||||
{hasErrors && <Icon material="cloud_off" className="error"/>}
|
||||
{cluster && <h2>{cluster.contextName}</h2>}
|
||||
<pre className="kube-auth-out">
|
||||
{authOutput.map((data, index) => {
|
||||
const error = data.startsWith("[stderr]");
|
||||
return <p key={index} className={cssNames({ error })}>{data}</p>
|
||||
})}
|
||||
</pre>
|
||||
{hasErrors && (
|
||||
{isInactive && (
|
||||
<Icon
|
||||
material="cloud_off"
|
||||
className={cssNames({ error: hasErrors })}
|
||||
/>
|
||||
)}
|
||||
<h2>
|
||||
{cluster.contextName}
|
||||
</h2>
|
||||
{!isDisconnected && (
|
||||
<pre className="kube-auth-out">
|
||||
{authOutput.map(({ data, error }, index) => {
|
||||
return <p key={index} className={cssNames({ error })}>{data}</p>
|
||||
})}
|
||||
</pre>
|
||||
)}
|
||||
{isInactive && (
|
||||
<Button
|
||||
primary className="box center"
|
||||
primary
|
||||
label="Reconnect"
|
||||
className="box center"
|
||||
onClick={this.reconnect}
|
||||
waiting={this.isReconnecting}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { landingURL } from "../+landing-page";
|
||||
import { Tooltip, TooltipContent } from "../tooltip";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||
import { clusterStatusURL } from "./cluster-status.route";
|
||||
|
||||
// fixme: allow to rearrange clusters with drag&drop
|
||||
|
||||
@@ -54,9 +55,9 @@ export class ClustersMenu extends React.Component<Props> {
|
||||
if (cluster.online) {
|
||||
menu.append(new MenuItem({
|
||||
label: _i18n._(t`Disconnect`),
|
||||
click: () => {
|
||||
navigate(landingURL());
|
||||
clusterIpc.disconnect.invokeFromRenderer();
|
||||
click: async () => {
|
||||
await clusterIpc.disconnect.invokeFromRenderer();
|
||||
navigate(clusterStatusURL());
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user