Skip to content

Commit

Permalink
Merge pull request #6875 from jeff-phillips-18/topology-monitoring
Browse files Browse the repository at this point in the history
Move topology monitoring data fetching into Hooks
  • Loading branch information
openshift-merge-robot committed Oct 20, 2020
2 parents cff0c56 + b9a9079 commit e010963
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 102 deletions.
Expand Up @@ -10,19 +10,19 @@ import { Table, TableHeader, TableBody, SortByDirection } from '@patternfly/reac
import { FilterToolbar } from '@console/internal/components/filter-toolbar';
import { getAlertsAndRules } from '@console/internal/components/monitoring/utils';
import { monitoringSetRules, monitoringLoaded, sortList } from '@console/internal/actions/ui';
import { useURLPoll } from '@console/internal/components/utils/url-poll-hook';
import { PrometheusRulesResponse, Rule } from '@console/internal/components/monitoring/types';
import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graphs';
import { Rule } from '@console/internal/components/monitoring/types';
import { RootState } from '@console/internal/redux';
import { getURLSearchParams } from '@console/internal/components/utils';
import { getFilteredRows } from '@console/internal/components/factory';
import { alertingRuleStateOrder } from '@console/internal/reducers/monitoring';
import { usePrometheusRulesPoll } from '@console/internal/components/graphs/prometheus-rules-hook';
import {
monitoringAlertRows,
monitoringAlertColumn,
alertFilters,
applyListSort,
} from './monitoring-alerts-utils';

import './MonitoringAlerts.scss';

