123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- import { CombinedRule, Rule, RuleIdentifier, RuleWithLocation } from 'app/types/unified-alerting';
- import { Annotations, Labels, RulerRuleDTO } from 'app/types/unified-alerting-dto';
- import { GRAFANA_RULES_SOURCE_NAME } from './datasource';
- import {
- isAlertingRule,
- isAlertingRulerRule,
- isCloudRuleIdentifier,
- isGrafanaRuleIdentifier,
- isGrafanaRulerRule,
- isPrometheusRuleIdentifier,
- isRecordingRule,
- isRecordingRulerRule,
- } from './rules';
- export function fromRulerRule(
- ruleSourceName: string,
- namespace: string,
- groupName: string,
- rule: RulerRuleDTO
- ): RuleIdentifier {
- if (isGrafanaRulerRule(rule)) {
- return { uid: rule.grafana_alert.uid!, ruleSourceName: 'grafana' };
- }
- return {
- ruleSourceName,
- namespace,
- groupName,
- rulerRuleHash: hashRulerRule(rule),
- };
- }
- export function fromRule(ruleSourceName: string, namespace: string, groupName: string, rule: Rule): RuleIdentifier {
- return {
- ruleSourceName,
- namespace,
- groupName,
- ruleHash: hashRule(rule),
- };
- }
- export function fromCombinedRule(ruleSourceName: string, rule: CombinedRule): RuleIdentifier {
- const namespaceName = rule.namespace.name;
- const groupName = rule.group.name;
- if (rule.rulerRule) {
- return fromRulerRule(ruleSourceName, namespaceName, groupName, rule.rulerRule);
- }
- if (rule.promRule) {
- return fromRule(ruleSourceName, namespaceName, groupName, rule.promRule);
- }
- throw new Error('Could not create an id for a rule that is missing both `rulerRule` and `promRule`.');
- }
- export function fromRuleWithLocation(rule: RuleWithLocation): RuleIdentifier {
- return fromRulerRule(rule.ruleSourceName, rule.namespace, rule.group.name, rule.rule);
- }
- export function equal(a: RuleIdentifier, b: RuleIdentifier) {
- if (isGrafanaRuleIdentifier(a) && isGrafanaRuleIdentifier(b)) {
- return a.uid === b.uid;
- }
- if (isCloudRuleIdentifier(a) && isCloudRuleIdentifier(b)) {
- return (
- a.groupName === b.groupName &&
- a.namespace === b.namespace &&
- a.rulerRuleHash === b.rulerRuleHash &&
- a.ruleSourceName === b.ruleSourceName
- );
- }
- if (isPrometheusRuleIdentifier(a) && isPrometheusRuleIdentifier(b)) {
- return (
- a.groupName === b.groupName &&
- a.namespace === b.namespace &&
- a.ruleHash === b.ruleHash &&
- a.ruleSourceName === b.ruleSourceName
- );
- }
- return false;
- }
- const cloudRuleIdentifierPrefix = 'cri';
- const prometheusRuleIdentifierPrefix = 'pri';
- function escapeDollars(value: string): string {
- return value.replace(/\$/g, '_DOLLAR_');
- }
- function unesacapeDollars(value: string): string {
- return value.replace(/\_DOLLAR\_/g, '$');
- }
- export function parse(value: string, decodeFromUri = false): RuleIdentifier {
- const source = decodeFromUri ? decodeURIComponent(value) : value;
- const parts = source.split('$');
- if (parts.length === 1) {
- return { uid: value, ruleSourceName: 'grafana' };
- }
- if (parts.length === 5) {
- const [prefix, ruleSourceName, namespace, groupName, hash] = parts.map(unesacapeDollars);
- if (prefix === cloudRuleIdentifierPrefix) {
- return { ruleSourceName, namespace, groupName, rulerRuleHash: Number(hash) };
- }
- if (prefix === prometheusRuleIdentifierPrefix) {
- return { ruleSourceName, namespace, groupName, ruleHash: Number(hash) };
- }
- }
- throw new Error(`Failed to parse rule location: ${value}`);
- }
- export function tryParse(value: string | undefined, decodeFromUri = false): RuleIdentifier | undefined {
- if (!value) {
- return;
- }
- try {
- return parse(value, decodeFromUri);
- } catch (error) {
- return;
- }
- }
- export function stringifyIdentifier(identifier: RuleIdentifier): string {
- if (isGrafanaRuleIdentifier(identifier)) {
- return identifier.uid;
- }
- if (isCloudRuleIdentifier(identifier)) {
- return [
- cloudRuleIdentifierPrefix,
- identifier.ruleSourceName,
- identifier.namespace,
- identifier.groupName,
- identifier.rulerRuleHash,
- ]
- .map(String)
- .map(escapeDollars)
- .join('$');
- }
- return [
- prometheusRuleIdentifierPrefix,
- identifier.ruleSourceName,
- identifier.namespace,
- identifier.groupName,
- identifier.ruleHash,
- ]
- .map(String)
- .map(escapeDollars)
- .join('$');
- }
- function hash(value: string): number {
- let hash = 0;
- if (value.length === 0) {
- return hash;
- }
- for (var i = 0; i < value.length; i++) {
- var char = value.charCodeAt(i);
- hash = (hash << 5) - hash + char;
- hash = hash & hash; // Convert to 32bit integer
- }
- return hash;
- }
- // this is used to identify lotex rules, as they do not have a unique identifier
- function hashRulerRule(rule: RulerRuleDTO): number {
- if (isRecordingRulerRule(rule)) {
- return hash(JSON.stringify([rule.record, rule.expr, hashLabelsOrAnnotations(rule.labels)]));
- } else if (isAlertingRulerRule(rule)) {
- return hash(
- JSON.stringify([
- rule.alert,
- rule.expr,
- hashLabelsOrAnnotations(rule.annotations),
- hashLabelsOrAnnotations(rule.labels),
- ])
- );
- } else {
- throw new Error('only recording and alerting ruler rules can be hashed');
- }
- }
- function hashRule(rule: Rule): number {
- if (isRecordingRule(rule)) {
- return hash(JSON.stringify([rule.type, rule.query, hashLabelsOrAnnotations(rule.labels)]));
- }
- if (isAlertingRule(rule)) {
- return hash(
- JSON.stringify([
- rule.type,
- rule.query,
- hashLabelsOrAnnotations(rule.annotations),
- hashLabelsOrAnnotations(rule.labels),
- ])
- );
- }
- throw new Error('only recording and alerting rules can be hashed');
- }
- function hashLabelsOrAnnotations(item: Labels | Annotations | undefined): string {
- return JSON.stringify(Object.entries(item || {}).sort((a, b) => a[0].localeCompare(b[0])));
- }
- export function ruleIdentifierToRuleSourceName(identifier: RuleIdentifier): string {
- return isGrafanaRuleIdentifier(identifier) ? GRAFANA_RULES_SOURCE_NAME : identifier.ruleSourceName;
- }
|