Alerting: Support alertingDisableSendAlertsExternal feature toggle (#88384)

This commit is contained in:
Gilles De Mey 2024-05-29 13:54:07 +02:00 committed by GitHub
parent e93169b66c
commit 781f67dfb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 74 additions and 29 deletions

View File

@ -221,10 +221,7 @@ export function getNavSubTitle(navId: string | undefined) {
'Upgrade your existing legacy alerts and notification channels to the new Grafana Alerting'
);
case 'alerting-admin':
return t(
'nav.alerting-admin.subtitle',
'Manage Alertmanager configurations and configure where alert instances generated from Grafana managed alert rules are sent'
);
return t('nav.alerting-admin.subtitle', 'Manage Alertmanager configurations');
case 'alert-list':
return t('nav.alerting-list.subtitle', 'Rules that determine whether an alert will fire');
case 'receivers':

View File

@ -147,6 +147,24 @@ describe('Alertmanager card', () => {
render(cardWithStatus('inconclusive'));
expect(screen.getByText(/Inconclusive/)).toBeInTheDocument();
});
it('should not render the enable / disable buttons or status when disabled', () => {
render(
<AlertmanagerCard
name="Foo"
receiving={true}
status="active"
showStatus={false}
onEditConfiguration={jest.fn()}
/>
);
const enableButton = screen.queryByRole('button', { name: 'Enable' });
expect(enableButton).not.toBeInTheDocument();
// should also not show the status for external alertmanagers
expect(screen.queryByText(/Receiving/)).not.toBeInTheDocument();
});
});
const cardWithStatus = (status: ConnectionStatus) => (

View File

@ -1,7 +1,7 @@
import { capitalize } from 'lodash';
import React from 'react';
import { Card, Badge, Button, Stack, Text, TextLink } from '@grafana/ui';
import { Badge, Button, Card, Stack, Text, TextLink } from '@grafana/ui';
import { ConnectionStatus } from '../../hooks/useExternalAmSelector';
import { ProvisioningBadge } from '../Provisioning';
@ -14,13 +14,14 @@ interface Props {
logo?: string;
provisioned?: boolean;
readOnly?: boolean;
showStatus?: boolean;
implementation?: string;
receiving?: boolean;
status?: ConnectionStatus;
// functions
onEditConfiguration: () => void;
onDisable: () => void;
onEnable: () => void;
onDisable?: () => void;
onEnable?: () => void;
}
export function AlertmanagerCard({
@ -30,6 +31,7 @@ export function AlertmanagerCard({
logo = 'public/app/plugins/datasource/alertmanager/img/logo.svg',
provisioned = false,
readOnly = provisioned,
showStatus = true,
implementation,
receiving = false,
status = 'unknown',
@ -37,6 +39,8 @@ export function AlertmanagerCard({
onEnable,
onDisable,
}: Props) {
const showActions = !provisioned && Boolean(onEnable) && Boolean(onDisable);
return (
<Card data-testid={`alertmanager-card-${name}`}>
<Card.Heading>
@ -60,16 +64,21 @@ export function AlertmanagerCard({
{implementation && capitalize(implementation)}
{url && url}
</Card.Meta>
{!receiving ? (
<Text variant="bodySmall">Not receiving Grafana managed alerts</Text>
) : (
{/* if forwarding is diabled, still show status for the "canonical" Alertmanager */}
{showStatus ? (
<>
{status === 'pending' && <Badge text="Activation in progress" color="orange" />}
{status === 'active' && <Badge text="Receiving Grafana-managed alerts" color="green" />}
{status === 'dropped' && <Badge text="Failed to adopt Alertmanager" color="red" />}
{status === 'inconclusive' && <Badge text="Inconclusive" color="orange" />}
{!receiving ? (
<Text variant="bodySmall">Not receiving Grafana managed alerts</Text>
) : (
<>
{status === 'pending' && <Badge text="Activation in progress" color="orange" />}
{status === 'active' && <Badge text="Receiving Grafana-managed alerts" color="green" />}
{status === 'dropped' && <Badge text="Failed to adopt Alertmanager" color="red" />}
{status === 'inconclusive' && <Badge text="Inconclusive" color="orange" />}
</>
)}
</>
)}
) : null}
</Stack>
</Card.Meta>
@ -80,7 +89,7 @@ export function AlertmanagerCard({
<Button onClick={onEditConfiguration} icon={readOnly ? 'eye' : 'edit'} variant="secondary" fill="outline">
{readOnly ? 'View configuration' : 'Edit configuration'}
</Button>
{provisioned ? null : (
{showActions ? (
<>
{receiving ? (
<Button icon="times" variant="destructive" fill="outline" onClick={onDisable}>
@ -92,7 +101,7 @@ export function AlertmanagerCard({
</Button>
)}
</>
)}
) : null}
</Stack>
</Card.Tags>
</Card>

View File

@ -20,8 +20,13 @@ interface Props {
}
export const ExternalAlertmanagers = ({ onEditConfiguration }: Props) => {
const { externalAlertmanagerDataSourcesWithStatus, configuration, enableAlertmanager, disableAlertmanager } =
useSettings();
const {
externalAlertmanagerDataSourcesWithStatus,
configuration,
enableAlertmanager,
disableAlertmanager,
forwardingDisabled,
} = useSettings();
// determine if the alertmanger is receiving alerts
// this is true if Grafana is configured to send to either "both" or "external" and the Alertmanager datasource _wants_ to receive alerts.
@ -51,6 +56,8 @@ export const ExternalAlertmanagers = ({ onEditConfiguration }: Props) => {
const detailHref = createUrl(DATASOURCES_ROUTES.Edit.replace(/:uid/gi, uid));
const handleEditConfiguration = () => onEditConfiguration(name);
const handleEnable = forwardingDisabled ? undefined : () => enableAlertmanager(uid);
const handleDisable = forwardingDisabled ? undefined : () => disableAlertmanager(uid);
return (
<AlertmanagerCard
@ -60,12 +67,13 @@ export const ExternalAlertmanagers = ({ onEditConfiguration }: Props) => {
url={url}
provisioned={isProvisioned}
readOnly={isReadOnly}
showStatus={!forwardingDisabled}
implementation={jsonData.implementation ?? 'Prometheus'}
receiving={isReceiving}
status={status}
onEditConfiguration={handleEditConfiguration}
onDisable={() => disableAlertmanager(uid)}
onEnable={() => enableAlertmanager(uid)}
onDisable={handleDisable}
onEnable={handleEnable}
/>
);
})}

View File

@ -11,22 +11,27 @@ interface Props {
onEditConfiguration: (dataSourceName: string) => void;
}
const BUILTIN_ALERTMANAGER_NAME = 'Grafana built-in';
export default function InternalAlertmanager({ onEditConfiguration }: Props) {
const { configuration, enableAlertmanager, disableAlertmanager } = useSettings();
const { configuration, enableAlertmanager, disableAlertmanager, forwardingDisabled } = useSettings();
const isReceiving = isInternalAlertmanagerInterestedInAlerts(configuration);
const status: ConnectionStatus = isReceiving ? 'active' : 'uninterested';
const handleEditConfiguration = () => onEditConfiguration(GRAFANA_RULES_SOURCE_NAME);
const handleEnable = forwardingDisabled ? undefined : () => enableAlertmanager(GRAFANA_RULES_SOURCE_NAME);
const handleDisable = forwardingDisabled ? undefined : () => disableAlertmanager(GRAFANA_RULES_SOURCE_NAME);
return (
<AlertmanagerCard
name="Grafana built-in"
name={BUILTIN_ALERTMANAGER_NAME}
logo="public/img/grafana_icon.svg"
status={status}
receiving={isReceiving}
onEditConfiguration={handleEditConfiguration}
onEnable={() => enableAlertmanager(GRAFANA_RULES_SOURCE_NAME)}
onDisable={() => disableAlertmanager(GRAFANA_RULES_SOURCE_NAME)}
onEnable={handleEnable}
onDisable={handleDisable}
/>
);
}

View File

@ -1,8 +1,8 @@
import { union, without, debounce } from 'lodash';
import { debounce, union, without } from 'lodash';
import React, { PropsWithChildren, useEffect, useRef } from 'react';
import { AppEvents } from '@grafana/data';
import { getAppEvents } from '@grafana/runtime';
import { config, getAppEvents } from '@grafana/runtime';
import { AlertmanagerChoice, GrafanaAlertingConfiguration } from 'app/plugins/datasource/alertmanager/types';
import { dispatch } from 'app/store/store';
@ -34,6 +34,9 @@ interface Context {
// for updating or resetting the configuration for an Alertmanager
updateAlertmanagerSettings: (name: string, oldConfig: string, newConfig: string) => void;
resetAlertmanagerSettings: (name: string) => void;
// this feature toggle is for disabling the "send to external Alertmanagers" feature
forwardingDisabled: boolean;
}
const SettingsContext = React.createContext<Context | undefined>(undefined);
@ -44,6 +47,8 @@ export const SettingsProvider = (props: PropsWithChildren) => {
// this will be used to infer the correct "delivery mode" and update the correct list of datasources with "wantsAlertsReceived"
let interestedAlertmanagers: string[] = [];
const forwardingDisabled = config.featureToggles.alertingDisableSendAlertsExternal === true;
const { currentData: configuration, isLoading: isLoadingConfiguration } =
alertmanagerApi.endpoints.getGrafanaAlertingConfiguration.useQuery();
@ -115,9 +120,12 @@ export const SettingsProvider = (props: PropsWithChildren) => {
const value: Context = {
configuration,
forwardingDisabled,
externalAlertmanagerDataSourcesWithStatus: externalAlertmanagersWithStatus,
enableAlertmanager,
disableAlertmanager,
isLoading: isLoadingConfiguration,
isUpdating: updateConfigurationState.isLoading || enableOrDisableHandlingGrafanaManagedAlertsState.isLoading,

View File

@ -894,7 +894,7 @@
"title": "Alerting"
},
"alerting-admin": {
"subtitle": "Manage Alertmanager configurations and configure where alert instances generated from Grafana managed alert rules are sent",
"subtitle": "Manage Alertmanager configurations",
"title": "Settings"
},
"alerting-am-routes": {

View File

@ -894,7 +894,7 @@
"title": "Åľęřŧįʼnģ"
},
"alerting-admin": {
"subtitle": "Mäʼnäģę Åľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼnş äʼnđ čőʼnƒįģūřę ŵĥęřę äľęřŧ įʼnşŧäʼnčęş ģęʼnęřäŧęđ ƒřőm Ğřäƒäʼnä mäʼnäģęđ äľęřŧ řūľęş äřę şęʼnŧ",
"subtitle": "Mäʼnäģę Åľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼnş",
"title": "Ŝęŧŧįʼnģş"
},
"alerting-am-routes": {