type MonitoringAlertsProps = {
Expand All @@ -39,7 +39,6 @@ type StateProps = {

type props = MonitoringAlertsProps & StateProps;

const POLL_DELAY = 15 * 1000;
const reduxID = 'devMonitoringAlerts';
const textFilter = 'resource-list-text';

Expand All @@ -55,11 +54,7 @@ export const MonitoringAlerts: React.FC<props> = ({ match, rules, filters, listS
const { sortBy: listSortBy, orderBy: listOrderBy } = getURLSearchParams();
const columnIndex = _.findIndex(monitoringAlertColumn, { title: listSortBy });
const sortOrder = listOrderBy || SortByDirection.asc;
const [response, loadError, loading] = useURLPoll<PrometheusRulesResponse>(
`${PROMETHEUS_TENANCY_BASE_PATH}/api/v1/rules?namespace=${namespace}`,
POLL_DELAY,
namespace,
);
const [response, loadError, loading] = usePrometheusRulesPoll({ namespace });
const thanosAlertsAndRules = React.useMemo(
() => (!loading && !loadError ? getAlertsAndRules(response?.data) : { rules: [], alerts: [] }),
[response, loadError, loading],
Expand Down
Expand Up @@ -11,12 +11,10 @@ import {
AlertsDetailsPage,
AlertRulesDetailsPage,
} from '@console/internal/components/monitoring/alerting';
import { PrometheusRulesResponse } from '@console/internal/components/monitoring/types';
import { useURLPoll } from '@console/internal/components/utils/url-poll-hook';
import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graphs';
import { getAlertsAndRules } from '@console/internal/components/monitoring/utils';
import { alertingRuleStateOrder } from '@console/internal/reducers/monitoring';
import { monitoringSetRules, monitoringLoaded } from '@console/internal/actions/ui';
import { usePrometheusRulesPoll } from '@console/internal/components/graphs/prometheus-rules-hook';

interface MonitoringAlertsDetailsPageProps {
match: RMatch<{
Expand All @@ -25,7 +23,6 @@ interface MonitoringAlertsDetailsPageProps {
}>;
}

const POLL_DELAY = 15 * 1000;
const ALERT_DETAILS_PATH = '/dev-monitoring/ns/:ns/alerts/:ruleID';
const RULE_DETAILS_PATH = '/dev-monitoring/ns/:ns/rules/:id';

Expand All @@ -41,11 +38,7 @@ const MonitoringAlertsDetailsPage: React.FC<MonitoringAlertsDetailsPageProps> =
const namespace = match.params.ns;
const { path } = match;
const dispatch = useDispatch();
const [response, loadError, loading] = useURLPoll<PrometheusRulesResponse>(
`${PROMETHEUS_TENANCY_BASE_PATH}/api/v1/rules?namespace=${namespace}`,
POLL_DELAY,
namespace,
);
const [response, loadError, loading] = usePrometheusRulesPoll({ namespace });
const thanosAlertsAndRules = React.useMemo(
() => (!loading && !loadError ? getAlertsAndRules(response?.data) : { rules: [], alerts: [] }),
[response, loadError, loading],
Expand Down
Expand Up @@ -4,9 +4,7 @@ import {
useK8sWatchResources,
WatchK8sResources,
} from '@console/internal/components/utils/k8s-watch-hook';
import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graphs';
import { useURLPoll } from '@console/internal/components/utils/url-poll-hook';
import { Alerts, PrometheusRulesResponse } from '@console/internal/components/monitoring/types';
import { usePrometheusRulesPoll } from '@console/internal/components/graphs/prometheus-rules-hook';
import { getAlertsAndRules } from '@console/internal/components/monitoring/utils';
import { RootState } from '@console/internal/redux';
import { TopologyResourcesObject, TrafficData } from './topology-types';
Expand All @@ -22,8 +20,6 @@ export type TopologyDataRetrieverProps = {
trafficData?: TrafficData;
};

const POLL_DELAY = 15 * 1000;

export const ConnectedTopologyDataRetriever: React.FC<TopologyDataRetrieverProps & StateProps> = ({
kindsInFlight,
trafficData,
Expand All @@ -38,20 +34,27 @@ export const ConnectedTopologyDataRetriever: React.FC<TopologyDataRetrieverProps
);

const resources = useK8sWatchResources<TopologyResourcesObject>(resourcesList);
const [alertsResponse, alertsError, alertsLoading] = usePrometheusRulesPoll({ namespace });
const monitoringAlerts = React.useMemo(() => {
let alertData;
if (!alertsLoading && !alertsError) {
alertData = getAlertsAndRules(alertsResponse?.data).alerts;

const url = PROMETHEUS_TENANCY_BASE_PATH
? `${PROMETHEUS_TENANCY_BASE_PATH}/api/v1/rules?namespace=${namespace}`
: null;
const [response, error, loading] = useURLPoll<PrometheusRulesResponse>(
url,
POLL_DELAY,
namespace,
);

const monitoringAlerts: Alerts = React.useMemo(() => {
const { alerts } = getAlertsAndRules(response?.data);
return { data: alerts, loaded: !loading, loadError: error };
}, [response, error, loading]);
// Don't update due to time changes
alertData.forEach((alert) => {
delete alert.activeAt;
if (alert.rule) {
delete alert.rule.evaluationTime;
delete alert.rule.lastEvaluation;
alert.rule.alerts &&
alert.rule.alerts.forEach((ruleAlert) => {
delete ruleAlert.activeAt;
});
}
});
}
return { data: alertData, loaded: !alertsLoading, loadError: alertsError };
}, [alertsError, alertsLoading, alertsResponse]);

// Wipe the current model on a namespace change
React.useEffect(() => {
Expand Down
@@ -0,0 +1,34 @@
import { fetchMonitoringAlerts } from '@console/internal/components/overview/metricUtils';
import { Alert } from '@console/internal/components/monitoring/types';

type StopOverviewAUpdater = () => void;

export const useOverviewAlertsUpdater = (
namespace: string,
updateMonitoringAlerts: (alerts: Alert[]) => void,
interval: number = 15 * 1000,
): StopOverviewAUpdater => {
let alertsInterval: any = null;

const fetchAlerts = (): void => {
fetchMonitoringAlerts(namespace)
.then((alerts) => {
updateMonitoringAlerts(alerts);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
})
.then(() => {
alertsInterval = setTimeout(fetchAlerts, interval);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
});
};

fetchAlerts();

return () => {
clearTimeout(alertsInterval);
};
};
@@ -0,0 +1,50 @@
import * as _ from 'lodash';
import { METRICS_POLL_INTERVAL } from '@console/shared/src';
import {
fetchOverviewMetrics,
METRICS_FAILURE_CODES,
OverviewMetrics,
} from '@console/internal/components/overview/metricUtils';
import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graphs';

type StopOverviewMetricsUpdater = () => void;

export const useOverviewMetricsUpdater = (
namespace: string,
metrics: OverviewMetrics,
updateMetrics: (metrics: OverviewMetrics) => void,
interval: number = METRICS_POLL_INTERVAL,
): StopOverviewMetricsUpdater => {
let metricsInterval = null;

const fetchMetrics = () => {
if (!PROMETHEUS_TENANCY_BASE_PATH) {
return;
}
fetchOverviewMetrics(namespace)
.then((updatedMetrics) => {
updateMetrics(updatedMetrics);
})
.catch((res) => {
const status = res?.response?.status;
// eslint-disable-next-line no-console
console.error('Could not fetch metrics, status:', status);
// Don't retry on some status codes unless a previous request succeeded.
if (_.includes(METRICS_FAILURE_CODES, status) && _.isEmpty(metrics)) {
throw new Error(`Could not fetch metrics, status: ${status}`);
}
})
.then(() => {
metricsInterval = setTimeout(fetchMetrics, interval);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
});
};

fetchMetrics();

return () => {
clearTimeout(metricsInterval);
};
};
Expand Up @@ -11,18 +11,14 @@ import {
Model,
} from '@patternfly/react-topology';
import { DataList } from '@patternfly/react-core';
import { METRICS_POLL_INTERVAL, useQueryParams } from '@console/shared';
import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graphs';
import {
fetchOverviewMetrics,
fetchMonitoringAlerts,
OverviewMetrics,
METRICS_FAILURE_CODES,
} from '@console/internal/components/overview/metricUtils';
import { useQueryParams } from '@console/shared';
import { OverviewMetrics } from '@console/internal/components/overview/metricUtils';
import { Alert } from '@console/internal/components/monitoring/types';
import * as UIActions from '@console/internal/actions/ui';
import { TYPE_APPLICATION_GROUP } from '../components';
import { odcElementFactory } from '../elements';
import { useOverviewMetricsUpdater } from '../hooks/useOverviewMetricsUpdater';
import { useOverviewAlertsUpdater } from '../hooks/useOverviewAlertsUpdater';
import { TopologyListViewAppGroup } from './TopologyListViewAppGroup';
import { getChildKinds, sortGroupChildren } from './list-view-utils';
import { TopologyListViewUnassignedGroup } from './TopologyListViewUnassignedGroup';
Expand Down Expand Up @@ -287,56 +283,12 @@ const ConnectedTopologyListView: React.FC<TopologyListViewProps &
}, [visualization, selectedId, applicationGroups, unassignedItems, onSelect]);

React.useEffect(() => {
let metricsInterval: any = null;
let alertsInterval: any = null;

const fetchMetrics = () => {
if (!PROMETHEUS_TENANCY_BASE_PATH) {
return;
}
fetchOverviewMetrics(namespace)
.then((updatedMetrics) => {
updateMetrics(updatedMetrics);
})
.catch((res) => {
const status = res?.response?.status;
// eslint-disable-next-line no-console
console.error('Could not fetch metrics, status:', status);
// Don't retry on some status codes unless a previous request succeeded.
if (_.includes(METRICS_FAILURE_CODES, status) && _.isEmpty(metrics)) {
throw new Error(`Could not fetch metrics, status: ${status}`);
}
})
.then(() => {
metricsInterval = setTimeout(fetchMetrics, METRICS_POLL_INTERVAL);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
});
};

const fetchAlerts = (): void => {
fetchMonitoringAlerts(namespace)
.then((alerts) => {
updateMonitoringAlerts(alerts);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
})
.then(() => {
alertsInterval = setTimeout(fetchAlerts, 15 * 1000);
})
.catch((e) => {
console.error(e); // eslint-disable-line no-console
});
};

fetchMetrics();
fetchAlerts();
const clearMetricsInterval = useOverviewMetricsUpdater(namespace, metrics, updateMetrics);
const clearAlertsInterval = useOverviewAlertsUpdater(namespace, updateMonitoringAlerts);

return () => {
clearInterval(metricsInterval);
clearInterval(alertsInterval);
clearMetricsInterval();
clearAlertsInterval();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [namespace, updateMetrics, updateMonitoringAlerts]);
Expand Down
5 changes: 3 additions & 2 deletions frontend/public/components/graphs/helpers.ts
Expand Up @@ -4,6 +4,7 @@ import { PROMETHEUS_BASE_PATH, PROMETHEUS_TENANCY_BASE_PATH } from './index';

export enum PrometheusEndpoint {
LABEL = 'api/v1/label',
RULES = 'api/v1/rules',
QUERY = 'api/v1/query',
QUERY_RANGE = 'api/v1/query_range',
}
Expand Down Expand Up @@ -44,7 +45,7 @@ export const getPrometheusURL = (
props: PrometheusURLProps,
basePath: string = props.namespace ? PROMETHEUS_TENANCY_BASE_PATH : PROMETHEUS_BASE_PATH,
): string => {
if (!props.query) {
if (props.endpoint !== PrometheusEndpoint.RULES && !props.query) {
return '';
}
const params = getSearchParams(props);
Expand All @@ -55,7 +56,7 @@ type PrometheusURLProps = {
endpoint: PrometheusEndpoint;
endTime?: number;
namespace?: string;
query: string;
query?: string;
samples?: number;
timeout?: string;
timespan?: number;
Expand Down
17 changes: 17 additions & 0 deletions frontend/public/components/graphs/prometheus-rules-hook.ts
@@ -0,0 +1,17 @@
import { useURLPoll } from '../utils/url-poll-hook';
import { getPrometheusURL, PrometheusEndpoint } from './helpers';
import { PrometheusRulesResponse } from '../monitoring/types';

export const usePrometheusRulesPoll = ({ delay, namespace }: PrometheusPollProps) => {
const url = getPrometheusURL({
endpoint: PrometheusEndpoint.RULES,
namespace,
});

return useURLPoll<PrometheusRulesResponse>(url, delay, namespace);
};

type PrometheusPollProps = {
delay?: number;
namespace?: string;
};
4 changes: 3 additions & 1 deletion frontend/public/components/monitoring/alerting.tsx
Expand Up @@ -91,6 +91,7 @@ import { ResourceStatus } from '../utils/resource-status';
import { history } from '../utils/router';
import { LoadingInline, StatusBox } from '../utils/status-box';
import { Timestamp } from '../utils/timestamp';
import { getPrometheusURL, PrometheusEndpoint } from '../graphs/helpers';

const ruleURL = (rule: Rule) => `${RuleResource.plural}/${_.get(rule, 'id')}`;

Expand Down Expand Up @@ -1559,8 +1560,9 @@ const PollerPages = () => {
const alertsKey = 'alerts';
const rulesKey = 'rules';
store.dispatch(UIActions.monitoringLoading(alertsKey));
const url = getPrometheusURL({ endpoint: PrometheusEndpoint.RULES });
const poller = (): void => {
coFetchJSON(`${prometheusBaseURL}/api/v1/rules`)
coFetchJSON(url)
.then(({ data }) => {
const { alerts, rules } = getAlertsAndRules(data);
store.dispatch(UIActions.monitoringLoaded(alertsKey, alerts));
Expand Down
3 changes: 2 additions & 1 deletion frontend/public/components/notification-drawer.tsx
Expand Up @@ -46,6 +46,7 @@ import { ClusterVersionModel } from '../models';
import { useK8sWatchResource, WatchK8sResource } from './utils/k8s-watch-hook';
import { useAccessReview } from './utils/rbac';
import { LinkifyExternal } from './utils';
import { PrometheusEndpoint } from './graphs/helpers';

const criticalCompare = (a: Alert): boolean => getAlertSeverity(a) === 'critical';
const otherAlertCompare = (a: Alert): boolean => getAlertSeverity(a) !== 'critical';
Expand Down Expand Up @@ -225,7 +226,7 @@ export const ConnectedNotificationDrawer_: React.FC<ConnectedNotificationDrawerP
const { alertManagerBaseURL, prometheusBaseURL } = window.SERVER_FLAGS;

if (prometheusBaseURL) {
poll(`${prometheusBaseURL}/api/v1/rules`, 'notificationAlerts', getAlerts);
poll(`${prometheusBaseURL}/${PrometheusEndpoint.RULES}`, 'notificationAlerts', getAlerts);
} else {
store.dispatch(
UIActions.monitoringErrored('notificationAlerts', new Error('prometheusBaseURL not set')),
Expand Down

0 comments on commit e010963

Please sign in to comment.