mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Use new readonly permission endpoints for getting contact points and mute timings (#82132)
* use new read only contact points list endpoint in simplified routing section * Dont use alertmanager endpoint to get groupby defaults * Use the new read only endpoint for mute timings in route settings * review suggestions * Rename hook * Use options in params for useContactPointsWithStatus hook * Refactor useContactPointsWithStatus * second part of the enhanceContactPointsWithMetadata refactor
This commit is contained in:
parent
ed9e26122a
commit
3e93a0991f
@ -11,7 +11,9 @@ import {
|
||||
ExternalAlertmanagerConfig,
|
||||
ExternalAlertmanagers,
|
||||
ExternalAlertmanagersResponse,
|
||||
GrafanaManagedContactPoint,
|
||||
Matcher,
|
||||
MuteTimeInterval,
|
||||
} from '../../../../plugins/datasource/alertmanager/types';
|
||||
import { NotifierDTO } from '../../../../types';
|
||||
import { withPerformanceLogging } from '../Analytics';
|
||||
@ -257,5 +259,12 @@ export const alertmanagerApi = alertingApi.injectEndpoints({
|
||||
}));
|
||||
},
|
||||
}),
|
||||
// Grafana Managed Alertmanager only
|
||||
getContactPointsList: build.query<GrafanaManagedContactPoint[], void>({
|
||||
query: () => ({ url: '/api/v1/notifications/receivers' }),
|
||||
}),
|
||||
getMuteTimingList: build.query<MuteTimeInterval[], void>({
|
||||
query: () => ({ url: '/api/v1/notifications/time-intervals' }),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
@ -27,7 +27,13 @@ const RECEIVER_STATUS_POLLING_INTERVAL = 10 * 1000; // 10 seconds
|
||||
* 3. (if available) additional metadata about Grafana Managed contact points
|
||||
* 4. (if available) the OnCall plugin metadata
|
||||
*/
|
||||
export function useContactPointsWithStatus() {
|
||||
interface UseContactPointsWithStatusOptions {
|
||||
includePoliciesCount: boolean;
|
||||
}
|
||||
|
||||
export function useContactPointsWithStatus(
|
||||
{ includePoliciesCount }: UseContactPointsWithStatusOptions = { includePoliciesCount: true }
|
||||
) {
|
||||
const { selectedAlertmanager, isGrafanaAlertmanager } = useAlertmanager();
|
||||
const { installed: onCallPluginInstalled, loading: onCallPluginStatusLoading } = usePluginBridge(
|
||||
SupportedPlugin.OnCall
|
||||
@ -64,6 +70,7 @@ export function useContactPointsWithStatus() {
|
||||
}
|
||||
|
||||
// fetch the latest config from the Alertmanager
|
||||
// we use this endpoint only when we need to get the number of policies
|
||||
const fetchAlertmanagerConfiguration = alertmanagerApi.endpoints.getAlertmanagerConfiguration.useQuery(
|
||||
selectedAlertmanager!,
|
||||
{
|
||||
@ -73,31 +80,56 @@ export function useContactPointsWithStatus() {
|
||||
...result,
|
||||
contactPoints: result.data
|
||||
? enhanceContactPointsWithMetadata(
|
||||
result.data,
|
||||
fetchContactPointsStatus.data,
|
||||
fetchReceiverMetadata.data,
|
||||
onCallMetadata
|
||||
onCallMetadata,
|
||||
result.data.alertmanager_config.receivers ?? [],
|
||||
result.data
|
||||
)
|
||||
: [],
|
||||
}),
|
||||
skip: !includePoliciesCount,
|
||||
}
|
||||
);
|
||||
|
||||
// for Grafana Managed Alertmanager, we use the new read-only endpoint for getting the list of contact points
|
||||
const fetchGrafanaContactPoints = alertmanagerApi.endpoints.getContactPointsList.useQuery(undefined, {
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
selectFromResult: (result) => ({
|
||||
...result,
|
||||
contactPoints: result.data
|
||||
? enhanceContactPointsWithMetadata(
|
||||
fetchContactPointsStatus.data,
|
||||
fetchReceiverMetadata.data,
|
||||
onCallMetadata,
|
||||
result.data, // contact points from the new readonly endpoint
|
||||
undefined //no config data
|
||||
)
|
||||
: [],
|
||||
}),
|
||||
skip: includePoliciesCount || !isGrafanaAlertmanager,
|
||||
});
|
||||
|
||||
// we will fail silently for fetching OnCall plugin status and integrations
|
||||
const error = fetchAlertmanagerConfiguration.error ?? fetchContactPointsStatus.error;
|
||||
const error =
|
||||
fetchAlertmanagerConfiguration.error || fetchGrafanaContactPoints.error || fetchContactPointsStatus.error;
|
||||
const isLoading =
|
||||
fetchAlertmanagerConfiguration.isLoading ||
|
||||
fetchGrafanaContactPoints.isLoading ||
|
||||
fetchContactPointsStatus.isLoading ||
|
||||
onCallPluginStatusLoading ||
|
||||
onCallPluginIntegrationsLoading;
|
||||
|
||||
const contactPoints = fetchAlertmanagerConfiguration.contactPoints.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const unsortedContactPoints = includePoliciesCount
|
||||
? fetchAlertmanagerConfiguration.contactPoints
|
||||
: fetchGrafanaContactPoints.contactPoints;
|
||||
const contactPoints = unsortedContactPoints.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return {
|
||||
error,
|
||||
isLoading,
|
||||
contactPoints,
|
||||
refetchReceivers: fetchAlertmanagerConfiguration.refetch,
|
||||
refetchReceivers: fetchGrafanaContactPoints.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
GrafanaManagedContactPoint,
|
||||
GrafanaManagedReceiverConfig,
|
||||
MatcherOperator,
|
||||
Receiver,
|
||||
Route,
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { NotifierDTO, NotifierStatus, ReceiversStateDTO } from 'app/types';
|
||||
@ -30,6 +31,9 @@ export function isProvisioned(contactPoint: GrafanaManagedContactPoint) {
|
||||
|
||||
// TODO we should really add some type information to these receiver settings...
|
||||
export function getReceiverDescription(receiver: ReceiverConfigWithMetadata): ReactNode | undefined {
|
||||
if (!receiver.settings) {
|
||||
return undefined;
|
||||
}
|
||||
switch (receiver.type) {
|
||||
case 'email': {
|
||||
const hasEmailAddresses = 'addresses' in receiver.settings; // when dealing with alertmanager email_configs we don't normalize the settings
|
||||
@ -87,7 +91,7 @@ export interface ReceiverConfigWithMetadata extends GrafanaManagedReceiverConfig
|
||||
}
|
||||
|
||||
export interface ContactPointWithMetadata extends GrafanaManagedContactPoint {
|
||||
numberOfPolicies: number;
|
||||
numberOfPolicies?: number; // now is optional as we don't have the data from the read-only endpoint
|
||||
grafana_managed_receiver_configs: ReceiverConfigWithMetadata[];
|
||||
}
|
||||
|
||||
@ -95,30 +99,36 @@ export interface ContactPointWithMetadata extends GrafanaManagedContactPoint {
|
||||
* This function adds the status information for each of the integrations (contact point types) in a contact point
|
||||
* 1. we iterate over all contact points
|
||||
* 2. for each contact point we "enhance" it with the status or "undefined" for vanilla Alertmanager
|
||||
* contactPoints: list of contact points
|
||||
* alertmanagerConfiguration: optional as is passed when we need to get number of policies for each contact point
|
||||
* and we prefer using the data from the read-only endpoint.
|
||||
*/
|
||||
export function enhanceContactPointsWithMetadata(
|
||||
result: AlertManagerCortexConfig,
|
||||
status: ReceiversStateDTO[] = [],
|
||||
notifiers: NotifierDTO[] = [],
|
||||
onCallIntegrations: OnCallIntegrationDTO[] | undefined | null
|
||||
onCallIntegrations: OnCallIntegrationDTO[] | undefined | null,
|
||||
contactPoints: Receiver[],
|
||||
alertmanagerConfiguration?: AlertManagerCortexConfig
|
||||
): ContactPointWithMetadata[] {
|
||||
const contactPoints = result.alertmanager_config.receivers ?? [];
|
||||
|
||||
// compute the entire inherited tree before finding what notification policies are using a particular contact point
|
||||
const fullyInheritedTree = computeInheritedTree(result?.alertmanager_config?.route ?? {});
|
||||
const fullyInheritedTree = computeInheritedTree(alertmanagerConfiguration?.alertmanager_config?.route ?? {});
|
||||
const usedContactPoints = getUsedContactPoints(fullyInheritedTree);
|
||||
const usedContactPointsByName = countBy(usedContactPoints);
|
||||
|
||||
return contactPoints.map((contactPoint) => {
|
||||
const contactPointsList = alertmanagerConfiguration
|
||||
? alertmanagerConfiguration?.alertmanager_config.receivers ?? []
|
||||
: contactPoints ?? [];
|
||||
|
||||
return contactPointsList.map((contactPoint) => {
|
||||
const receivers = extractReceivers(contactPoint);
|
||||
const statusForReceiver = status.find((status) => status.name === contactPoint.name);
|
||||
|
||||
return {
|
||||
...contactPoint,
|
||||
numberOfPolicies: usedContactPointsByName[contactPoint.name] ?? 0,
|
||||
numberOfPolicies:
|
||||
alertmanagerConfiguration && usedContactPointsByName && (usedContactPointsByName[contactPoint.name] ?? 0),
|
||||
grafana_managed_receiver_configs: receivers.map((receiver, index) => {
|
||||
const isOnCallReceiver = receiver.type === ReceiverTypes.OnCall;
|
||||
|
||||
return {
|
||||
...receiver,
|
||||
[RECEIVER_STATUS_KEY]: statusForReceiver?.integrations[index],
|
||||
@ -130,6 +140,7 @@ export function enhanceContactPointsWithMetadata(
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function isAutoGeneratedPolicy(route: Route) {
|
||||
const simplifiedRoutingToggleEnabled = config.featureToggles.alertingSimplifiedRouting ?? false;
|
||||
if (!simplifiedRoutingToggleEnabled) {
|
||||
|
@ -22,7 +22,12 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const alertManagerName = alertManager.name;
|
||||
const { isLoading, error: errorInContactPointStatus, contactPoints, refetchReceivers } = useContactPointsWithStatus();
|
||||
const {
|
||||
isLoading,
|
||||
error: errorInContactPointStatus,
|
||||
contactPoints,
|
||||
refetchReceivers,
|
||||
} = useContactPointsWithStatus({ includePoliciesCount: false });
|
||||
const [selectedContactPointWithMetadata, setSelectedContactPointWithMetadata] = useState<
|
||||
ContactPointWithMetadata | undefined
|
||||
>();
|
||||
|
@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Field, InputControl, MultiSelect, useStyles2 } from '@grafana/ui';
|
||||
import { useMuteTimingOptions } from 'app/features/alerting/unified/hooks/useMuteTimingOptions';
|
||||
import { alertmanagerApi } from 'app/features/alerting/unified/api/alertmanagerApi';
|
||||
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||
import { timeIntervalToString } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { mapMultiSelectValueToStrings } from 'app/features/alerting/unified/utils/amroutes';
|
||||
|
||||
import { getFormStyles } from '../../../../notification-policies/formStyles';
|
||||
@ -19,7 +21,7 @@ export function MuteTimingFields({ alertManager }: MuteTimingFieldsProps) {
|
||||
formState: { errors },
|
||||
} = useFormContext<RuleFormValues>();
|
||||
|
||||
const muteTimingOptions = useMuteTimingOptions();
|
||||
const muteTimingOptions = useSelectableMuteTimings();
|
||||
return (
|
||||
<Field
|
||||
label="Mute timings"
|
||||
@ -44,3 +46,21 @@ export function MuteTimingFields({ alertManager }: MuteTimingFieldsProps) {
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
function useSelectableMuteTimings(): Array<SelectableValue<string>> {
|
||||
const fetchGrafanaMuteTimings = alertmanagerApi.endpoints.getMuteTimingList.useQuery(undefined, {
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
selectFromResult: (result) => ({
|
||||
...result,
|
||||
mutetimings: result.data
|
||||
? result.data.map((value) => ({
|
||||
value: value.name,
|
||||
label: value.name,
|
||||
description: value.time_intervals.map((interval) => timeIntervalToString(interval)).join(', AND '),
|
||||
}))
|
||||
: [],
|
||||
}),
|
||||
});
|
||||
return fetchGrafanaMuteTimings.mutetimings;
|
||||
}
|
||||
|
@ -14,8 +14,6 @@ import {
|
||||
Text,
|
||||
useStyles2,
|
||||
} from '@grafana/ui';
|
||||
import { useAlertmanagerConfig } from 'app/features/alerting/unified/hooks/useAlertmanagerConfig';
|
||||
import { useAlertmanager } from 'app/features/alerting/unified/state/AlertmanagerContext';
|
||||
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||
import {
|
||||
commonGroupByOptions,
|
||||
@ -42,7 +40,7 @@ export const RoutingSettings = ({ alertManager }: RoutingSettingsProps) => {
|
||||
formState: { errors },
|
||||
} = useFormContext<RuleFormValues>();
|
||||
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues([]));
|
||||
const { groupBy, groupIntervalValue, groupWaitValue, repeatIntervalValue } = useGetDefaultsForRoutingSettings();
|
||||
const { groupIntervalValue, groupWaitValue, repeatIntervalValue } = getDefaultsForRoutingSettings();
|
||||
const overrideGrouping = watch(`contactPoints.${alertManager}.overrideGrouping`);
|
||||
const overrideTimings = watch(`contactPoints.${alertManager}.overrideTimings`);
|
||||
const requiredFieldsInGroupBy = ['grafana_folder', 'alertname'];
|
||||
@ -56,7 +54,7 @@ export const RoutingSettings = ({ alertManager }: RoutingSettingsProps) => {
|
||||
</InlineField>
|
||||
{!overrideGrouping && (
|
||||
<Text variant="body" color="secondary">
|
||||
Grouping: <strong>{groupBy.join(', ')}</strong>
|
||||
Grouping: <strong>{requiredFieldsInGroupBy.join(', ')}</strong>
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
@ -131,20 +129,13 @@ export const RoutingSettings = ({ alertManager }: RoutingSettingsProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
function useGetDefaultsForRoutingSettings() {
|
||||
const { selectedAlertmanager } = useAlertmanager();
|
||||
const { currentData } = useAlertmanagerConfig(selectedAlertmanager);
|
||||
const config = currentData?.alertmanager_config;
|
||||
return React.useMemo(() => {
|
||||
function getDefaultsForRoutingSettings() {
|
||||
return {
|
||||
groupWaitValue: TIMING_OPTIONS_DEFAULTS.group_wait,
|
||||
groupIntervalValue: TIMING_OPTIONS_DEFAULTS.group_interval,
|
||||
repeatIntervalValue: TIMING_OPTIONS_DEFAULTS.repeat_interval,
|
||||
groupBy: config?.route?.group_by ?? [],
|
||||
};
|
||||
}, [config]);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
switchElement: css({
|
||||
flexFlow: 'row-reverse',
|
||||
|
Loading…
Reference in New Issue
Block a user