Skip to content

Commit

Permalink
Add operators status to Dashboards
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner committed Jan 13, 2020
1 parent 0f1578c commit 910a506
Show file tree
Hide file tree
Showing 29 changed files with 1,015 additions and 255 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import DashboardCardHeader from '@console/shared/src/components/dashboard/dashbo
import DashboardCardTitle from '@console/shared/src/components/dashboard/dashboard-card/DashboardCardTitle';
import HealthBody from '@console/shared/src/components/dashboard/status-card/HealthBody';
import HealthItem from '@console/shared/src/components/dashboard/status-card/HealthItem';
import { getAlerts } from '@console/shared/src/components/dashboard/status-card/utils';
import { getAlerts } from '@console/shared/src/components/dashboard/status-card/alert-utils';
import { PrometheusResponse } from '@console/internal/components/graphs';
import {
withDashboardResources,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { referenceForModel, ClusterOperator } from '@console/internal/module/k8s';
import { ClusterOperatorModel } from '@console/internal/models';
import { ResourceLink } from '@console/internal/components/utils/resource-link';
import { OperatorRowProps } from '@console/plugin-sdk';
import { OperatorStatusRow } from '@console/shared/src/components/dashboard/status-card/OperatorStatusBody';

import './operator-status.scss';

const ClusterOperatorStatusRow: React.FC<OperatorRowProps<ClusterOperator>> = ({
operatorStatus,
}) => (
<OperatorStatusRow title={operatorStatus.status.title} icon={operatorStatus.status.icon}>
<ResourceLink
kind={referenceForModel(ClusterOperatorModel)}
name={operatorStatus.operators[0].metadata.name}
hideIcon
className="co-operator-status__title"
/>
</OperatorStatusRow>
);

export default ClusterOperatorStatusRow;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.co-operator-status__title .co-resource-item__resource-name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import * as _ from 'lodash';
import { PrometheusHealthHandler, URLHealthHandler, SubsystemHealth } from '@console/plugin-sdk';
import { HealthState } from '@console/shared/src/components/dashboard/status-card/states';
import {
PrometheusHealthHandler,
URLHealthHandler,
SubsystemHealth,
GetOperatorsWithStatuses,
GetOperatorStatusPriority,
} from '@console/plugin-sdk';
import {
HealthState,
operatorHealthPriority,
} from '@console/shared/src/components/dashboard/status-card/states';
import { coFetch } from '@console/internal/co-fetch';
import {
ClusterVersionKind,
ClusterUpdateStatus,
getClusterUpdateStatus,
getClusterOperatorStatus,
OperatorStatus,
ClusterOperator,
} from '@console/internal/module/k8s';
import { PrometheusResponse } from '@console/internal/components/graphs';
import { humanizePercentage } from '@console/internal/components/utils/units';
import { getOperatorsStatus } from '@console/shared/src/components/dashboard/status-card/state-utils';

export const fetchK8sHealth = async (url: string) => {
const response = await coFetch(url);
Expand Down Expand Up @@ -38,7 +51,8 @@ export const getControlPlaneComponentHealth = (
if (
error ||
(response &&
(response.status === 'success' && _.isNil(_.get(response, 'data.result[0].value[1]'))))
response.status === 'success' &&
_.isNil(_.get(response, 'data.result[0].value[1]')))
) {
return { state: HealthState.UNKNOWN, message: 'Not available' };
}
Expand All @@ -55,19 +69,16 @@ export const getControlPlaneComponentHealth = (
return { state: HealthState.ERROR, message: perc.string };
};

const errorStates = [HealthState.WARNING, HealthState.ERROR, HealthState.UNKNOWN];

export const getControlPlaneHealth: PrometheusHealthHandler = (responses = [], errors = []) => {
const componentsHealth = responses.map((r, index) =>
getControlPlaneComponentHealth(r, errors[index]),
);
if (componentsHealth.some((c) => c.state === HealthState.LOADING)) {
return { state: HealthState.LOADING };
}
const errComponents = componentsHealth.filter(
(c) =>
c.state === HealthState.WARNING ||
c.state === HealthState.ERROR ||
c.state === HealthState.UNKNOWN,
);
const errComponents = componentsHealth.filter(({ state }) => errorStates.includes(state));
if (errComponents.length) {
return {
state: errComponents.length === 4 ? HealthState.UNKNOWN : HealthState.WARNING,
Expand All @@ -76,3 +87,27 @@ export const getControlPlaneHealth: PrometheusHealthHandler = (responses = [], e
}
return { state: HealthState.OK };
};

export const getClusterOperatorStatusPriority: GetOperatorStatusPriority<ClusterOperator> = (
co,
) => {
const status = getClusterOperatorStatus(co);
if (status === OperatorStatus.Degraded) {
return { ...operatorHealthPriority[HealthState.WARNING], title: status };
}
if (status === OperatorStatus.Unknown) {
return { ...operatorHealthPriority[HealthState.UNKNOWN], title: status };
}
if (status === OperatorStatus.Updating) {
return { ...operatorHealthPriority[HealthState.UPDATING], title: status };
}
return { ...operatorHealthPriority[HealthState.OK], title: status };
};

export const getClusterOperatorHealthStatus: GetOperatorsWithStatuses<ClusterOperator> = (
resources,
) => {
return (resources.clusterOperators.data as ClusterOperator[]).map((co) =>
getOperatorsStatus<ClusterOperator>([co], getClusterOperatorStatusPriority),
);
};
28 changes: 26 additions & 2 deletions frontend/packages/console-app/src/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ import {
DashboardsOverviewHealthURLSubsystem,
DashboardsOverviewHealthPrometheusSubsystem,
DashboardsOverviewInventoryItem,
DashboardsOverviewHealthOperator,
} from '@console/plugin-sdk';
import {
ClusterVersionModel,
NodeModel,
PodModel,
StorageClassModel,
PersistentVolumeClaimModel,
ClusterOperatorModel,
} from '@console/internal/models';
import { referenceForModel } from '@console/internal/module/k8s';
import { referenceForModel, ClusterOperator } from '@console/internal/module/k8s';
import {
getNodeStatusGroups,
getPodStatusGroups,
Expand All @@ -30,6 +32,7 @@ import {
fetchK8sHealth,
getK8sHealthState,
getControlPlaneHealth,
getClusterOperatorHealthStatus,
} from './components/dashboards-page/status';
import {
API_SERVERS_UP,
Expand All @@ -43,7 +46,8 @@ type ConsumedExtensions =
| DashboardsOverviewResourceActivity
| DashboardsOverviewHealthURLSubsystem<any>
| DashboardsOverviewHealthPrometheusSubsystem
| DashboardsOverviewInventoryItem;
| DashboardsOverviewInventoryItem
| DashboardsOverviewHealthOperator<ClusterOperator>;

const plugin: Plugin<ConsumedExtensions> = [
{
Expand Down Expand Up @@ -135,6 +139,26 @@ const plugin: Plugin<ConsumedExtensions> = [
useAbbr: true,
},
},
{
type: 'Dashboards/Overview/Health/Operator',
properties: {
title: 'Cluster operators',
resources: [
{
kind: referenceForModel(ClusterOperatorModel),
isList: true,
namespaced: false,
prop: 'clusterOperators',
},
],
getOperatorsWithStatuses: getClusterOperatorHealthStatus,
operatorRowLoader: () =>
import(
'./components/dashboards-page/OperatorStatus' /* webpackChunkName: "console-app" */
).then((c) => c.default),
viewAllLink: '/settings/cluster/clusteroperators',
},
},
];

export default plugin;
76 changes: 72 additions & 4 deletions frontend/packages/console-plugin-sdk/src/typings/dashboards.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { HealthState } from '@console/shared/src/components/dashboard/status-card/states';
import { GridPosition } from '@console/shared/src/components/dashboard/DashboardGrid';
import { FirehoseResource, FirehoseResult } from '@console/internal/components/utils';
import { K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import {
FirehoseResource,
FirehoseResult,
FirehoseResourcesResult,
} from '@console/internal/components/utils';
import { K8sKind, K8sResourceKind, K8sResourceCommon } from '@console/internal/module/k8s';
import {
StatusGroupMapper,
ExpandedComponentProps,
Expand Down Expand Up @@ -77,6 +81,27 @@ namespace ExtensionProperties {
popupTitle?: string;
}

export interface DashboardsOverviewHealthOperator<R extends K8sResourceCommon>
extends DashboardsOverviewHealthSubsystem {
/** Title of operators section in popup */
title: string;

/** Resources which will be fetched and passed to healthHandler */
resources: FirehoseResource[];

/** Resolve status for operators */
getOperatorsWithStatuses: GetOperatorsWithStatuses<R>;

/** Loader for popup row component */
operatorRowLoader: LazyLoader<OperatorRowProps<R>>;

/**
* Link to all resources page.
* If not provided than list page of first resource from resources prop is used.
* */
viewAllLink?: string;
}

export interface DashboardsTab extends DashboardsExtensionProperties {
/** The tab's ID which will be used as part of href within dashboards page */
id: string;
Expand Down Expand Up @@ -197,14 +222,26 @@ export const isDashboardsOverviewHealthPrometheusSubsystem = (
): e is DashboardsOverviewHealthPrometheusSubsystem =>
e.type === 'Dashboards/Overview/Health/Prometheus';

export interface DashboardsOverviewHealthOperator<R extends K8sResourceCommon = K8sResourceCommon>
extends Extension<ExtensionProperties.DashboardsOverviewHealthOperator<R>> {
type: 'Dashboards/Overview/Health/Operator';
}

export const isDashboardsOverviewHealthOperator = (
e: Extension,
): e is DashboardsOverviewHealthOperator => e.type === 'Dashboards/Overview/Health/Operator';

export type DashboardsOverviewHealthSubsystem =
| DashboardsOverviewHealthURLSubsystem
| DashboardsOverviewHealthPrometheusSubsystem;
| DashboardsOverviewHealthPrometheusSubsystem
| DashboardsOverviewHealthOperator;

export const isDashboardsOverviewHealthSubsystem = (
e: Extension,
): e is DashboardsOverviewHealthSubsystem =>
isDashboardsOverviewHealthURLSubsystem(e) || isDashboardsOverviewHealthPrometheusSubsystem(e);
isDashboardsOverviewHealthURLSubsystem(e) ||
isDashboardsOverviewHealthPrometheusSubsystem(e) ||
isDashboardsOverviewHealthOperator(e);

export interface DashboardsTab extends Extension<ExtensionProperties.DashboardsTab> {
type: 'Dashboards/Tab';
Expand Down Expand Up @@ -307,3 +344,34 @@ export type PrometheusHealthHandler = (
errors: any[],
additionalResource?: FirehoseResult<K8sResourceKind | K8sResourceKind[]>,
) => SubsystemHealth;

export type OperatorHealthHandler = (resources: FirehoseResourcesResult) => OperatorHealth;

export type OperatorHealth = {
health: HealthState;
count?: number;
};

export type GetOperatorsWithStatuses<R extends K8sResourceCommon = K8sResourceCommon> = (
resources: FirehoseResourcesResult,
) => OperatorStatusWithResources<R>[];

export type OperatorStatusWithResources<R extends K8sResourceCommon = K8sResourceCommon> = {
operators: R[];
status: OperatorStatusPriority;
};

export type GetOperatorStatusPriority<R extends K8sResourceCommon = K8sResourceCommon> = (
operator: R,
) => OperatorStatusPriority;

export type OperatorStatusPriority = {
title: string;
priority: number;
icon: React.ReactNode;
health: HealthState;
};

export type OperatorRowProps<R extends K8sResourceCommon = K8sResourceCommon> = {
operatorStatus: OperatorStatusWithResources<R>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { Link } from 'react-router-dom';
import { RedExclamationCircleIcon, YellowExclamationTriangleIcon } from '@console/shared';
import { Timestamp } from '@console/internal/components/utils/timestamp';
import { alertURL, Alert } from '@console/internal/components/monitoring';
import { getAlertSeverity, getAlertMessage, getAlertDescription, getAlertTime } from './utils';
import {
getAlertSeverity,
getAlertMessage,
getAlertDescription,
getAlertTime,
} from './alert-utils';

const getSeverityIcon = (severity: string) => {
switch (severity) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,7 @@
import * as React from 'react';
import classNames from 'classnames';
import {
GreenCheckCircleIcon,
RedExclamationCircleIcon,
YellowExclamationTriangleIcon,
} from '@console/shared';
import { InProgressIcon, SyncAltIcon, UnknownIcon } from '@patternfly/react-icons';
import { DashboardCardPopupLink } from '../dashboard-card/DashboardCardLink';
import { HealthState } from './states';

const healthStateMapping = {
[HealthState.OK]: {
icon: <GreenCheckCircleIcon />,
},
[HealthState.ERROR]: {
icon: <RedExclamationCircleIcon />,
},
[HealthState.WARNING]: {
icon: <YellowExclamationTriangleIcon />,
message: 'Degraded',
},
[HealthState.UPDATING]: {
icon: <SyncAltIcon className="update-pending" />,
message: 'Updating',
},
[HealthState.PROGRESS]: {
icon: <InProgressIcon />,
},
[HealthState.UNKNOWN]: {
icon: <UnknownIcon className="text-secondary" />,
message: 'Not available',
},
};
import { HealthState, healthStateMapping } from './states';

const HealthItemIcon: React.FC<HealthItemIconProps> = ({ state }) => (
<div className="co-dashboard-icon">
Expand Down

0 comments on commit 910a506

Please sign in to comment.