mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Replace cluster warning event polling with watches (#1521)
* replace cluster warning event polling with watches Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * cleanup Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix loadAll calls Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * tweak Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
1547142125
commit
8739baab5b
@ -42,7 +42,6 @@ export interface ClusterState {
|
|||||||
accessible: boolean;
|
accessible: boolean;
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
failureReason: string;
|
failureReason: string;
|
||||||
eventCount: number;
|
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
allowedNamespaces: string[]
|
allowedNamespaces: string[]
|
||||||
allowedResources: string[]
|
allowedResources: string[]
|
||||||
@ -74,7 +73,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
@observable disconnected = true; // false if user has selected to connect
|
@observable disconnected = true; // false if user has selected to connect
|
||||||
@observable failureReason: string;
|
@observable failureReason: string;
|
||||||
@observable isAdmin = false;
|
@observable isAdmin = false;
|
||||||
@observable eventCount = 0;
|
|
||||||
@observable preferences: ClusterPreferences = {};
|
@observable preferences: ClusterPreferences = {};
|
||||||
@observable metadata: ClusterMetadata = {};
|
@observable metadata: ClusterMetadata = {};
|
||||||
@observable allowedNamespaces: string[] = [];
|
@observable allowedNamespaces: string[] = [];
|
||||||
@ -209,10 +207,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
await this.refreshConnectionStatus();
|
await this.refreshConnectionStatus();
|
||||||
if (this.accessible) {
|
if (this.accessible) {
|
||||||
this.isAdmin = await this.isClusterAdmin();
|
this.isAdmin = await this.isClusterAdmin();
|
||||||
await Promise.all([
|
await this.refreshAllowedResources();
|
||||||
this.refreshEvents(),
|
|
||||||
this.refreshAllowedResources(),
|
|
||||||
]);
|
|
||||||
if (opts.refreshMetadata) {
|
if (opts.refreshMetadata) {
|
||||||
this.refreshMetadata();
|
this.refreshMetadata();
|
||||||
}
|
}
|
||||||
@ -242,11 +237,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.allowedResources = await this.getAllowedResources();
|
this.allowedResources = await this.getAllowedResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
async refreshEvents() {
|
|
||||||
this.eventCount = await this.getEventCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getKubeconfig(): KubeConfig {
|
protected getKubeconfig(): KubeConfig {
|
||||||
return loadConfig(this.kubeConfigPath);
|
return loadConfig(this.kubeConfigPath);
|
||||||
}
|
}
|
||||||
@ -332,40 +322,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getEventCount(): Promise<number> {
|
|
||||||
if (!this.isAdmin) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const client = this.getProxyKubeconfig().makeApiClient(CoreV1Api);
|
|
||||||
try {
|
|
||||||
const response = await client.listEventForAllNamespaces(false, null, null, null, 1000);
|
|
||||||
const uniqEventSources = new Set();
|
|
||||||
const warnings = response.body.items.filter(e => e.type !== 'Normal');
|
|
||||||
for (const w of warnings) {
|
|
||||||
if (w.involvedObject.kind === 'Pod') {
|
|
||||||
try {
|
|
||||||
const { body: pod } = await client.readNamespacedPod(w.involvedObject.name, w.involvedObject.namespace);
|
|
||||||
logger.debug(`checking pod ${w.involvedObject.namespace}/${w.involvedObject.name}`);
|
|
||||||
if (podHasIssues(pod)) {
|
|
||||||
uniqEventSources.add(w.involvedObject.uid);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uniqEventSources.add(w.involvedObject.uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const nodes = (await client.listNode()).body.items;
|
|
||||||
const nodeNotificationCount = nodes
|
|
||||||
.map(getNodeWarningConditions)
|
|
||||||
.reduce((sum, conditions) => sum + conditions.length, 0);
|
|
||||||
return uniqEventSources.size + nodeNotificationCount;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("Failed to fetch event count: " + JSON.stringify(error));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): ClusterModel {
|
toJSON(): ClusterModel {
|
||||||
const model: ClusterModel = {
|
const model: ClusterModel = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
@ -393,7 +349,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
accessible: this.accessible,
|
accessible: this.accessible,
|
||||||
failureReason: this.failureReason,
|
failureReason: this.failureReason,
|
||||||
isAdmin: this.isAdmin,
|
isAdmin: this.isAdmin,
|
||||||
eventCount: this.eventCount,
|
|
||||||
allowedNamespaces: this.allowedNamespaces,
|
allowedNamespaces: this.allowedNamespaces,
|
||||||
allowedResources: this.allowedResources,
|
allowedResources: this.allowedResources,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,13 +37,17 @@ import { webFrame } from "electron";
|
|||||||
import { clusterPageRegistry, getExtensionPageUrl, PageRegistration, RegisteredPage } from "../../extensions/registries/page-registry";
|
import { clusterPageRegistry, getExtensionPageUrl, PageRegistration, RegisteredPage } from "../../extensions/registries/page-registry";
|
||||||
import { extensionLoader } from "../../extensions/extension-loader";
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
import { appEventBus } from "../../common/event-bus";
|
import { appEventBus } from "../../common/event-bus";
|
||||||
import { requestMain } from "../../common/ipc";
|
import { broadcastMessage, requestMain } from "../../common/ipc";
|
||||||
import whatInput from 'what-input';
|
import whatInput from 'what-input';
|
||||||
import { clusterSetFrameIdHandler } from "../../common/cluster-ipc";
|
import { clusterSetFrameIdHandler } from "../../common/cluster-ipc";
|
||||||
import { ClusterPageMenuRegistration, clusterPageMenuRegistry } from "../../extensions/registries";
|
import { ClusterPageMenuRegistration, clusterPageMenuRegistry } from "../../extensions/registries";
|
||||||
import { TabLayoutRoute, TabLayout } from "./layout/tab-layout";
|
import { TabLayoutRoute, TabLayout } from "./layout/tab-layout";
|
||||||
import { Trans } from "@lingui/macro";
|
import { StatefulSetScaleDialog } from "./+workloads-statefulsets/statefulset-scale-dialog";
|
||||||
import {StatefulSetScaleDialog} from "./+workloads-statefulsets/statefulset-scale-dialog";
|
import { eventStore } from "./+events/event.store";
|
||||||
|
import { reaction, computed } from "mobx";
|
||||||
|
import { nodesStore } from "./+nodes/nodes.store";
|
||||||
|
import { podsStore } from "./+workloads-pods/pods.store";
|
||||||
|
import { sum } from "lodash";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -69,6 +73,39 @@ export class App extends React.Component {
|
|||||||
whatInput.ask(); // Start to monitor user input device
|
whatInput.ask(); // Start to monitor user input device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
const cluster = getHostedCluster();
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
if (isAllowedResource("events") && isAllowedResource("pods")) {
|
||||||
|
promises.push(eventStore.loadAll());
|
||||||
|
promises.push(podsStore.loadAll());
|
||||||
|
}
|
||||||
|
if (isAllowedResource("nodes")) {
|
||||||
|
promises.push(nodesStore.loadAll());
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
if (eventStore.isLoaded && podsStore.isLoaded) {
|
||||||
|
eventStore.subscribe();
|
||||||
|
podsStore.subscribe();
|
||||||
|
}
|
||||||
|
if (nodesStore.isLoaded) {
|
||||||
|
nodesStore.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
reaction(() => this.warningsCount, (count) => {
|
||||||
|
broadcastMessage(`cluster-warning-event-count:${cluster.id}`, count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed
|
||||||
|
get warningsCount() {
|
||||||
|
let warnings = sum(nodesStore.items
|
||||||
|
.map(node => node.getWarningConditions().length));
|
||||||
|
warnings = warnings + eventStore.getWarnings().length;
|
||||||
|
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
get startURL() {
|
get startURL() {
|
||||||
if (isAllowedResource(["events", "nodes", "pods"])) {
|
if (isAllowedResource(["events", "nodes", "pods"])) {
|
||||||
return clusterURL();
|
return clusterURL();
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import "./cluster-icon.scss";
|
import "./cluster-icon.scss";
|
||||||
|
|
||||||
import React, { DOMAttributes } from "react";
|
import React, { DOMAttributes } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { Params as HashiconParams } from "@emeraldpay/hashicon";
|
import { Params as HashiconParams } from "@emeraldpay/hashicon";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Cluster } from "../../../main/cluster";
|
import { Cluster } from "../../../main/cluster";
|
||||||
import { cssNames, IClassName } from "../../utils";
|
import { cssNames, IClassName } from "../../utils";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
|
import { eventStore } from "../+events/event.store";
|
||||||
|
import { forCluster } from "../../api/kube-api";
|
||||||
|
import { subscribeToBroadcast, unsubscribeAllFromBroadcast } from "../../../common/ipc";
|
||||||
|
import { observable, when } from "mobx";
|
||||||
|
|
||||||
interface Props extends DOMAttributes<HTMLElement> {
|
interface Props extends DOMAttributes<HTMLElement> {
|
||||||
cluster: Cluster;
|
cluster: Cluster;
|
||||||
@ -29,12 +33,29 @@ const defaultProps: Partial<Props> = {
|
|||||||
export class ClusterIcon extends React.Component<Props> {
|
export class ClusterIcon extends React.Component<Props> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
|
@observable eventCount = 0;
|
||||||
|
|
||||||
|
get eventCountBroadcast() {
|
||||||
|
return `cluster-warning-event-count:${this.props.cluster.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const subscriber = subscribeToBroadcast(this.eventCountBroadcast, (ev, eventCount) => {
|
||||||
|
this.eventCount = eventCount;
|
||||||
|
});
|
||||||
|
|
||||||
|
disposeOnUnmount(this, [
|
||||||
|
subscriber
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
cluster, showErrors, showTooltip, errorClass, options, interactive, isActive,
|
cluster, showErrors, showTooltip, errorClass, options, interactive, isActive,
|
||||||
children, ...elemProps
|
children, ...elemProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { isAdmin, name, eventCount, preferences, id: clusterId } = cluster;
|
const { name, preferences, id: clusterId } = cluster;
|
||||||
|
const eventCount = this.eventCount;
|
||||||
const { icon } = preferences;
|
const { icon } = preferences;
|
||||||
const clusterIconId = `cluster-icon-${clusterId}`;
|
const clusterIconId = `cluster-icon-${clusterId}`;
|
||||||
const className = cssNames("ClusterIcon flex inline", this.props.className, {
|
const className = cssNames("ClusterIcon flex inline", this.props.className, {
|
||||||
@ -48,7 +69,7 @@ export class ClusterIcon extends React.Component<Props> {
|
|||||||
)}
|
)}
|
||||||
{icon && <img src={icon} alt={name}/>}
|
{icon && <img src={icon} alt={name}/>}
|
||||||
{!icon && <Hashicon value={clusterId} options={options}/>}
|
{!icon && <Hashicon value={clusterId} options={options}/>}
|
||||||
{showErrors && isAdmin && eventCount > 0 && (
|
{showErrors && eventCount > 0 && !isActive && (
|
||||||
<Badge
|
<Badge
|
||||||
className={cssNames("events-count", errorClass)}
|
className={cssNames("events-count", errorClass)}
|
||||||
label={eventCount >= 1000 ? Math.ceil(eventCount / 1000) + "k+" : eventCount}
|
label={eventCount >= 1000 ? Math.ceil(eventCount / 1000) + "k+" : eventCount}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user