-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathapi.ts
More file actions
106 lines (96 loc) · 4.11 KB
/
api.ts
File metadata and controls
106 lines (96 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/* eslint-disable max-len */
import {
consoleFetchJSON,
PrometheusEndpoint,
PrometheusResponse,
} from '@openshift-console/dynamic-plugin-sdk';
import { getPrometheusBasePath, buildPrometheusUrl } from '../utils';
import { PROMETHEUS_QUERY_INTERVAL_SECONDS } from './utils';
const NO_TIMEOUT = -1;
/**
* Creates a Prometheus alerts query string from grouped alert values.
* The function dynamically includes any properties in the input objects that have the "src_" prefix,
* but the prefix is removed from the keys in the final query string.
*
* @param {Object[]} groupedAlertsValues - Array of grouped alert objects.
* Each alert object should contain various properties, including "src_" prefixed properties,
* as well as "layer" and "component" for constructing the meta fields in the query.
*
* @param {string} groupedAlertsValues[].layer - The layer of the alert, used in the absent condition.
* @param {string} groupedAlertsValues[].component - The component of the alert, used in the absent condition.
* @returns {string} - A string representing the combined Prometheus alerts query.
* Each alert query is formatted as `(ALERTS{key="value", ...} + on () group_left (component, layer) (absent(meta{layer="value", component="value"})))`
* and multiple queries are joined by "or".
*
* @example
* const alerts = [
* {
* src_alertname: "AlertmanagerReceiversNotConfigured",
* src_namespace: "openshift-monitoring",
* src_severity: "warning",
* layer: "core",
* component: "monitoring"
* },
* {
* src_alertname: "AnotherAlert",
* src_namespace: "default",
* src_severity: "critical",
* layer: "app",
* component: "frontend"
* }
* ];
*
* const query = createAlertsQuery(alerts);
* // Returns:
* // '(ALERTS{alertname="AlertmanagerReceiversNotConfigured", namespace="openshift-monitoring", severity="warning"} + on () group_left (component, layer) (absent(meta{layer="core", component="monitoring"}))) or
* // (ALERTS{alertname="AnotherAlert", namespace="default", severity="critical"} + on () group_left (component, layer) (absent(meta{layer="app", component="frontend"})))'
*/
export const createAlertsQuery = (groupedAlertsValues) => {
const alertsQuery = groupedAlertsValues
.map((query) => {
// Dynamically get all keys starting with "src_"
const srcKeys = Object.keys(query).filter((key) => key.startsWith('src_'));
// Create the alertParts array using the dynamically discovered src_ keys,
// but remove the "src_" prefix from the keys in the final query string.
const alertParts = srcKeys
.filter((key) => query[key]) // Only include keys that are present in the query object
.map((key) => `${key.replace('src_', '')}="${query[key]}"`) // Remove "src_" prefix from keys
.join(', ');
// Construct the query string for each grouped alert
return `(ALERTS{${alertParts}} + on () group_left (component, layer) (absent(meta{layer="${query.layer}", component="${query.component}"})))`;
})
.join(' or '); // Join all individual alert queries with "or"
// TODO: remove duplicated conditions, optimize query
return alertsQuery;
};
export const fetchDataForIncidentsAndAlerts = (
fetch: (url: string) => Promise<PrometheusResponse>,
range: { endTime: number; duration: number },
customQuery: string,
) => {
// Calculate samples to ensure step=PROMETHEUS_QUERY_INTERVAL_SECONDS (300s / 5 minutes)
// For 24h duration: Math.ceil(86400000 / 288 / 1000) = 300 seconds
const samples = Math.floor(range.duration / (PROMETHEUS_QUERY_INTERVAL_SECONDS * 1000));
const url = buildPrometheusUrl({
prometheusUrlProps: {
endpoint: PrometheusEndpoint.QUERY_RANGE,
endTime: range.endTime,
query: customQuery,
samples,
timespan: range.duration,
},
basePath: getPrometheusBasePath({
prometheus: 'cmo',
useTenancyPath: false,
}),
});
if (!url) {
// Return empty result when query is empty to avoid making invalid API calls
return Promise.resolve({
data: {
result: [],
},
});
}
return consoleFetchJSON(url, 'GET', {}, NO_TIMEOUT);
};