rules.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { capitalize } from 'lodash';
  2. import { AlertState } from '@grafana/data';
  3. import {
  4. Alert,
  5. AlertingRule,
  6. CloudRuleIdentifier,
  7. CombinedRuleGroup,
  8. GrafanaRuleIdentifier,
  9. PrometheusRuleIdentifier,
  10. PromRuleWithLocation,
  11. RecordingRule,
  12. Rule,
  13. RuleIdentifier,
  14. RuleNamespace,
  15. } from 'app/types/unified-alerting';
  16. import {
  17. GrafanaAlertState,
  18. GrafanaAlertStateWithReason,
  19. mapStateWithReasonToBaseState,
  20. PromAlertingRuleState,
  21. PromRuleType,
  22. RulerAlertingRuleDTO,
  23. RulerGrafanaRuleDTO,
  24. RulerRecordingRuleDTO,
  25. RulerRuleDTO,
  26. } from 'app/types/unified-alerting-dto';
  27. import { State } from '../components/StateTag';
  28. import { RULER_NOT_SUPPORTED_MSG } from './constants';
  29. import { AsyncRequestState } from './redux';
  30. export function isAlertingRule(rule: Rule | undefined): rule is AlertingRule {
  31. return typeof rule === 'object' && rule.type === PromRuleType.Alerting;
  32. }
  33. export function isRecordingRule(rule: Rule): rule is RecordingRule {
  34. return rule.type === PromRuleType.Recording;
  35. }
  36. export function isAlertingRulerRule(rule?: RulerRuleDTO): rule is RulerAlertingRuleDTO {
  37. return typeof rule === 'object' && 'alert' in rule;
  38. }
  39. export function isRecordingRulerRule(rule?: RulerRuleDTO): rule is RulerRecordingRuleDTO {
  40. return typeof rule === 'object' && 'record' in rule;
  41. }
  42. export function isGrafanaRulerRule(rule?: RulerRuleDTO): rule is RulerGrafanaRuleDTO {
  43. return typeof rule === 'object' && 'grafana_alert' in rule;
  44. }
  45. export function alertInstanceKey(alert: Alert): string {
  46. return JSON.stringify(alert.labels);
  47. }
  48. export function isRulerNotSupportedResponse(resp: AsyncRequestState<any>) {
  49. return resp.error && resp.error?.message?.includes(RULER_NOT_SUPPORTED_MSG);
  50. }
  51. export function isGrafanaRuleIdentifier(identifier: RuleIdentifier): identifier is GrafanaRuleIdentifier {
  52. return 'uid' in identifier;
  53. }
  54. export function isCloudRuleIdentifier(identifier: RuleIdentifier): identifier is CloudRuleIdentifier {
  55. return 'rulerRuleHash' in identifier;
  56. }
  57. export function isPrometheusRuleIdentifier(identifier: RuleIdentifier): identifier is PrometheusRuleIdentifier {
  58. return 'ruleHash' in identifier;
  59. }
  60. export function alertStateToReadable(state: PromAlertingRuleState | GrafanaAlertStateWithReason | AlertState): string {
  61. if (state === PromAlertingRuleState.Inactive) {
  62. return 'Normal';
  63. }
  64. return capitalize(state);
  65. }
  66. export const flattenRules = (rules: RuleNamespace[]) => {
  67. return rules.reduce<PromRuleWithLocation[]>((acc, { dataSourceName, name: namespaceName, groups }) => {
  68. groups.forEach(({ name: groupName, rules }) => {
  69. rules.forEach((rule) => {
  70. if (isAlertingRule(rule)) {
  71. acc.push({ dataSourceName, namespaceName, groupName, rule });
  72. }
  73. });
  74. });
  75. return acc;
  76. }, []);
  77. };
  78. export function alertStateToState(state: PromAlertingRuleState | GrafanaAlertStateWithReason | AlertState): State {
  79. let key: PromAlertingRuleState | GrafanaAlertState | AlertState;
  80. if (Object.values(AlertState).includes(state as AlertState)) {
  81. key = state as AlertState;
  82. } else {
  83. key = mapStateWithReasonToBaseState(state as GrafanaAlertStateWithReason | PromAlertingRuleState);
  84. }
  85. return alertStateToStateMap[key];
  86. }
  87. const alertStateToStateMap: Record<PromAlertingRuleState | GrafanaAlertState | AlertState, State> = {
  88. [PromAlertingRuleState.Inactive]: 'good',
  89. [PromAlertingRuleState.Firing]: 'bad',
  90. [PromAlertingRuleState.Pending]: 'warning',
  91. [GrafanaAlertState.Alerting]: 'bad',
  92. [GrafanaAlertState.Error]: 'bad',
  93. [GrafanaAlertState.NoData]: 'info',
  94. [GrafanaAlertState.Normal]: 'good',
  95. [GrafanaAlertState.Pending]: 'warning',
  96. [AlertState.NoData]: 'info',
  97. [AlertState.Paused]: 'warning',
  98. [AlertState.Alerting]: 'bad',
  99. [AlertState.OK]: 'good',
  100. [AlertState.Pending]: 'warning',
  101. [AlertState.Unknown]: 'info',
  102. };
  103. export function getFirstActiveAt(promRule: AlertingRule) {
  104. if (!promRule.alerts) {
  105. return null;
  106. }
  107. return promRule.alerts.reduce((prev, alert) => {
  108. const isNotNormal =
  109. mapStateWithReasonToBaseState(alert.state as GrafanaAlertStateWithReason) !== GrafanaAlertState.Normal;
  110. if (alert.activeAt && isNotNormal) {
  111. const activeAt = new Date(alert.activeAt);
  112. if (prev === null || prev.getTime() > activeAt.getTime()) {
  113. return activeAt;
  114. }
  115. }
  116. return prev;
  117. }, null as Date | null);
  118. }
  119. /**
  120. * A rule group is "federated" when it has at least one "source_tenants" entry, federated rule groups will evaluate rules in multiple tenants
  121. * Non-federated rules do not have this property
  122. *
  123. * see https://grafana.com/docs/metrics-enterprise/latest/tenant-management/tenant-federation/#cross-tenant-alerting-and-recording-rule-federation
  124. */
  125. export function isFederatedRuleGroup(group: CombinedRuleGroup) {
  126. return Array.isArray(group.source_tenants);
  127. }