Alerting: Skip rules that are managed by plugins when importing datasource-managed rules (#103573)

* skip rules that are managed by plugins when importing from data source rules to GMA

* update translations

* update text

* update translations

* handle no rules to import

* filter out rules instead of groups

* Reuse skipped rules alert

* update translations keys

* fix filter

---------

Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com>
This commit is contained in:
Sonia Aguilar
2025-04-08 15:07:03 +02:00
committed by GitHub
co-authored by Tom Ratcliffe
parent 04ac3960de
commit 1d5f51e383
2 changed files with 70 additions and 6 deletions
@@ -4,7 +4,7 @@ import { ComponentProps } from 'react';
import { useFormContext } from 'react-hook-form';
import { locationService } from '@grafana/runtime';
import { Alert, CodeEditor, ConfirmModal, Stack, Text, useStyles2 } from '@grafana/ui';
import { Alert, CodeEditor, ConfirmModal, Modal, Stack, Text, useStyles2 } from '@grafana/ui';
import { useAppNotification } from 'app/core/copy/appNotification';
import { Trans, t } from 'app/core/internationalization';
import { stringifyErrorLike } from 'app/features/alerting/unified/utils/misc';
@@ -12,6 +12,7 @@ import { RulerRulesConfigDTO } from 'app/types/unified-alerting-dto';
import { trackImportToGMAError, trackImportToGMASuccess } from '../../Analytics';
import { convertToGMAApi } from '../../api/convertToGMAApi';
import { GRAFANA_ORIGIN_LABEL } from '../../utils/labels';
import { createListFilterLink } from '../../utils/navigation';
import { useGetRulerRules } from '../rule-editor/useAlertRuleSuggestions';
@@ -21,8 +22,22 @@ type ModalProps = Pick<ComponentProps<typeof ConfirmModal>, 'isOpen' | 'onDismis
isOpen: boolean;
};
const AlertSomeRulesSkipped = () => (
<Alert
title={t('alerting.import-to-gma.confirm-modal.plugin-rules-warning.title', 'Some rules are excluded from import')}
severity="info"
>
<Text variant="body">
<Trans i18nKey="alerting.import-to-gma.confirm-modal.plugin-rules-warning.text">
We have detected that some rules are managed by plugins. These rules will not be imported.
</Trans>
</Text>
</Alert>
);
export const ConfirmConversionModal = ({ isOpen, onDismiss }: ModalProps) => {
const { watch } = useFormContext<ImportFormValues>();
const styles = useStyles2(getStyles);
const [
targetFolder,
@@ -46,7 +61,11 @@ export const ConfirmConversionModal = ({ isOpen, onDismiss }: ModalProps) => {
const { rulerRules } = useGetRulerRules(selectedDatasourceName || undefined);
const [convert] = convertToGMAApi.useConvertToGMAMutation();
const notifyApp = useAppNotification();
const rulerRulesToPayload = filterRulerRulesConfig(rulerRules, namespace, ruleGroup);
const { filteredConfig: rulerRulesToPayload, someRulesAreSkipped } = filterRulerRulesConfig(
rulerRules,
namespace,
ruleGroup
);
async function onConvertConfirm() {
try {
@@ -79,6 +98,28 @@ export const ConfirmConversionModal = ({ isOpen, onDismiss }: ModalProps) => {
}
}
const noRulesToImport = isEmpty(rulerRulesToPayload);
if (noRulesToImport) {
return (
<Modal
isOpen={isOpen}
title={t('alerting.import-to-gma.confirm-modal.no-rules-title', 'No rules to import')}
onDismiss={onDismiss}
onClickBackdrop={onDismiss}
>
<Stack direction="column" gap={2}>
{someRulesAreSkipped && <AlertSomeRulesSkipped />}
<Text>
{t(
'alerting.import-to-gma.confirm-modal.no-rules-body',
'There are no rules to import. Please select a different namespace or rule group.'
)}
</Text>
</Stack>
</Modal>
);
}
// translations for texts in the modal
const title = t('alerting.import-to-gma.confirm-modal.title', 'Confirm import');
const confirmText = t('alerting.import-to-gma.confirm-modal.confirm', 'Yes, import');
@@ -88,6 +129,7 @@ export const ConfirmConversionModal = ({ isOpen, onDismiss }: ModalProps) => {
title={title}
confirmText={confirmText}
confirmButtonVariant="primary"
modalClass={styles.modal}
body={
<Stack direction="column" gap={2}>
<Alert title={t('alerting.to-gma.confirm-modal.title-warning', 'Warning')} severity="warning">
@@ -98,6 +140,7 @@ export const ConfirmConversionModal = ({ isOpen, onDismiss }: ModalProps) => {
</Trans>
</Text>
</Alert>
{someRulesAreSkipped && <AlertSomeRulesSkipped />}
<Text variant="h6">
<Trans i18nKey="alerting.to-gma.confirm-modal.summary">
These are the list of rules that will be imported:
@@ -116,8 +159,9 @@ function filterRulerRulesConfig(
rulerRulesConfig: RulerRulesConfigDTO,
namespace?: string,
groupName?: string
): RulerRulesConfigDTO {
): { filteredConfig: RulerRulesConfigDTO; someRulesAreSkipped: boolean } {
const filteredConfig: RulerRulesConfigDTO = {};
let someRulesAreSkipped = false;
Object.entries(rulerRulesConfig).forEach(([ns, groups]) => {
if (namespace && ns !== namespace) {
@@ -128,7 +172,21 @@ function filterRulerRulesConfig(
if (groupName && group.name !== groupName) {
return false;
}
return true;
// Filter out rules that have the GRAFANA_ORIGIN_LABEL
const filteredRules = group.rules.filter((rule) => {
const hasGrafanaOriginLabel = rule.labels?.[GRAFANA_ORIGIN_LABEL];
if (hasGrafanaOriginLabel) {
someRulesAreSkipped = true;
return false;
}
return true;
});
return {
...group,
rules: filteredRules,
};
});
if (filteredGroups.length > 0) {
@@ -136,7 +194,7 @@ function filterRulerRulesConfig(
}
});
return filteredConfig;
return { filteredConfig, someRulesAreSkipped };
}
function RulesPreview({ rules }: { rules: RulerRulesConfigDTO }) {
@@ -166,6 +224,6 @@ const getStyles = () => ({
flex: '1 1 100%',
}),
modal: css({
width: '700px',
width: '800px',
}),
});
+6
View File
@@ -1048,6 +1048,12 @@
"alert-rules": "Alert rules",
"confirm-modal": {
"confirm": "Yes, import",
"no-rules-body": "There are no rules to import. Please select a different namespace or rule group.",
"no-rules-title": "No rules to import",
"plugin-rules-warning": {
"text": "We have detected that some rules are managed by plugins. These rules will not be imported.",
"title": "Some rules are excluded from import"
},
"title": "Confirm import"
},
"datasource": {