1
0
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:
Roman 2020-07-29 14:57:27 +03:00
parent 2054f38d08
commit 3a115ae485
6 changed files with 80 additions and 43 deletions

View File

@ -153,10 +153,8 @@ export class Cluster implements ClusterModel {
@action
async refresh() {
logger.info(`[CLUSTER]: refreshing status`, this.getMeta());
const connectionStatus = await this.getConnectionStatus();
this.online = connectionStatus > ClusterStatus.Offline;
this.accessible = connectionStatus == ClusterStatus.AccessGranted;
logger.info(`[CLUSTER]: refresh`, this.getMeta());
await this.refreshConnectionStatus();
if (this.accessible) {
this.kubeCtl = new Kubectl(this.version)
this.distribution = this.detectKubernetesDistribution(this.version)
@ -169,11 +167,18 @@ export class Cluster implements ClusterModel {
this.features = features;
this.isAdmin = isAdmin;
this.nodes = nodesCount;
await Promise.all([
this.refreshEvents(),
this.refreshAllowedResources(),
]);
}
await Promise.all([
this.refreshEvents(),
this.refreshAllowedResources(),
]);
}
@action
async refreshConnectionStatus() {
const connectionStatus = await this.getConnectionStatus();
this.online = connectionStatus > ClusterStatus.Offline;
this.accessible = connectionStatus == ClusterStatus.AccessGranted;
}
@action
@ -219,7 +224,7 @@ export class Cluster implements ClusterModel {
const apiUrl = this.kubeProxyUrl + path;
return request(apiUrl, {
json: true,
timeout: 10000,
timeout: 5000,
headers: {
...(options.headers || {}),
Host: new URL(this.webContentUrl).host,
@ -442,6 +447,9 @@ export class Cluster implements ClusterModel {
{ resource: "storageclasses", group: "storage.k8s.io" },
]
try {
if (!this.allowedNamespaces.length) {
return [];
}
const resourceAccessStatuses = await Promise.all(
apiResources.map(apiResource => this.canI({
resource: apiResource.resource,

View File

@ -5,7 +5,7 @@ import type { Cluster } from "./cluster"
import { bundledKubectl, Kubectl } from "./kubectl"
import logger from "./logger"
export interface KubeAuthProxyResponse {
export interface KubeAuthProxyLog {
data: string;
error?: boolean; // stream=stderr
}
@ -80,7 +80,7 @@ export class KubeAuthProxy {
return errorMsg
}
protected async sendIpcLogMessage(res: KubeAuthProxyResponse) {
protected async sendIpcLogMessage(res: KubeAuthProxyLog) {
const channel = `kube-auth:${this.cluster.id}`
logger.info(`[KUBE-AUTH]: out-channel "${channel}"`, { ...res, meta: this.cluster.getMeta() });
broadcastIpc({

View File

@ -1,5 +1,4 @@
import "./nodes.scss";
import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
@ -15,7 +14,7 @@ import { NodeMenu } from "./node-menu";
import { LineProgress } from "../line-progress";
import { _i18n } from "../../i18n";
import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip } from "../tooltip";
import { Tooltip, TooltipPosition } from "../tooltip";
import kebabCase from "lodash/kebabCase";
import upperFirst from "lodash/upperFirst";
import { apiManager } from "../../api/api-manager";
@ -57,7 +56,10 @@ export class Nodes extends React.Component<Props> {
<LineProgress
max={cores}
value={usage}
tooltip={_i18n._(t`CPU:`) + ` ${Math.ceil(usage * 100) / cores}\%, ` + _i18n._(t`cores:`) + ` ${cores}`}
tooltip={{
position: TooltipPosition.BOTTOM,
children: _i18n._(t`CPU:`) + ` ${Math.ceil(usage * 100) / cores}\%, ` + _i18n._(t`cores:`) + ` ${cores}`
}}
/>
)
}
@ -71,7 +73,10 @@ export class Nodes extends React.Component<Props> {
<LineProgress
max={capacity}
value={usage}
tooltip={_i18n._(t`Memory:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`}
tooltip={{
position: TooltipPosition.BOTTOM,
children: _i18n._(t`Memory:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`
}}
/>
)
}
@ -85,7 +90,10 @@ export class Nodes extends React.Component<Props> {
<LineProgress
max={capacity}
value={usage}
tooltip={_i18n._(t`Disk:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`}
tooltip={{
position: TooltipPosition.BOTTOM,
children: _i18n._(t`Disk:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`
}}
/>
)
}

View File

@ -51,20 +51,19 @@ export class App extends React.Component {
async componentDidMount() {
if (this.cluster) {
await clusterIpc.activate.invokeFromRenderer(); // refresh state, reconnect, etc.
disposeOnUnmount(this, [
reaction(() => this.cluster.accessible, this.onClusterAccessChange, {
fireImmediately: true
})
])
}
disposeOnUnmount(this, [
reaction(() => this.startURL, this.onStartUrlChange, {
fireImmediately: true
})
])
this.isReady = true;
}
protected onStartUrlChange = (startURL: string) => {
protected onClusterAccessChange = (accessible: boolean) => {
const path = navigation.getPath();
const redirectRequired = ["/", clusterStatusURL()].includes(path);
if (redirectRequired || !this.cluster?.accessible) {
navigate(startURL);
if (!accessible || path === "/") {
navigate(this.startURL);
}
}

View File

@ -1,32 +1,40 @@
import type { KubeAuthProxyResponse } from "../../../main/kube-auth-proxy";
import type { KubeAuthProxyLog } from "../../../main/kube-auth-proxy";
import "./cluster-status.scss"
import React from "react";
import { observer } from "mobx-react";
import { disposeOnUnmount, observer } from "mobx-react";
import { ipcRenderer } from "electron";
import { computed, observable } from "mobx";
import { autorun, 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 { navigate } from "../../navigation";
@observer
export class ClusterStatus extends React.Component {
@observable authOutput: KubeAuthProxyResponse[] = [];
@observable authOutput: KubeAuthProxyLog[] = [];
@observable isReconnecting = false;
@computed get hasErrors() {
return this.authOutput.some(({ error }) => error)
}
@computed get cluster() {
return getHostedCluster();
}
@computed get hasErrors(): boolean {
return this.authOutput.some(({ error }) => error) || !!this.cluster.failureReason;
}
@disposeOnUnmount
autoRedirectToMain = autorun(() => {
if (this.cluster.accessible && !this.hasErrors) {
navigate("/");
}
})
async componentDidMount() {
this.authOutput = [{ data: "Connecting..." }];
ipcRenderer.on(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyResponse) => {
ipcRenderer.on(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyLog) => {
this.authOutput.push({
data: res.data.trimRight(),
error: res.error,
@ -48,10 +56,11 @@ export class ClusterStatus extends React.Component {
render() {
const { authOutput, cluster, hasErrors } = this;
const isDisconnected = !!cluster.disconnected;
const isInactive = hasErrors || isDisconnected;
const failureReason = cluster.failureReason;
const isError = hasErrors || isDisconnected;
return (
<div className="ClusterStatus flex column gaps">
{isInactive && (
{isError && (
<Icon
material="cloud_off"
className={cssNames({ error: hasErrors })}
@ -67,7 +76,10 @@ export class ClusterStatus extends React.Component {
})}
</pre>
)}
{isInactive && (
{failureReason && (
<div className="failure-reason error">{failureReason}</div>
)}
{isError && (
<Button
primary
label="Reconnect"

View File

@ -6,13 +6,18 @@ import { observer } from "mobx-react";
import { autobind, cssNames, IClassName } from "../../utils";
import { observable } from "mobx";
export type TooltipPosition = "top" | "left" | "right" | "bottom";
export enum TooltipPosition {
TOP = "top",
BOTTOM = "bottom",
LEFT = "left",
RIGHT = "right",
}
export interface TooltipProps {
targetId: string; // "id" of target html-element to bind
visible?: boolean;
offset?: number; // px
usePortal?: boolean;
targetId: string; // html-id of target element to bind for
visible?: boolean; // initial visibility
offset?: number; // offset from target element in pixels (all sides)
usePortal?: boolean; // renders element outside of parent (in body), disable for "easy-styling", default: true
position?: TooltipPosition;
className?: IClassName;
formatters?: TooltipContentFormatters;
@ -71,7 +76,12 @@ export class Tooltip extends React.Component<TooltipProps> {
const { position } = this.props;
const { elem, targetElem } = this;
let allPositions: TooltipPosition[] = ["right", "bottom", "top", "left"];
let allPositions: TooltipPosition[] = [
TooltipPosition.RIGHT,
TooltipPosition.BOTTOM,
TooltipPosition.LEFT,
TooltipPosition.RIGHT,
];
if (allPositions.includes(position)) {
allPositions = [
position, // put first as priority side for positioning