mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Show a warning when a template has been potentially misconfigured (#94698)
This commit is contained in:
parent
016dea1143
commit
0841497cad
@ -6,3 +6,4 @@
|
||||
import { Text } from '@grafana/ui';
|
||||
|
||||
export const PrimaryText = ({ content }: { content: string }) => <Text color="primary">{content}</Text>;
|
||||
export const CodeText = ({ content }: { content: string }) => <Text variant="code">{content}</Text>;
|
||||
|
@ -126,6 +126,13 @@ describe('contact points', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('templates tab', () => {
|
||||
it('shows a warning when a template is misconfigured', async () => {
|
||||
renderWithProvider(<ContactPointsPageContents />, { initialEntries: ['/?tab=templates'] });
|
||||
expect((await screen.findAllByText(/^misconfigured$/i))[0]).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show / hide loading states, have all actions enabled', async () => {
|
||||
renderWithProvider(<ContactPointsPageContents />);
|
||||
|
||||
|
@ -3,10 +3,13 @@
|
||||
"slack-template": "{{ define \"slack-template\" }} Custom slack template {{ end }}",
|
||||
"custom-email": "{{ define \"custom-email\" }} Custom email template {{ end }}",
|
||||
"provisioned-template": "{{ define \"provisioned-template\" }} Custom provisioned template {{ end }}",
|
||||
"template with spaces": "{{ define \"template with spaces\" }} Custom template with spaces in the name {{ end }}"
|
||||
"template with spaces": "{{ define \"template with spaces\" }} Custom template with spaces in the name {{ end }}",
|
||||
"misconfigured-template": "{{ define \"misconfigured template\" }} Template that is defined in template_files but not templates {{ end }}",
|
||||
"misconfigured and provisioned": "{{ define \"misconfigured and provisioned template\" }} Provisioned template that is defined in template_files but not templates {{ end }}"
|
||||
},
|
||||
"template_file_provenances": {
|
||||
"provisioned-template": "api"
|
||||
"provisioned-template": "api",
|
||||
"misconfigured and provisioned": "api"
|
||||
},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
|
@ -26,6 +26,7 @@ export interface NotificationTemplate {
|
||||
title: string;
|
||||
content: string;
|
||||
provenance: string;
|
||||
missing?: boolean;
|
||||
}
|
||||
|
||||
const { useGetAlertmanagerConfigurationQuery, useLazyGetAlertmanagerConfigurationQuery } = alertmanagerApi;
|
||||
@ -84,12 +85,15 @@ function templateGroupToTemplate(
|
||||
}
|
||||
|
||||
function amConfigToTemplates(config: AlertManagerCortexConfig): NotificationTemplate[] {
|
||||
const { alertmanager_config } = config;
|
||||
const { templates = [] } = alertmanager_config;
|
||||
return Object.entries(config.template_files).map(([title, content]) => ({
|
||||
uid: title,
|
||||
title,
|
||||
content,
|
||||
// Undefined, null or empty string should be converted to PROVENANCE_NONE
|
||||
provenance: (config.template_file_provenances ?? {})[title] || PROVENANCE_NONE,
|
||||
missing: !templates.includes(title),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,11 @@ describe('alerting API server disabled', () => {
|
||||
);
|
||||
|
||||
const testBody = await testRequest?.json();
|
||||
const saveBody = await saveRequest?.json();
|
||||
const fullSaveBody = await saveRequest?.json();
|
||||
|
||||
// Only snapshot and check the receivers, as we don't want other tests to break this
|
||||
// just because we added something new to the mock config
|
||||
const saveBody = fullSaveBody.alertmanager_config.receivers;
|
||||
|
||||
expect([testBody]).toMatchSnapshot();
|
||||
expect([saveBody]).toMatchSnapshot();
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
import { logError } from '@grafana/runtime';
|
||||
import { ConfirmModal, useStyles2 } from '@grafana/ui';
|
||||
import { Badge, ConfirmModal, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { CodeText } from 'app/features/alerting/unified/components/common/TextVariants';
|
||||
|
||||
import { Authorize } from '../../components/Authorize';
|
||||
import { AlertmanagerAction } from '../../hooks/useAbilities';
|
||||
@ -116,8 +118,8 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const { isProvisioned } = useNotificationTemplateMetadata(notificationTemplate);
|
||||
|
||||
const { uid, title: name, content: template } = notificationTemplate;
|
||||
|
||||
const { uid, title: name, content: template, missing } = notificationTemplate;
|
||||
const misconfiguredBadgeText = t('alerting.templates.misconfigured-badge-text', 'Misconfigured');
|
||||
return (
|
||||
<Fragment key={uid}>
|
||||
<tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}>
|
||||
@ -125,7 +127,25 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
|
||||
<CollapseToggle isCollapsed={!isExpanded} onToggle={() => setIsExpanded(!isExpanded)} />
|
||||
</td>
|
||||
<td>
|
||||
{name} {isProvisioned && <ProvisioningBadge />}
|
||||
{name} {isProvisioned && <ProvisioningBadge />}{' '}
|
||||
{missing && (
|
||||
<Tooltip
|
||||
content={
|
||||
<>
|
||||
<Trans i18nKey="alerting.templates.misconfigured-warning">This template is misconfigured.</Trans>
|
||||
<br />
|
||||
<Trans i18nKey="alerting.templates.misconfigured-warning-details">
|
||||
Templates must be defined in both the <CodeText content="template_files" /> and{' '}
|
||||
<CodeText content="templates" /> sections of your alertmanager configuration.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Badge text={misconfiguredBadgeText} color="orange" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</td>
|
||||
<td className={tableStyles.actionsCell}>
|
||||
{isProvisioned && (
|
||||
|
@ -34,142 +34,113 @@ exports[`alerting API server disabled should be able to test and save a receiver
|
||||
|
||||
exports[`alerting API server disabled should be able to test and save a receiver 2`] = `
|
||||
[
|
||||
{
|
||||
"alertmanager_config": {
|
||||
"mute_time_intervals": [],
|
||||
"receivers": [
|
||||
[
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "grafana-default-email",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "xeKQrBrnk",
|
||||
},
|
||||
],
|
||||
"disableResolveMessage": false,
|
||||
"name": "grafana-default-email",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "provisioned-contact-point",
|
||||
"provenance": "api",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "s8SdCVjnk",
|
||||
},
|
||||
],
|
||||
"name": "provisioned-contact-point",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "lotsa-emails",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
|
||||
},
|
||||
],
|
||||
"name": "lotsa-emails",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
|
||||
},
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts2",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "b286a3be-f690-49e2-8605-b075cbace2df",
|
||||
},
|
||||
],
|
||||
"name": "Slack with multiple channels",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Oncall-integration",
|
||||
"settings": {
|
||||
"url": "https://oncall-endpoint.example.com",
|
||||
},
|
||||
"type": "oncall",
|
||||
},
|
||||
],
|
||||
"name": "OnCall Conctact point",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "my new receiver",
|
||||
"secureSettings": {},
|
||||
"settings": {
|
||||
"addresses": "tester@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
},
|
||||
],
|
||||
"name": "my new receiver",
|
||||
},
|
||||
],
|
||||
"route": {
|
||||
"receiver": "grafana-default-email",
|
||||
"routes": [
|
||||
{
|
||||
"receiver": "provisioned-contact-point",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"templates": [
|
||||
"slack-template",
|
||||
"custom-email",
|
||||
"provisioned-template",
|
||||
"template with spaces",
|
||||
"type": "email",
|
||||
"uid": "xeKQrBrnk",
|
||||
},
|
||||
],
|
||||
"time_intervals": [],
|
||||
"name": "grafana-default-email",
|
||||
},
|
||||
"template_file_provenances": {
|
||||
"provisioned-template": "api",
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "provisioned-contact-point",
|
||||
"provenance": "api",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "s8SdCVjnk",
|
||||
},
|
||||
],
|
||||
"name": "provisioned-contact-point",
|
||||
},
|
||||
"template_files": {
|
||||
"custom-email": "{{ define "custom-email" }} Custom email template {{ end }}",
|
||||
"provisioned-template": "{{ define "provisioned-template" }} Custom provisioned template {{ end }}",
|
||||
"slack-template": "{{ define "slack-template" }} Custom slack template {{ end }}",
|
||||
"template with spaces": "{{ define "template with spaces" }} Custom template with spaces in the name {{ end }}",
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "lotsa-emails",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
|
||||
},
|
||||
],
|
||||
"name": "lotsa-emails",
|
||||
},
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
|
||||
},
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts2",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "b286a3be-f690-49e2-8605-b075cbace2df",
|
||||
},
|
||||
],
|
||||
"name": "Slack with multiple channels",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Oncall-integration",
|
||||
"settings": {
|
||||
"url": "https://oncall-endpoint.example.com",
|
||||
},
|
||||
"type": "oncall",
|
||||
},
|
||||
],
|
||||
"name": "OnCall Conctact point",
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "my new receiver",
|
||||
"secureSettings": {},
|
||||
"settings": {
|
||||
"addresses": "tester@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
},
|
||||
],
|
||||
"name": "my new receiver",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
@ -2,6 +2,7 @@ import { ReactNode } from 'react';
|
||||
import { render, screen, userEvent } from 'test/test-utils';
|
||||
|
||||
import { CodeEditorProps } from '@grafana/ui/src/components/Monaco/types';
|
||||
import alertmanagerConfigMock from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json';
|
||||
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
import { grantUserPermissions } from 'app/features/alerting/unified/mocks';
|
||||
import { AlertmanagerProvider } from 'app/features/alerting/unified/state/AlertmanagerContext';
|
||||
@ -98,7 +99,7 @@ describe('TemplatesPicker', () => {
|
||||
const input = screen.getByRole('combobox');
|
||||
expect(screen.queryByText('slack-template')).not.toBeInTheDocument();
|
||||
await userEvent.click(input);
|
||||
expect(screen.getAllByRole('option')).toHaveLength(7); // 4 templates in mock plus 3 in the default template
|
||||
expect(screen.getAllByRole('option')).toHaveLength(Object.keys(alertmanagerConfigMock.template_files).length + 3); // 4 templates in mock plus 3 in the default template
|
||||
const template = screen.getByRole('option', { name: 'slack-template' });
|
||||
await userEvent.click(template);
|
||||
expect(screen.getByText('slack-template')).toBeInTheDocument();
|
||||
|
@ -284,6 +284,11 @@
|
||||
"ofQuery": {
|
||||
"To": "TO"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"misconfigured-badge-text": "Misconfigured",
|
||||
"misconfigured-warning": "This template is misconfigured.",
|
||||
"misconfigured-warning-details": "Templates must be defined in both the <1></1> and <4></4> sections of your alertmanager configuration."
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
|
@ -284,6 +284,11 @@
|
||||
"ofQuery": {
|
||||
"To": "ŦØ"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"misconfigured-badge-text": "Mįşčőʼnƒįģūřęđ",
|
||||
"misconfigured-warning": "Ŧĥįş ŧęmpľäŧę įş mįşčőʼnƒįģūřęđ.",
|
||||
"misconfigured-warning-details": "Ŧęmpľäŧęş mūşŧ þę đęƒįʼnęđ įʼn þőŧĥ ŧĥę <1></1> äʼnđ <4></4> şęčŧįőʼnş őƒ yőūř äľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼn."
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
|
Loading…
Reference in New Issue
Block a user