From 781f67dfb621b5a455e3df3b4d32e150c53e21ff Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Wed, 29 May 2024 13:54:07 +0200 Subject: [PATCH] Alerting: Support `alertingDisableSendAlertsExternal` feature toggle (#88384) --- .../app/core/utils/navBarItem-translations.ts | 5 +-- .../settings/AlertmanagerCard.test.tsx | 18 ++++++++++ .../components/settings/AlertmanagerCard.tsx | 35 ++++++++++++------- .../settings/ExternalAlertmanagers.tsx | 16 ++++++--- .../settings/InternalAlertmanager.tsx | 13 ++++--- .../components/settings/SettingsContext.tsx | 12 +++++-- public/locales/en-US/grafana.json | 2 +- public/locales/pseudo-LOCALE/grafana.json | 2 +- 8 files changed, 74 insertions(+), 29 deletions(-) diff --git a/public/app/core/utils/navBarItem-translations.ts b/public/app/core/utils/navBarItem-translations.ts index 9a7dc478d30..5c53129ebfa 100644 --- a/public/app/core/utils/navBarItem-translations.ts +++ b/public/app/core/utils/navBarItem-translations.ts @@ -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': diff --git a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.test.tsx b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.test.tsx index 02a397fd35a..4aecd7844cd 100644 --- a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.test.tsx +++ b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.test.tsx @@ -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( + + ); + + 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) => ( diff --git a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx index 76ef9d9606a..926e2588fae 100644 --- a/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx +++ b/public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx @@ -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 ( @@ -60,16 +64,21 @@ export function AlertmanagerCard({ {implementation && capitalize(implementation)} {url && url} - {!receiving ? ( - Not receiving Grafana managed alerts - ) : ( + {/* if forwarding is diabled, still show status for the "canonical" Alertmanager */} + {showStatus ? ( <> - {status === 'pending' && } - {status === 'active' && } - {status === 'dropped' && } - {status === 'inconclusive' && } + {!receiving ? ( + Not receiving Grafana managed alerts + ) : ( + <> + {status === 'pending' && } + {status === 'active' && } + {status === 'dropped' && } + {status === 'inconclusive' && } + + )} - )} + ) : null} @@ -80,7 +89,7 @@ export function AlertmanagerCard({ - {provisioned ? null : ( + {showActions ? ( <> {receiving ? ( )} - )} + ) : null} diff --git a/public/app/features/alerting/unified/components/settings/ExternalAlertmanagers.tsx b/public/app/features/alerting/unified/components/settings/ExternalAlertmanagers.tsx index f9f0541ca43..f39dff4e469 100644 --- a/public/app/features/alerting/unified/components/settings/ExternalAlertmanagers.tsx +++ b/public/app/features/alerting/unified/components/settings/ExternalAlertmanagers.tsx @@ -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 ( { 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} /> ); })} diff --git a/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx b/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx index 9c0275df305..cea3396cadb 100644 --- a/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx +++ b/public/app/features/alerting/unified/components/settings/InternalAlertmanager.tsx @@ -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 ( enableAlertmanager(GRAFANA_RULES_SOURCE_NAME)} - onDisable={() => disableAlertmanager(GRAFANA_RULES_SOURCE_NAME)} + onEnable={handleEnable} + onDisable={handleDisable} /> ); } diff --git a/public/app/features/alerting/unified/components/settings/SettingsContext.tsx b/public/app/features/alerting/unified/components/settings/SettingsContext.tsx index e69a6c71316..20ff037adf8 100644 --- a/public/app/features/alerting/unified/components/settings/SettingsContext.tsx +++ b/public/app/features/alerting/unified/components/settings/SettingsContext.tsx @@ -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(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, diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index ed87ba04fed..b0e163a7deb 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -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": { diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 492ed9100a4..33b61703f2e 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -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": {