Files
grafana/public/app/features/alerting/unified/utils/datasource.ts
2024-05-24 16:40:49 +02:00

298 lines
10 KiB
TypeScript

import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceSettings } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import {
AlertManagerDataSourceJsonData,
AlertManagerImplementation,
AlertmanagerChoice,
} from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types';
import { RulesSource } from 'app/types/unified-alerting';
import { PromApplication, RulesSourceApplication } from 'app/types/unified-alerting-dto';
import { alertmanagerApi } from '../api/alertmanagerApi';
import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources';
import { isAlertManagerWithConfigAPI } from '../state/AlertmanagerContext';
import { instancesPermissions, notificationsPermissions } from './access-control';
import { getAllDataSources } from './config';
export const GRAFANA_RULES_SOURCE_NAME = 'grafana';
export const GRAFANA_DATASOURCE_NAME = '-- Grafana --';
export enum DataSourceType {
Alertmanager = 'alertmanager',
Loki = 'loki',
Prometheus = 'prometheus',
}
export interface AlertManagerDataSource {
name: string;
imgUrl: string;
meta?: DataSourceInstanceSettings['meta'];
hasConfigurationAPI?: boolean;
handleGrafanaManagedAlerts?: boolean;
}
export const RulesDataSourceTypes: string[] = [DataSourceType.Loki, DataSourceType.Prometheus];
export function getRulesDataSources() {
if (!contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalRead)) {
return [];
}
return getAllDataSources()
.filter((ds) => RulesDataSourceTypes.includes(ds.type) && ds.jsonData.manageAlerts !== false)
.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 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: 'public/img/grafana_icon.svg',
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,
};
if (contextSrv.hasPermission(permissions[permission].grafana)) {
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 getLotexDataSourceByName(dataSourceName: string): DataSourceInstanceSettings {
const dataSource = getDataSourceByName(dataSourceName);
if (!dataSource) {
throw new Error(`Data source ${dataSourceName} not found`);
}
if (dataSource.type !== DataSourceType.Loki && dataSource.type !== DataSourceType.Prometheus) {
throw new Error(`Unexpected data source type ${dataSource.type}`);
}
return dataSource;
}
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 getAllRulesSources(): RulesSource[] {
const availableRulesSources: RulesSource[] = getRulesDataSources();
if (contextSrv.hasPermission(AccessControlAction.AlertingRuleRead)) {
availableRulesSources.push(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 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 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 getApplicationFromRulesSource(rulesSource: RulesSource): RulesSourceApplication {
if (isGrafanaRulesSource(rulesSource)) {
return 'grafana';
}
// @TODO use buildinfo
if ('prometheusType' in rulesSource.jsonData) {
return rulesSource.jsonData?.prometheusType ?? PromApplication.Prometheus;
}
if (rulesSource.type === 'loki') {
return 'loki';
}
return PromApplication.Prometheus; // assume Prometheus if nothing matches
}