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 { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { LoadingPlaceholder } from '@grafana/ui';
|
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 { RulesTable } from '../components/rules/RulesTable';
|
||||||
import { useCombinedRuleNamespaces } from '../hooks/useCombinedRuleNamespaces';
|
import { useCombinedRuleNamespaces } from '../hooks/useCombinedRuleNamespaces';
|
||||||
@ -10,14 +11,26 @@ import { fetchPromAndRulerRulesAction } from '../state/actions';
|
|||||||
import { Annotation } from '../utils/constants';
|
import { Annotation } from '../utils/constants';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
|
||||||
|
import LegacyAlertsDeprecationNotice from './LegacyAlertsDeprecationNotice';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dashboardUid: string;
|
dashboardUid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
||||||
const dispatch = useDispatch();
|
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 }));
|
await dispatch(fetchPromAndRulerRulesAction({ rulesSourceName: GRAFANA_RULES_SOURCE_NAME }));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@ -27,12 +40,17 @@ export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
|||||||
.flatMap((g) => g.rules)
|
.flatMap((g) => g.rules)
|
||||||
.filter((rule) => rule.annotations[Annotation.dashboardUID] === dashboardUid);
|
.filter((rule) => rule.annotations[Annotation.dashboardUID] === dashboardUid);
|
||||||
|
|
||||||
|
const loading = loadingDashboardState || loadingAlertRules;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoadingPlaceholder text="Loading alert rules" />
|
<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)}`);
|
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makePanelLink(dashboardUID: string, panelId: string): string {
|
type PanelLinkParams = {
|
||||||
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`, { viewPanel: panelId });
|
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
|
// 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 { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
||||||
import { SceneComponentProps, SceneObjectBase, sceneUtils } from '@grafana/scenes';
|
import { SceneComponentProps, SceneObjectBase, sceneUtils } from '@grafana/scenes';
|
||||||
|
import { Dashboard } from '@grafana/schema';
|
||||||
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
|
import LegacyAlertsDeprecationNotice from 'app/features/alerting/unified/integration/LegacyAlertsDeprecationNotice';
|
||||||
import { getPrettyJSON } from 'app/features/inspector/utils/utils';
|
import { getPrettyJSON } from 'app/features/inspector/utils/utils';
|
||||||
import { DashboardDTO } from 'app/types';
|
import { DashboardDTO } from 'app/types';
|
||||||
|
|
||||||
@ -38,9 +40,13 @@ export class JsonModelEditView extends SceneObjectBase<JsonModelEditViewState> i
|
|||||||
return getDashboardSceneFor(this);
|
return getDashboardSceneFor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getJsonText(): string {
|
public getSaveModel(): Dashboard {
|
||||||
const dashboard = this.getDashboard();
|
const dashboard = this.getDashboard();
|
||||||
const jsonData = transformSceneToSaveModel(dashboard);
|
return transformSceneToSaveModel(dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getJsonText(): string {
|
||||||
|
const jsonData = this.getSaveModel();
|
||||||
return getPrettyJSON(jsonData);
|
return getPrettyJSON(jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +73,7 @@ export class JsonModelEditView extends SceneObjectBase<JsonModelEditViewState> i
|
|||||||
const { jsonText } = model.useState();
|
const { jsonText } = model.useState();
|
||||||
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
const saveModel = model.getSaveModel();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Standard}>
|
<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,
|
The JSON model below is the data structure that defines the dashboard. This includes dashboard settings,
|
||||||
panel settings, layout, queries, and so on.
|
panel settings, layout, queries, and so on.
|
||||||
</Trans>
|
</Trans>
|
||||||
|
<LegacyAlertsDeprecationNotice dashboard={saveModel} />
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
width="100%"
|
width="100%"
|
||||||
value={jsonText}
|
value={jsonText}
|
||||||
|
@ -5,6 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data';
|
|||||||
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
import { Button, CodeEditor, useStyles2 } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
|
import LegacyAlertsDeprecationNotice from 'app/features/alerting/unified/integration/LegacyAlertsDeprecationNotice';
|
||||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||||
|
|
||||||
import { getDashboardSrv } from '../../services/DashboardSrv';
|
import { getDashboardSrv } from '../../services/DashboardSrv';
|
||||||
@ -12,7 +13,8 @@ import { getDashboardSrv } from '../../services/DashboardSrv';
|
|||||||
import { SettingsPageProps } from './types';
|
import { SettingsPageProps } from './types';
|
||||||
|
|
||||||
export function JsonEditorSettings({ dashboard, sectionNav }: SettingsPageProps) {
|
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 pageNav = sectionNav.node.parentItem;
|
||||||
|
|
||||||
const onClick = async () => {
|
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
|
The JSON model below is the data structure that defines the dashboard. This includes dashboard settings, panel
|
||||||
settings, layout, queries, and so on.
|
settings, layout, queries, and so on.
|
||||||
</Trans>
|
</Trans>
|
||||||
|
<LegacyAlertsDeprecationNotice dashboard={dashboardSaveModel} />
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
value={dashboardJson}
|
value={dashboardJson}
|
||||||
language="json"
|
language="json"
|
||||||
|
Loading…
Reference in New Issue
Block a user