UnifiedAlertStatesWorker.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { from, Observable } from 'rxjs';
  2. import { catchError, map } from 'rxjs/operators';
  3. import { AlertState, AlertStateInfo } from '@grafana/data';
  4. import { getBackendSrv } from '@grafana/runtime';
  5. import { contextSrv } from 'app/core/services/context_srv';
  6. import { Annotation } from 'app/features/alerting/unified/utils/constants';
  7. import { isAlertingRule } from 'app/features/alerting/unified/utils/rules';
  8. import { AccessControlAction } from 'app/types';
  9. import { PromAlertingRuleState, PromRulesResponse } from 'app/types/unified-alerting-dto';
  10. import { DashboardQueryRunnerOptions, DashboardQueryRunnerWorker, DashboardQueryRunnerWorkerResult } from './types';
  11. import { emptyResult, handleDashboardQueryRunnerWorkerError } from './utils';
  12. export class UnifiedAlertStatesWorker implements DashboardQueryRunnerWorker {
  13. // maps dashboard uid to wether it has alert rules.
  14. // if it is determined that a dashboard does not have alert rules,
  15. // further attempts to get alert states for it will not be made
  16. private hasAlertRules: Record<string, boolean> = {};
  17. canWork({ dashboard, range }: DashboardQueryRunnerOptions): boolean {
  18. if (!dashboard.uid) {
  19. return false;
  20. }
  21. if (range.raw.to !== 'now') {
  22. return false;
  23. }
  24. if (this.hasAlertRules[dashboard.uid] === false) {
  25. return false;
  26. }
  27. const hasRuleReadPermission =
  28. contextSrv.hasPermission(AccessControlAction.AlertingRuleRead) &&
  29. contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalRead);
  30. if (!hasRuleReadPermission) {
  31. return false;
  32. }
  33. return true;
  34. }
  35. work(options: DashboardQueryRunnerOptions): Observable<DashboardQueryRunnerWorkerResult> {
  36. if (!this.canWork(options)) {
  37. return emptyResult();
  38. }
  39. const { dashboard } = options;
  40. return from(
  41. getBackendSrv().get(
  42. '/api/prometheus/grafana/api/v1/rules',
  43. {
  44. dashboard_uid: dashboard.uid,
  45. },
  46. `dashboard-query-runner-unified-alert-states-${dashboard.id}`
  47. )
  48. ).pipe(
  49. map((result: PromRulesResponse) => {
  50. if (result.status === 'success') {
  51. this.hasAlertRules[dashboard.uid] = false;
  52. const panelIdToAlertState: Record<number, AlertStateInfo> = {};
  53. result.data.groups.forEach((group) =>
  54. group.rules.forEach((rule) => {
  55. if (isAlertingRule(rule) && rule.annotations && rule.annotations[Annotation.panelID]) {
  56. this.hasAlertRules[dashboard.uid] = true;
  57. const panelId = Number(rule.annotations[Annotation.panelID]);
  58. const state = promAlertStateToAlertState(rule.state);
  59. // there can be multiple alerts per panel, so we make sure we get the most severe state:
  60. // alerting > pending > ok
  61. if (!panelIdToAlertState[panelId]) {
  62. panelIdToAlertState[panelId] = {
  63. state,
  64. id: Object.keys(panelIdToAlertState).length,
  65. panelId,
  66. dashboardId: dashboard.id,
  67. };
  68. } else if (
  69. state === AlertState.Alerting &&
  70. panelIdToAlertState[panelId].state !== AlertState.Alerting
  71. ) {
  72. panelIdToAlertState[panelId].state = AlertState.Alerting;
  73. } else if (
  74. state === AlertState.Pending &&
  75. panelIdToAlertState[panelId].state !== AlertState.Alerting &&
  76. panelIdToAlertState[panelId].state !== AlertState.Pending
  77. ) {
  78. panelIdToAlertState[panelId].state = AlertState.Pending;
  79. }
  80. }
  81. })
  82. );
  83. return { alertStates: Object.values(panelIdToAlertState), annotations: [] };
  84. }
  85. throw new Error(`Unexpected alert rules response.`);
  86. }),
  87. catchError(handleDashboardQueryRunnerWorkerError)
  88. );
  89. }
  90. }
  91. function promAlertStateToAlertState(state: PromAlertingRuleState): AlertState {
  92. if (state === PromAlertingRuleState.Firing) {
  93. return AlertState.Alerting;
  94. } else if (state === PromAlertingRuleState.Pending) {
  95. return AlertState.Pending;
  96. }
  97. return AlertState.OK;
  98. }