Alerting: Stop allowing manual editing/restore of internal AM config via settings (#103884)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'test/test-utils';
|
||||
import { byRole, byTestId, byText } from 'testing-library-selector';
|
||||
|
||||
@@ -26,8 +25,9 @@ const ui = {
|
||||
statusReceiving: byText(/receiving grafana-managed alerts/i),
|
||||
statusNotReceiving: byText(/not receiving/i),
|
||||
|
||||
configurationDrawer: byRole('dialog', { name: 'Drawer title Internal Grafana Alertmanager' }),
|
||||
configurationDrawer: byRole('dialog', { name: 'Drawer title Grafana built-in Alertmanager' }),
|
||||
editConfigurationButton: byRole('button', { name: /edit configuration/i }),
|
||||
viewConfigurationButton: byRole('button', { name: /view configuration/i }),
|
||||
saveConfigurationButton: byRole('button', { name: /save/i }),
|
||||
|
||||
enableButton: byRole('button', { name: 'Enable' }),
|
||||
@@ -54,7 +54,7 @@ describe('Alerting settings', () => {
|
||||
|
||||
expect(ui.statusReceiving.get(ui.builtInAlertmanagerCard.get())).toBeInTheDocument();
|
||||
|
||||
// check external altermanagers
|
||||
// check external alertmanagers
|
||||
DataSourcesResponse.forEach((ds) => {
|
||||
// get the card for datasource
|
||||
const card = ui.alertmanagerCard(ds.name).get();
|
||||
@@ -74,45 +74,36 @@ describe('Alerting settings', () => {
|
||||
});
|
||||
|
||||
it('should be able to view configuration', async () => {
|
||||
render(<SettingsPage />);
|
||||
const { user } = render(<SettingsPage />);
|
||||
|
||||
// wait for loading to be done
|
||||
await waitFor(() => expect(ui.builtInAlertmanagerSection.get()).toBeInTheDocument());
|
||||
|
||||
// open configuration drawer
|
||||
const internalAMCard = ui.builtInAlertmanagerCard.get();
|
||||
const editInternal = ui.editConfigurationButton.get(internalAMCard);
|
||||
await userEvent.click(editInternal);
|
||||
await user.click(ui.viewConfigurationButton.get(internalAMCard));
|
||||
expect(await ui.configurationDrawer.find()).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(ui.configurationDrawer.get()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await userEvent.click(ui.saveConfigurationButton.get());
|
||||
expect(ui.saveConfigurationButton.get()).toBeDisabled();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(ui.saveConfigurationButton.get()).toBeEnabled();
|
||||
});
|
||||
expect(ui.saveConfigurationButton.query()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should be able to view versions', async () => {
|
||||
render(<SettingsPage />);
|
||||
const { user } = render(<SettingsPage />);
|
||||
|
||||
// wait for loading to be done
|
||||
await waitFor(() => expect(ui.builtInAlertmanagerSection.get()).toBeInTheDocument());
|
||||
expect(await ui.builtInAlertmanagerSection.find()).toBeInTheDocument();
|
||||
|
||||
// open configuration drawer
|
||||
const internalAMCard = ui.builtInAlertmanagerCard.get();
|
||||
const editInternal = ui.editConfigurationButton.get(internalAMCard);
|
||||
await userEvent.click(editInternal);
|
||||
await user.click(ui.viewConfigurationButton.get(internalAMCard));
|
||||
expect(await ui.configurationDrawer.find()).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(ui.configurationDrawer.get()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// click versions tab
|
||||
await userEvent.click(ui.versionsTab.get());
|
||||
await user.click(ui.versionsTab.get());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/last applied/i)).toBeInTheDocument();
|
||||
|
||||
@@ -121,7 +121,11 @@ export function AlertmanagerCard({
|
||||
<Stack direction="row" gap={1}>
|
||||
{/* ⚠️ provisioned Data sources cannot have their "enable" / "disable" actions but we should still allow editing of the configuration */}
|
||||
<Button onClick={onEditConfiguration} icon={readOnly ? 'eye' : 'edit'} variant="secondary" fill="outline">
|
||||
{readOnly ? 'View configuration' : 'Edit configuration'}
|
||||
{readOnly ? (
|
||||
<Trans i18nKey="alerting.alertmanager-card.view-configuration">View configuration</Trans>
|
||||
) : (
|
||||
<Trans i18nKey="alerting.alertmanager-card.edit-configuration">Edit configuration</Trans>
|
||||
)}
|
||||
</Button>
|
||||
{showActions ? (
|
||||
<>
|
||||
|
||||
+18
-11
@@ -47,20 +47,11 @@ describe('Alerting Settings', () => {
|
||||
grantUserPermissions([AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingInstanceRead]);
|
||||
});
|
||||
|
||||
it('should be able to reset alertmanager config', async () => {
|
||||
it('should not be able to reset alertmanager config', async () => {
|
||||
const onReset = jest.fn();
|
||||
renderConfiguration('grafana', { onReset });
|
||||
|
||||
await userEvent.click(await ui.resetButton.find());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(ui.resetConfirmButton.query()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await userEvent.click(ui.resetConfirmButton.get());
|
||||
|
||||
await waitFor(() => expect(onReset).toHaveBeenCalled());
|
||||
expect(onReset).toHaveBeenLastCalledWith('grafana');
|
||||
expect(ui.resetButton.query()).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should be able to cancel', async () => {
|
||||
@@ -100,4 +91,20 @@ describe('vanilla Alertmanager', () => {
|
||||
expect(ui.saveButton.get()).toBeInTheDocument();
|
||||
expect(ui.resetButton.get()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should be able to reset non-Grafana alertmanager config', async () => {
|
||||
const onReset = jest.fn();
|
||||
renderConfiguration(PROVISIONED_MIMIR_ALERTMANAGER_UID, { onReset });
|
||||
|
||||
expect(ui.cancelButton.get()).toBeInTheDocument();
|
||||
expect(ui.saveButton.get()).toBeInTheDocument();
|
||||
expect(ui.resetButton.get()).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(ui.resetButton.get());
|
||||
|
||||
await userEvent.click(ui.resetConfirmButton.get());
|
||||
|
||||
await waitFor(() => expect(onReset).toHaveBeenCalled());
|
||||
expect(onReset).toHaveBeenLastCalledWith(PROVISIONED_MIMIR_ALERTMANAGER_UID);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,12 +28,12 @@ export default function AlertmanagerConfig({ alertmanagerName, onDismiss, onSave
|
||||
const { loading: isDeleting, error: deletingError } = useUnifiedAlertingSelector((state) => state.deleteAMConfig);
|
||||
const { loading: isSaving, error: savingError } = useUnifiedAlertingSelector((state) => state.saveAMConfig);
|
||||
const [showResetConfirmation, setShowResetConfirmation] = useState(false);
|
||||
const isGrafanaManagedAlertmanager = alertmanagerName === GRAFANA_RULES_SOURCE_NAME;
|
||||
|
||||
// ⚠️ provisioned data sources should not prevent the configuration from being edited
|
||||
const immutableDataSource = alertmanagerName ? isVanillaPrometheusAlertManagerDataSource(alertmanagerName) : false;
|
||||
const readOnly = immutableDataSource;
|
||||
const readOnly = immutableDataSource || isGrafanaManagedAlertmanager;
|
||||
|
||||
const isGrafanaManagedAlertmanager = alertmanagerName === GRAFANA_RULES_SOURCE_NAME;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const {
|
||||
@@ -128,12 +128,28 @@ export default function AlertmanagerConfig({ alertmanagerName, onDismiss, onSave
|
||||
);
|
||||
}
|
||||
|
||||
const confirmationText = isGrafanaManagedAlertmanager
|
||||
? `Are you sure you want to reset configuration for the Grafana Alertmanager? Contact points and notification policies will be reset to their defaults.`
|
||||
: `Are you sure you want to reset configuration for "${alertmanagerName}"? Contact points and notification policies will be reset to their defaults.`;
|
||||
const confirmationText = t(
|
||||
'alerting.alertmanager-config.reset-confirmation',
|
||||
'Are you sure you want to reset configuration for "{{alertmanagerName}}"? Contact points and notification policies will be reset to their defaults.',
|
||||
{ alertmanagerName }
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{isGrafanaManagedAlertmanager && (
|
||||
<Alert
|
||||
severity="info"
|
||||
title={t(
|
||||
'alerting.alertmanager-config.gma-manual-configuration-is-not-supported',
|
||||
'Manual configuration changes not supported'
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="alerting.alertmanager-config.gma-manual-configuration-description">
|
||||
The internal Grafana Alertmanager configuration cannot be manually changed. To change this configuration,
|
||||
edit the individual resources through the UI.
|
||||
</Trans>
|
||||
</Alert>
|
||||
)}
|
||||
{/* form error state */}
|
||||
{errors.configJSON && (
|
||||
<Alert
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
import { Drawer, Tab, TabsBar } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
||||
import { GRAFANA_RULES_SOURCE_NAME, isVanillaPrometheusAlertManagerDataSource } from '../../utils/datasource';
|
||||
|
||||
import AlertmanagerConfig from './AlertmanagerConfig';
|
||||
import { useSettings } from './SettingsContext';
|
||||
@@ -17,6 +17,10 @@ export function useEditConfigurationDrawer() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { updateAlertmanagerSettings, resetAlertmanagerSettings } = useSettings();
|
||||
|
||||
const isGrafanaManagedAlertmanager = dataSourceName === GRAFANA_RULES_SOURCE_NAME;
|
||||
const immutableDataSource = dataSourceName ? isVanillaPrometheusAlertManagerDataSource(dataSourceName) : false;
|
||||
const readOnly = immutableDataSource || isGrafanaManagedAlertmanager;
|
||||
|
||||
const showConfiguration = (dataSourceName: string) => {
|
||||
setDataSourceName(dataSourceName);
|
||||
setOpen(true);
|
||||
@@ -33,14 +37,29 @@ export function useEditConfigurationDrawer() {
|
||||
}
|
||||
|
||||
const isGrafanaAlertmanager = dataSourceName === GRAFANA_RULES_SOURCE_NAME;
|
||||
const title = isGrafanaAlertmanager ? 'Internal Grafana Alertmanager' : dataSourceName;
|
||||
const title = isGrafanaAlertmanager
|
||||
? t(
|
||||
'alerting.use-edit-configuration-drawer.drawer.internal-grafana-alertmanager-title',
|
||||
'Grafana built-in Alertmanager'
|
||||
)
|
||||
: dataSourceName;
|
||||
|
||||
const subtitle = readOnly
|
||||
? t(
|
||||
'alerting.use-edit-configuration-drawer.drawer.title-view-the-alertmanager-configuration',
|
||||
'View Alertmanager configuration'
|
||||
)
|
||||
: t(
|
||||
'alerting.use-edit-configuration-drawer.drawer.title-edit-the-alertmanager-configuration',
|
||||
'Edit Alertmanager configuration'
|
||||
);
|
||||
|
||||
// @todo check copy
|
||||
return (
|
||||
<Drawer
|
||||
onClose={handleDismiss}
|
||||
title={title}
|
||||
subtitle="Edit the Alertmanager configuration"
|
||||
subtitle={subtitle}
|
||||
tabs={
|
||||
<TabsBar>
|
||||
<Tab
|
||||
@@ -74,7 +93,7 @@ export function useEditConfigurationDrawer() {
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
}, [open, dataSourceName, handleDismiss, activeTab, updateAlertmanagerSettings, resetAlertmanagerSettings]);
|
||||
}, [open, dataSourceName, readOnly, handleDismiss, activeTab, updateAlertmanagerSettings, resetAlertmanagerSettings]);
|
||||
|
||||
return [drawer, showConfiguration, handleDismiss] as const;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export default function InternalAlertmanager({ onEditConfiguration }: Props) {
|
||||
onEditConfiguration={handleEditConfiguration}
|
||||
onEnable={handleEnable}
|
||||
onDisable={handleDisable}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,11 +94,15 @@ const AlertmanagerConfigurationVersionManager = ({
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return 'Loading...';
|
||||
return <Trans i18nKey="alerting.alertmanager-configuration-version-manager.loading">Loading...</Trans>;
|
||||
}
|
||||
|
||||
if (!historicalConfigs.length) {
|
||||
return 'No previous configurations';
|
||||
return (
|
||||
<Trans i18nKey="alerting.alertmanager-configuration-version-manager.no-previous-configurations">
|
||||
No previous configurations
|
||||
</Trans>
|
||||
);
|
||||
}
|
||||
|
||||
// with this function we'll compute the diff with the previous version; that way the user can get some idea of how many lines where changed in each update that was applied
|
||||
|
||||
@@ -413,16 +413,21 @@
|
||||
},
|
||||
"alertmanager-card": {
|
||||
"disable": "Disable",
|
||||
"edit-configuration": "Edit configuration",
|
||||
"enable": "Enable",
|
||||
"not-receiving-grafana-managed-alerts": "Not receiving Grafana managed alerts",
|
||||
"text-activation-in-progress": "Activation in progress",
|
||||
"text-failed-to-adopt-alertmanager": "Failed to adopt Alertmanager",
|
||||
"text-inconclusive": "Inconclusive",
|
||||
"text-receiving-grafanamanaged-alerts": "Receiving Grafana-managed alerts",
|
||||
"title-alerting-settings": "Alerting settings"
|
||||
"title-alerting-settings": "Alerting settings",
|
||||
"view-configuration": "View configuration"
|
||||
},
|
||||
"alertmanager-config": {
|
||||
"gma-manual-configuration-description": "The internal Grafana Alertmanager configuration cannot be manually changed. To change this configuration, edit the individual resources through the UI.",
|
||||
"gma-manual-configuration-is-not-supported": "Manual configuration changes not supported",
|
||||
"reset": "Reset",
|
||||
"reset-confirmation": "Are you sure you want to reset configuration for \"{{alertmanagerName}}\"? Contact points and notification policies will be reset to their defaults.",
|
||||
"resetting-configuration-might-while": "Resetting configuration, this might take a while.",
|
||||
"title-failed-to-load-alertmanager-configuration": "Failed to load Alertmanager configuration",
|
||||
"title-oops-something-went-wrong": "Oops, something went wrong",
|
||||
@@ -435,6 +440,8 @@
|
||||
"restore": "Restore",
|
||||
"text-latest": "Latest"
|
||||
},
|
||||
"loading": "Loading...",
|
||||
"no-previous-configurations": "No previous configurations",
|
||||
"this-might-take-a-while": "This might take a while...",
|
||||
"title-failed-to-load-configuration-history": "Failed to load configuration history",
|
||||
"title-restore-version": "Restore version",
|
||||
@@ -2075,8 +2082,11 @@
|
||||
},
|
||||
"use-edit-configuration-drawer": {
|
||||
"drawer": {
|
||||
"internal-grafana-alertmanager-title": "Grafana built-in Alertmanager",
|
||||
"label-json-model": "JSON Model",
|
||||
"label-versions": "Versions"
|
||||
"label-versions": "Versions",
|
||||
"title-edit-the-alertmanager-configuration": "Edit Alertmanager configuration",
|
||||
"title-view-the-alertmanager-configuration": "View Alertmanager configuration"
|
||||
}
|
||||
},
|
||||
"use-edit-policy-modal": {
|
||||
|
||||
Reference in New Issue
Block a user