mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 00:25:46 -06:00
Co-authored-by: Juan Cabanas <juan.cabanas@grafana.com> Co-authored-by: Ezequiel Victorero <ezequiel.victorero@grafana.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
117 lines
4.3 KiB
TypeScript
117 lines
4.3 KiB
TypeScript
import { from, Observable } from 'rxjs';
|
|
import { catchError, map } from 'rxjs/operators';
|
|
|
|
import { AlertState, AlertStateInfo } from '@grafana/data';
|
|
import { config, getBackendSrv } from '@grafana/runtime';
|
|
import { contextSrv } from 'app/core/services/context_srv';
|
|
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
|
import { isAlertingRule } from 'app/features/alerting/unified/utils/rules';
|
|
import { AccessControlAction } from 'app/types';
|
|
import { PromAlertingRuleState, PromRulesResponse } from 'app/types/unified-alerting-dto';
|
|
|
|
import { DashboardQueryRunnerOptions, DashboardQueryRunnerWorker, DashboardQueryRunnerWorkerResult } from './types';
|
|
import { emptyResult, handleDashboardQueryRunnerWorkerError } from './utils';
|
|
|
|
export class UnifiedAlertStatesWorker implements DashboardQueryRunnerWorker {
|
|
// maps dashboard uid to wether it has alert rules.
|
|
// if it is determined that a dashboard does not have alert rules,
|
|
// further attempts to get alert states for it will not be made
|
|
private hasAlertRules: Record<string, boolean> = {};
|
|
|
|
canWork({ dashboard, range }: DashboardQueryRunnerOptions): boolean {
|
|
if (!dashboard.uid) {
|
|
return false;
|
|
}
|
|
|
|
// Cannot fetch rules while on a public dashboard since it's unauthenticated
|
|
if (config.publicDashboardAccessToken) {
|
|
return false;
|
|
}
|
|
|
|
if (range.raw.to !== 'now') {
|
|
return false;
|
|
}
|
|
|
|
if (this.hasAlertRules[dashboard.uid] === false) {
|
|
return false;
|
|
}
|
|
|
|
const hasRuleReadPermission =
|
|
contextSrv.hasPermission(AccessControlAction.AlertingRuleRead) &&
|
|
contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalRead);
|
|
|
|
if (!hasRuleReadPermission) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
work(options: DashboardQueryRunnerOptions): Observable<DashboardQueryRunnerWorkerResult> {
|
|
if (!this.canWork(options)) {
|
|
return emptyResult();
|
|
}
|
|
|
|
const { dashboard } = options;
|
|
return from(
|
|
getBackendSrv().get(
|
|
'/api/prometheus/grafana/api/v1/rules',
|
|
{
|
|
dashboard_uid: dashboard.uid,
|
|
},
|
|
`dashboard-query-runner-unified-alert-states-${dashboard.id}`
|
|
)
|
|
).pipe(
|
|
map((result: PromRulesResponse) => {
|
|
if (result.status === 'success') {
|
|
this.hasAlertRules[dashboard.uid] = false;
|
|
const panelIdToAlertState: Record<number, AlertStateInfo> = {};
|
|
result.data.groups.forEach((group) =>
|
|
group.rules.forEach((rule) => {
|
|
if (isAlertingRule(rule) && rule.annotations && rule.annotations[Annotation.panelID]) {
|
|
this.hasAlertRules[dashboard.uid] = true;
|
|
const panelId = Number(rule.annotations[Annotation.panelID]);
|
|
const state = promAlertStateToAlertState(rule.state);
|
|
|
|
// there can be multiple alerts per panel, so we make sure we get the most severe state:
|
|
// alerting > pending > ok
|
|
if (!panelIdToAlertState[panelId]) {
|
|
panelIdToAlertState[panelId] = {
|
|
state,
|
|
id: Object.keys(panelIdToAlertState).length,
|
|
panelId,
|
|
dashboardId: dashboard.id,
|
|
};
|
|
} else if (
|
|
state === AlertState.Alerting &&
|
|
panelIdToAlertState[panelId].state !== AlertState.Alerting
|
|
) {
|
|
panelIdToAlertState[panelId].state = AlertState.Alerting;
|
|
} else if (
|
|
state === AlertState.Pending &&
|
|
panelIdToAlertState[panelId].state !== AlertState.Alerting &&
|
|
panelIdToAlertState[panelId].state !== AlertState.Pending
|
|
) {
|
|
panelIdToAlertState[panelId].state = AlertState.Pending;
|
|
}
|
|
}
|
|
})
|
|
);
|
|
return { alertStates: Object.values(panelIdToAlertState), annotations: [] };
|
|
}
|
|
throw new Error(`Unexpected alert rules response.`);
|
|
}),
|
|
catchError(handleDashboardQueryRunnerWorkerError)
|
|
);
|
|
}
|
|
}
|
|
|
|
function promAlertStateToAlertState(state: PromAlertingRuleState): AlertState {
|
|
if (state === PromAlertingRuleState.Firing) {
|
|
return AlertState.Alerting;
|
|
} else if (state === PromAlertingRuleState.Pending) {
|
|
return AlertState.Pending;
|
|
}
|
|
return AlertState.OK;
|
|
}
|