The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/alerting/unified/utils/datasource.ts

391 lines
14 KiB

import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceSettings } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { PERMISSIONS_TIME_INTERVALS } from 'app/features/alerting/unified/components/mute-timings/permissions';
import { PERMISSIONS_NOTIFICATION_POLICIES } from 'app/features/alerting/unified/components/notification-policies/permissions';
import {
AlertManagerDataSourceJsonData,
AlertManagerImplementation,
AlertmanagerChoice,
} from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types';
import {
DataSourceRulesSourceIdentifier as DataSourceRulesSourceIdentifier,
GrafanaRulesSourceIdentifier,
GrafanaRulesSourceSymbol,
RuleIdentifier,
RulesSource,
RulesSourceIdentifier,
RulesSourceUid,
} from 'app/types/unified-alerting';
import grafanaIconSvg from 'img/grafana_icon.svg';
import { alertmanagerApi } from '../api/alertmanagerApi';
import { PERMISSIONS_CONTACT_POINTS } from '../components/contact-points/permissions';
import { PERMISSIONS_TEMPLATES } from '../components/templates/permissions';
import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources';
import { isAlertManagerWithConfigAPI } from '../state/AlertmanagerContext';
import { instancesPermissions, notificationsPermissions, silencesPermissions } from './access-control';
import { getAllDataSources } from './config';
import { isGrafanaRuleIdentifier } from './rules';
export const GRAFANA_RULES_SOURCE_NAME = 'grafana';
export const GRAFANA_DATASOURCE_NAME = '-- Grafana --';
export const GrafanaRulesSource: GrafanaRulesSourceIdentifier = {
uid: GrafanaRulesSourceSymbol,
name: GRAFANA_RULES_SOURCE_NAME,
ruleSourceType: 'grafana',
};
/**
* @deprecated use "SupportedRulesSourceType" and related types instead
*/
export enum DataSourceType {
Alertmanager = 'alertmanager',
Loki = 'loki',
Prometheus = 'prometheus',
AmazonPrometheus = 'grafana-amazonprometheus-datasource',
AzurePrometheus = 'grafana-azureprometheus-datasource',
}
export interface AlertManagerDataSource {
name: string;
imgUrl: string;
meta?: DataSourceInstanceSettings['meta'];
hasConfigurationAPI?: boolean;
handleGrafanaManagedAlerts?: boolean;
}
export function getRulesDataSources() {
const hasReadPermission = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalRead);
const hasWritePermission = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalWrite);
if (!hasReadPermission && !hasWritePermission) {
return [];
}
return getAllDataSources()
.filter((ds) => isSupportedExternalRulesSourceType(ds.type))
.filter((ds) => isDataSourceManagingAlerts(ds))
.sort((a, b) => a.name.localeCompare(b.name));
}
export function getRulesSourceUniqueKey(rulesSource: RulesSource): string {
return isGrafanaRulesSource(rulesSource) ? 'grafana' : (rulesSource.uid ?? rulesSource.id);
}
export function getRulesDataSource(rulesSourceName: string) {
return getRulesDataSources().find((x) => x.name === rulesSourceName);
}
export function getRulesDataSourceByUID(uid: string) {
return getRulesDataSources().find((x) => x.uid === uid);
}
export function getAlertManagerDataSources() {
return getAllDataSources()
.filter(isAlertmanagerDataSourceInstance)
.sort((a, b) => a.name.localeCompare(b.name));
}
export function isAlertmanagerDataSourceInstance(
dataSource: DataSourceInstanceSettings
): dataSource is DataSourceInstanceSettings<AlertManagerDataSourceJsonData> {
return dataSource.type === DataSourceType.Alertmanager;
}
export function isAlertmanagerDataSource(
dataSource: DataSourceSettings
): dataSource is DataSourceSettings<AlertManagerDataSourceJsonData> {
return dataSource.type === DataSourceType.Alertmanager;
}
export function getExternalDsAlertManagers() {
return getAlertManagerDataSources().filter((ds) => ds.jsonData.handleGrafanaManagedAlerts);
}
export function isAlertmanagerDataSourceInterestedInAlerts(
dataSourceSettings: DataSourceSettings<AlertManagerDataSourceJsonData>
) {
return dataSourceSettings.jsonData.handleGrafanaManagedAlerts === true;
}
const grafanaAlertManagerDataSource: AlertManagerDataSource = {
name: GRAFANA_RULES_SOURCE_NAME,
imgUrl: grafanaIconSvg,
hasConfigurationAPI: true,
};
// Used only as a fallback for Alert Group plugin
export function getAllAlertManagerDataSources(): AlertManagerDataSource[] {
return [
grafanaAlertManagerDataSource,
...getAlertManagerDataSources().map<AlertManagerDataSource>((ds) => ({
name: ds.name,
displayName: ds.name,
imgUrl: ds.meta.info.logos.small,
meta: ds.meta,
})),
];
}
/**
* This method gets all alert managers that the user has access, and then filter them first by being able to handle grafana managed alerts,
* and then depending on the current alerting configuration returns either only the internal alert managers, only the external alert managers, or both.
*
*/
export function useGetAlertManagerDataSourcesByPermissionAndConfig(
permission: 'instance' | 'notification'
): AlertManagerDataSource[] {
const allAlertManagersByPermission = useAlertManagersByPermission(permission); // this hook memoizes the result of getAlertManagerDataSourcesByPermission
const externalDsAlertManagers: AlertManagerDataSource[] =
allAlertManagersByPermission.availableExternalDataSources.filter((ds) => ds.handleGrafanaManagedAlerts);
const internalDSAlertManagers = allAlertManagersByPermission.availableInternalDataSources;
//get current alerting configuration
const { currentData: amConfigStatus } = alertmanagerApi.endpoints.getGrafanaAlertingConfigurationStatus.useQuery();
const alertmanagerChoice = amConfigStatus?.alertmanagersChoice;
switch (alertmanagerChoice) {
case AlertmanagerChoice.Internal:
return internalDSAlertManagers;
case AlertmanagerChoice.External:
return externalDsAlertManagers;
default:
return [...internalDSAlertManagers, ...externalDsAlertManagers];
}
}
/**
* This method gets all alert managers that the user has access to and then split them into two groups:
* 1. Internal alert managers
* 2. External alert managers
*/
export function getAlertManagerDataSourcesByPermission(permission: 'instance' | 'notification'): {
availableInternalDataSources: AlertManagerDataSource[];
availableExternalDataSources: AlertManagerDataSource[];
} {
const availableInternalDataSources: AlertManagerDataSource[] = [];
const availableExternalDataSources: AlertManagerDataSource[] = [];
const permissions = {
instance: instancesPermissions.read,
notification: notificationsPermissions.read,
silence: silencesPermissions.read,
};
const builtinAlertmanagerPermissions = [
...Object.values(permissions).flatMap((permissions) => permissions.grafana),
...PERMISSIONS_CONTACT_POINTS,
...PERMISSIONS_NOTIFICATION_POLICIES,
...PERMISSIONS_TEMPLATES,
...PERMISSIONS_TIME_INTERVALS,
];
const hasPermissionsForInternalAlertmanager = builtinAlertmanagerPermissions.some((permission) =>
contextSrv.hasPermission(permission)
);
if (hasPermissionsForInternalAlertmanager) {
availableInternalDataSources.push(grafanaAlertManagerDataSource);
}
if (contextSrv.hasPermission(permissions[permission].external)) {
const cloudSources = getAlertManagerDataSources().map<AlertManagerDataSource>((ds) => ({
name: ds.name,
displayName: ds.name,
imgUrl: ds.meta.info.logos.small,
meta: ds.meta,
hasConfigurationAPI: isAlertManagerWithConfigAPI(ds.jsonData),
handleGrafanaManagedAlerts: ds.jsonData.handleGrafanaManagedAlerts,
}));
availableExternalDataSources.push(...cloudSources);
}
return { availableInternalDataSources, availableExternalDataSources };
}
export function getAllRulesSourceNames(): string[] {
const availableRulesSources: string[] = getRulesDataSources().map((r) => r.name);
if (contextSrv.hasPermission(AccessControlAction.AlertingRuleRead)) {
availableRulesSources.push(GRAFANA_RULES_SOURCE_NAME);
}
return availableRulesSources;
}
export function getExternalRulesSources(): DataSourceRulesSourceIdentifier[] {
return getRulesDataSources().map((ds) => ({
name: ds.name,
uid: ds.uid,
ruleSourceType: 'datasource',
}));
}
export function getAllRulesSources(): RulesSource[] {
const availableRulesSources: RulesSource[] = getRulesDataSources();
if (contextSrv.hasPermission(AccessControlAction.AlertingRuleRead)) {
availableRulesSources.unshift(GRAFANA_RULES_SOURCE_NAME);
}
return availableRulesSources;
}
export function getRulesSourceName(rulesSource: RulesSource): string {
return isCloudRulesSource(rulesSource) ? rulesSource.name : rulesSource;
}
export function getRulesSourceUid(rulesSource: RulesSource): string {
return isCloudRulesSource(rulesSource) ? rulesSource.uid : GRAFANA_RULES_SOURCE_NAME;
}
export function isCloudRulesSource(rulesSource: RulesSource | string): rulesSource is DataSourceInstanceSettings {
return rulesSource !== GRAFANA_RULES_SOURCE_NAME;
}
export function isVanillaPrometheusAlertManagerDataSource(name: string): boolean {
return (
name !== GRAFANA_RULES_SOURCE_NAME &&
getAlertmanagerDataSourceByName(name)?.jsonData?.implementation === AlertManagerImplementation.prometheus
);
}
export function isProvisionedDataSource(dataSource: DataSourceSettings): boolean {
return dataSource.readOnly === true;
}
export function isGrafanaRulesSource(
rulesSource: RulesSource | string
): rulesSource is typeof GRAFANA_RULES_SOURCE_NAME {
return rulesSource === GRAFANA_RULES_SOURCE_NAME;
}
export function getDataSourceByName(name: string): DataSourceInstanceSettings<DataSourceJsonData> | undefined {
return getAllDataSources().find((source) => source.name === name);
}
export function getDataSourceByUid(dsUid: string): DataSourceInstanceSettings<DataSourceJsonData> | undefined {
return getAllDataSources().find((source) => source.uid === dsUid);
}
export function getAlertmanagerDataSourceByName(name: string) {
return getAllDataSources()
.filter(isAlertmanagerDataSourceInstance)
.find((source) => source.name === name);
}
export function getRulesSourceByName(name: string): RulesSource | undefined {
if (name === GRAFANA_RULES_SOURCE_NAME) {
return GRAFANA_RULES_SOURCE_NAME;
}
return getDataSourceByName(name);
}
export function getDatasourceAPIId(dataSourceName: string) {
if (dataSourceName === GRAFANA_RULES_SOURCE_NAME) {
return GRAFANA_RULES_SOURCE_NAME;
}
const ds = getDataSourceByName(dataSourceName);
if (!ds) {
throw new Error(`Datasource "${dataSourceName}" not found`);
}
return String(ds.id);
}
export function getDatasourceAPIUid(dataSourceName: string) {
if (dataSourceName === GRAFANA_RULES_SOURCE_NAME) {
return GRAFANA_RULES_SOURCE_NAME;
}
const ds = getDataSourceByName(dataSourceName);
if (!ds) {
throw new Error(`Datasource "${dataSourceName}" not found`);
}
return ds.uid;
}
export function getDataSourceUID(rulesSourceIdentifier: { rulesSourceName: string } | { uid: RulesSourceUid }) {
if ('uid' in rulesSourceIdentifier) {
return rulesSourceIdentifier.uid;
}
if (rulesSourceIdentifier.rulesSourceName === GRAFANA_RULES_SOURCE_NAME) {
return GrafanaRulesSourceSymbol;
}
const ds = getRulesDataSource(rulesSourceIdentifier.rulesSourceName);
if (!ds) {
return undefined;
}
return ds.uid;
}
export function getFirstCompatibleDataSource(): DataSourceInstanceSettings<DataSourceJsonData> | undefined {
return getDataSourceSrv().getList({ alerting: true })[0];
}
export function getDefaultOrFirstCompatibleDataSource(): DataSourceInstanceSettings<DataSourceJsonData> | undefined {
const defaultDataSource = getDataSourceSrv().getInstanceSettings('default');
const defaultIsCompatible = defaultDataSource?.meta.alerting ?? false;
return defaultIsCompatible ? defaultDataSource : getFirstCompatibleDataSource();
}
export function isDataSourceManagingAlerts(ds: DataSourceInstanceSettings<DataSourceJsonData>) {
return ds.jsonData.manageAlerts !== false; //if this prop is undefined it defaults to true
}
export function ruleIdentifierToRuleSourceIdentifier(ruleIdentifier: RuleIdentifier): RulesSourceIdentifier {
if (isGrafanaRuleIdentifier(ruleIdentifier)) {
return { uid: GrafanaRulesSourceSymbol, name: GRAFANA_RULES_SOURCE_NAME, ruleSourceType: 'grafana' };
}
return {
uid: getDatasourceAPIUid(ruleIdentifier.ruleSourceName),
name: ruleIdentifier.ruleSourceName,
ruleSourceType: 'datasource',
};
}
/**
* Check if the given type is a supported external Prometheus flavored rules source type.
*/
export function isSupportedExternalPrometheusFlavoredRulesSourceType(
type: string
): type is SupportedExternalPrometheusFlavoredRulesSourceType {
return SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES.find((t) => t === type) !== undefined;
}
export const SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES = [
'prometheus',
'grafana-amazonprometheus-datasource',
'grafana-azureprometheus-datasource',
] as const;
export type SupportedExternalPrometheusFlavoredRulesSourceType =
(typeof SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES)[number]; // infer the type from the tuple above so we can maintain a single source of truth
/**
* Check if the given type is a supported external rules source type. Includes Loki and Prometheus flavored types.
*/
export function isSupportedExternalRulesSourceType(type: string): type is SupportedExternalRulesSourceType {
return SUPPORTED_EXTERNAL_RULE_SOURCE_TYPES.find((t) => t === type) !== undefined;
}
export type SupportedExternalRulesSourceType = 'loki' | SupportedExternalPrometheusFlavoredRulesSourceType;
export const SUPPORTED_EXTERNAL_RULE_SOURCE_TYPES = [
'loki',
...SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES,
] as const;
/**
* Check if the given type is a supported rules source type. Includes "grafana" for Grafana Managed Rules.
*/
export function isSupportedRulesSourceType(type: string): type is SupportedRulesSourceType {
return type === GRAFANA_RULES_SOURCE_NAME || isSupportedExternalRulesSourceType(type);
}
export type SupportedRulesSourceType = 'grafana' | SupportedExternalRulesSourceType;
export const SUPPORTED_RULE_SOURCE_TYPES = [
GRAFANA_RULES_SOURCE_NAME,
...SUPPORTED_EXTERNAL_RULE_SOURCE_TYPES,
] as const satisfies string[];