mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Show legacy provisioned alert rules warning (#81902)
This commit is contained in:
parent
6ce0efeb41
commit
248031d007
@ -2,7 +2,8 @@ import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { LoadingPlaceholder } from '@grafana/ui';
|
||||
import { useDispatch } from 'app/types';
|
||||
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
|
||||
import { DashboardRoutes, useDispatch } from 'app/types';
|
||||
|
||||
import { RulesTable } from '../components/rules/RulesTable';
|
||||
import { useCombinedRuleNamespaces } from '../hooks/useCombinedRuleNamespaces';
|
||||
@ -10,14 +11,26 @@ import { fetchPromAndRulerRulesAction } from '../state/actions';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
|
||||
import LegacyAlertsDeprecationNotice from './LegacyAlertsDeprecationNotice';
|
||||
|
||||
interface Props {
|
||||
dashboardUid: string;
|
||||
}
|
||||
|
||||
export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
||||
const dispatch = useDispatch();
|
||||
const dashboardStateManager = getDashboardScenePageStateManager();
|
||||
|
||||
const { loading } = useAsync(async () => {
|
||||
const { value: dashboard, loading: loadingDashboardState } = useAsync(() => {
|
||||
return dashboardStateManager
|
||||
.fetchDashboard({
|
||||
uid: dashboardUid,
|
||||
route: DashboardRoutes.Normal,
|
||||
})
|
||||
.then((data) => (data ? data.dashboard : undefined));
|
||||
}, [dashboardStateManager]);
|
||||
|
||||
const { loading: loadingAlertRules } = useAsync(async () => {
|
||||
await dispatch(fetchPromAndRulerRulesAction({ rulesSourceName: GRAFANA_RULES_SOURCE_NAME }));
|
||||
}, [dispatch]);
|
||||
|
||||
@ -27,12 +40,17 @@ export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
||||
.flatMap((g) => g.rules)
|
||||
.filter((rule) => rule.annotations[Annotation.dashboardUID] === dashboardUid);
|
||||
|
||||
const loading = loadingDashboardState || loadingAlertRules;
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<LoadingPlaceholder text="Loading alert rules" />
|
||||
) : (
|
||||
<RulesTable rules={rules} showNextEvaluationColumn={false} showGroupColumn={false} />
|
||||
<>
|
||||
<LegacyAlertsDeprecationNotice dashboard={dashboard} />
|
||||
<RulesTable rules={rules} showNextEvaluationColumn={false} showGroupColumn={false} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -0,0 +1,92 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
import React from 'react';
|
||||
import { useToggle } from 'react-use';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Dashboard, Panel, RowPanel } from '@grafana/schema';
|
||||
import { Alert, Collapse, Column, InteractiveTable, TextLink } from '@grafana/ui';
|
||||
|
||||
import { makePanelLink } from '../utils/misc';
|
||||
|
||||
interface DeprecationNoticeProps {
|
||||
dashboard: Dashboard;
|
||||
}
|
||||
|
||||
const usingLegacyAlerting = !config.unifiedAlertingEnabled;
|
||||
|
||||
export default function LegacyAlertsDeprecationNotice({ dashboard }: DeprecationNoticeProps) {
|
||||
// if the user is still using legacy alerting we don't need to show any notice at all – they will probably keep using legacy alerting and do not intend to upgrade.
|
||||
if (usingLegacyAlerting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const panelsWithLegacyAlerts = getLegacyAlertPanelsFromDashboard(dashboard);
|
||||
|
||||
// don't show anything when the user has no legacy alerts defined
|
||||
const hasLegacyAlerts = !isEmpty(panelsWithLegacyAlerts);
|
||||
if (!hasLegacyAlerts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dashboard.uid ? <LegacyAlertsWarning dashboardUid={dashboard.uid} panels={panelsWithLegacyAlerts} /> : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function uses two different ways to detect legacy alerts based on what dashboard system is being used.
|
||||
*
|
||||
* 1. if using the older (non-scenes) dashboard system we can simply check for "alert" in the panel definition.
|
||||
* 2. for dashboard scenes the alerts are no longer added to the model but we can check for "alertThreshold" in the panel options object
|
||||
*/
|
||||
function getLegacyAlertPanelsFromDashboard(dashboard: Dashboard): Panel[] {
|
||||
const panelsWithLegacyAlerts = dashboard.panels?.filter((panel) => {
|
||||
const hasAlertDefinition = 'alert' in panel;
|
||||
const hasAlertThreshold = 'options' in panel && panel.options ? 'alertThreshold' in panel.options : false;
|
||||
return hasAlertDefinition || hasAlertThreshold;
|
||||
});
|
||||
|
||||
return panelsWithLegacyAlerts ?? [];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
dashboardUid: string;
|
||||
panels: Panel[];
|
||||
}
|
||||
|
||||
function LegacyAlertsWarning({ dashboardUid, panels }: Props) {
|
||||
const [isOpen, toggleCollapsible] = useToggle(false);
|
||||
|
||||
const columns: Array<Column<Panel | RowPanel>> = [
|
||||
{ id: 'id', header: 'ID' },
|
||||
{
|
||||
id: 'title',
|
||||
header: 'Title',
|
||||
cell: (cell) => (
|
||||
<TextLink
|
||||
external
|
||||
href={makePanelLink(dashboardUid, String(cell.row.id), { editPanel: cell.row.id, tab: 'alert' })}
|
||||
>
|
||||
{cell.value}
|
||||
</TextLink>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Alert severity="warning" title="Legacy alert rules are deprecated">
|
||||
<p>
|
||||
You have legacy alert rules in this dashboard that were deprecated in Grafana 11 and are no longer supported.
|
||||
</p>
|
||||
<p>
|
||||
Refer to{' '}
|
||||
<TextLink href="https://grafana.com/docs/grafana/latest/alerting/set-up/migrating-alerts/" external>
|
||||
our documentation
|
||||
</TextLink>{' '}
|
||||
on how to migrate legacy alert rules and how to import and export using Grafana Alerting.
|
||||
</p>
|
||||
|
||||
<Collapse label={'List of panels using legacy alerts'} collapsible isOpen={isOpen} onToggle={toggleCollapsible}>
|
||||
<InteractiveTable columns={columns} data={panels} getRowId={(panel) => String(panel.id)} pageSize={5} />
|
||||
</Collapse>
|
||||
</Alert>
|
||||
);
|
||||
}
|
@ -157,8 +157,15 @@ export function makeDashboardLink(dashboardUID: string): string {
|
||||
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`);
|
||||
}
|
||||
|
||||
export function makePanelLink(dashboardUID: string, panelId: string): string {
|
||||
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`, { viewPanel: panelId });
|
||||
type PanelLinkParams = {
|
||||
viewPanel?: string;
|
||||
editPanel?: string;
|
||||
tab?: 'alert' | 'transform' | 'query';
|
||||
};
|
||||
|
||||
export function makePanelLink(dashboardUID: string, panelId: string, queryParams: PanelLinkParams = {}): string {
|
||||
const panelParams = new URLSearchParams(queryParams);
|
||||
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`, panelParams);
|
||||
}
|
||||
|
||||
// keep retrying fn if it's error passes shouldRetry(error) and timeout has not elapsed yet
|
||||
|
@ -3,9 +3,11 @@ import React from 'react';
|
||||
|
||||
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
||||
import { SceneComponentProps, SceneObjectBase, sceneUtils } from '@grafana/scenes';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import LegacyAlertsDeprecationNotice from 'app/features/alerting/unified/integration/LegacyAlertsDeprecationNotice';
|
||||
import { getPrettyJSON } from 'app/features/inspector/utils/utils';
|
||||
import { DashboardDTO } from 'app/types';
|
||||
|
||||
@ -38,9 +40,13 @@ export class JsonModelEditView extends SceneObjectBase<JsonModelEditViewState> i
|
||||
return getDashboardSceneFor(this);
|
||||
}
|
||||
|
||||
public getJsonText(): string {
|
||||
public getSaveModel(): Dashboard {
|
||||
const dashboard = this.getDashboard();
|
||||
const jsonData = transformSceneToSaveModel(dashboard);
|
||||
return transformSceneToSaveModel(dashboard);
|
||||
}
|
||||
|
||||
public getJsonText(): string {
|
||||
const jsonData = this.getSaveModel();
|
||||
return getPrettyJSON(jsonData);
|
||||
}
|
||||
|
||||
@ -67,6 +73,7 @@ export class JsonModelEditView extends SceneObjectBase<JsonModelEditViewState> i
|
||||
const { jsonText } = model.useState();
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
const saveModel = model.getSaveModel();
|
||||
|
||||
return (
|
||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Standard}>
|
||||
@ -76,6 +83,7 @@ export class JsonModelEditView extends SceneObjectBase<JsonModelEditViewState> i
|
||||
The JSON model below is the data structure that defines the dashboard. This includes dashboard settings,
|
||||
panel settings, layout, queries, and so on.
|
||||
</Trans>
|
||||
<LegacyAlertsDeprecationNotice dashboard={saveModel} />
|
||||
<CodeEditor
|
||||
width="100%"
|
||||
value={jsonText}
|
||||
|
@ -5,6 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import LegacyAlertsDeprecationNotice from 'app/features/alerting/unified/integration/LegacyAlertsDeprecationNotice';
|
||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||
|
||||
import { getDashboardSrv } from '../../services/DashboardSrv';
|
||||
@ -12,7 +13,8 @@ import { getDashboardSrv } from '../../services/DashboardSrv';
|
||||
import { SettingsPageProps } from './types';
|
||||
|
||||
export function JsonEditorSettings({ dashboard, sectionNav }: SettingsPageProps) {
|
||||
const [dashboardJson, setDashboardJson] = useState<string>(JSON.stringify(dashboard.getSaveModelClone(), null, 2));
|
||||
const dashboardSaveModel = dashboard.getSaveModelClone();
|
||||
const [dashboardJson, setDashboardJson] = useState<string>(JSON.stringify(dashboardSaveModel, null, 2));
|
||||
const pageNav = sectionNav.node.parentItem;
|
||||
|
||||
const onClick = async () => {
|
||||
@ -29,6 +31,7 @@ export function JsonEditorSettings({ dashboard, sectionNav }: SettingsPageProps)
|
||||
The JSON model below is the data structure that defines the dashboard. This includes dashboard settings, panel
|
||||
settings, layout, queries, and so on.
|
||||
</Trans>
|
||||
<LegacyAlertsDeprecationNotice dashboard={dashboardSaveModel} />
|
||||
<CodeEditor
|
||||
value={dashboardJson}
|
||||
language="json"
|
||||
|
Loading…
Reference in New Issue
Block a user