mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Catch and display load errors for WorkloadsOverview (#4393)
This commit is contained in:
parent
0e5fc65806
commit
78a4e2a126
@ -19,11 +19,12 @@
|
|||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { orderBy } from "lodash";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { BaseRegistry } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface WorkloadsOverviewDetailComponents {
|
export interface WorkloadsOverviewDetailComponents {
|
||||||
Details: React.ComponentType<any>;
|
Details: React.ComponentType<{}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkloadsOverviewDetailRegistration {
|
export interface WorkloadsOverviewDetailRegistration {
|
||||||
@ -31,10 +32,16 @@ export interface WorkloadsOverviewDetailRegistration {
|
|||||||
priority?: number;
|
priority?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WorkloadsOverviewDetailRegistry extends BaseRegistry<WorkloadsOverviewDetailRegistration> {
|
type RegisteredWorkloadsOverviewDetail = Required<WorkloadsOverviewDetailRegistration>;
|
||||||
getItems() {
|
|
||||||
const items = super.getItems();
|
|
||||||
|
|
||||||
return items.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50));
|
export class WorkloadsOverviewDetailRegistry extends BaseRegistry<WorkloadsOverviewDetailRegistration, RegisteredWorkloadsOverviewDetail> {
|
||||||
|
getItems() {
|
||||||
|
return orderBy(super.getItems(), "priority", "desc");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getRegisteredItem(item: WorkloadsOverviewDetailRegistration): RegisteredWorkloadsOverviewDetail {
|
||||||
|
const { priority = 50, ...rest } = item;
|
||||||
|
|
||||||
|
return { priority, ...rest };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,15 +25,6 @@
|
|||||||
min-width: $unit * 75;
|
min-width: $unit * 75;
|
||||||
background: var(--contentColor);
|
background: var(--contentColor);
|
||||||
|
|
||||||
> .header {
|
|
||||||
position: relative;
|
|
||||||
padding: $padding * 2;
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
color: var(--textColorPrimary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.workloads {
|
.workloads {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, 180px);
|
grid-template-columns: repeat(auto-fit, 180px);
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import { OverviewWorkloadStatus } from "./overview-workload-status";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { workloadStores } from "../+workloads";
|
import { workloadStores } from "../+workloads";
|
||||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
|
||||||
import type { KubeResource } from "../../../common/rbac";
|
import type { KubeResource } from "../../../common/rbac";
|
||||||
import { ResourceNames } from "../../utils/rbac";
|
import { ResourceNames } from "../../utils/rbac";
|
||||||
import { boundMethod } from "../../utils";
|
import { boundMethod } from "../../utils";
|
||||||
@ -73,10 +72,6 @@ export class OverviewStatuses extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="OverviewStatuses">
|
<div className="OverviewStatuses">
|
||||||
<div className="header flex gaps align-center">
|
|
||||||
<h5 className="box grow">Overview</h5>
|
|
||||||
<NamespaceSelectFilter />
|
|
||||||
</div>
|
|
||||||
<div className="workloads">
|
<div className="workloads">
|
||||||
{workloads}
|
{workloads}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,4 +22,18 @@
|
|||||||
.WorkloadsOverview {
|
.WorkloadsOverview {
|
||||||
--flex-gap: #{$padding * 2};
|
--flex-gap: #{$padding * 2};
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: var(--contentColor);
|
||||||
|
position: relative;
|
||||||
|
padding: $padding * 2;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
color: var(--textColorPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Icon.load-error {
|
||||||
|
color: var(--colorWarning);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,36 +33,77 @@ import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
|||||||
import { jobStore } from "../+workloads-jobs/job.store";
|
import { jobStore } from "../+workloads-jobs/job.store";
|
||||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||||
import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api";
|
import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api";
|
||||||
import { clusterContext } from "../context";
|
|
||||||
import { WorkloadsOverviewDetailRegistry } from "../../../extensions/registries";
|
import { WorkloadsOverviewDetailRegistry } from "../../../extensions/registries";
|
||||||
import type { WorkloadsOverviewRouteParams } from "../../../common/routes";
|
import type { WorkloadsOverviewRouteParams } from "../../../common/routes";
|
||||||
|
import { makeObservable, observable, reaction } from "mobx";
|
||||||
|
import { clusterContext } from "../context";
|
||||||
|
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
||||||
|
import { Icon } from "../icon";
|
||||||
|
import { TooltipPosition } from "../tooltip";
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<WorkloadsOverviewRouteParams> {
|
interface Props extends RouteComponentProps<WorkloadsOverviewRouteParams> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class WorkloadsOverview extends React.Component<Props> {
|
export class WorkloadsOverview extends React.Component<Props> {
|
||||||
|
@observable loadErrors: string[] = [];
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
makeObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
kubeWatchApi.subscribeStores([
|
kubeWatchApi.subscribeStores([
|
||||||
podsStore, deploymentStore, daemonSetStore, statefulSetStore, replicaSetStore,
|
podsStore, deploymentStore, daemonSetStore, statefulSetStore, replicaSetStore,
|
||||||
jobStore, cronJobStore, eventStore,
|
jobStore, cronJobStore, eventStore,
|
||||||
], {
|
], {
|
||||||
preload: true,
|
onLoadFailure: error => this.loadErrors.push(String(error)),
|
||||||
namespaces: clusterContext.contextNamespaces,
|
}),
|
||||||
|
reaction(() => clusterContext.contextNamespaces.slice(), () => {
|
||||||
|
// clear load errors
|
||||||
|
this.loadErrors.length = 0;
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLoadErrors() {
|
||||||
|
if (this.loadErrors.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
material="warning"
|
||||||
|
className="load-error"
|
||||||
|
tooltip={{
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
{this.loadErrors.map((error, index) => <p key={index}>{error}</p>)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
preferredPositions: TooltipPosition.BOTTOM,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const items = WorkloadsOverviewDetailRegistry.getInstance().getItems().map((item, index) => {
|
const items = WorkloadsOverviewDetailRegistry
|
||||||
return (
|
.getInstance()
|
||||||
<item.components.Details key={`workload-overview-${index}`}/>
|
.getItems()
|
||||||
);
|
.map(({ components: { Details }}, index) => (
|
||||||
});
|
<Details key={`workload-overview-${index}`}/>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="WorkloadsOverview flex column gaps">
|
<div className="WorkloadsOverview flex column gaps">
|
||||||
|
<div className="header flex gaps align-center">
|
||||||
|
<h5 className="box grow">Overview</h5>
|
||||||
|
{this.renderLoadErrors()}
|
||||||
|
<NamespaceSelectFilter />
|
||||||
|
</div>
|
||||||
{items}
|
{items}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -67,7 +67,7 @@ export class KubeObjectListLayout<K extends KubeObject> extends React.Component<
|
|||||||
const { store, dependentStores = [], subscribeStores } = this.props;
|
const { store, dependentStores = [], subscribeStores } = this.props;
|
||||||
const stores = Array.from(new Set([store, ...dependentStores]));
|
const stores = Array.from(new Set([store, ...dependentStores]));
|
||||||
const reactions: Disposer[] = [
|
const reactions: Disposer[] = [
|
||||||
reaction(() => clusterContext.contextNamespaces.length, () => {
|
reaction(() => clusterContext.contextNamespaces.slice(), () => {
|
||||||
// clear load errors
|
// clear load errors
|
||||||
this.loadErrors.length = 0;
|
this.loadErrors.length = 0;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export function initWorkloadsOverviewDetailRegistry() {
|
|||||||
.add([
|
.add([
|
||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
Details: (props: any) => <OverviewStatuses {...props} />,
|
Details: OverviewStatuses,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user