Alerting: Fix contact points secure settings migration (#95787)

Add missing migration for secure settings, add types to notifiers mocks
This commit is contained in:
Konrad Lalik 2024-11-05 14:46:17 +01:00 committed by GitHub
parent 91088d1f56
commit 3c8d29fa46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 364 additions and 29 deletions

View File

@ -1,7 +1,7 @@
import { NotifierDTO } from 'app/types';
import { GrafanaNotifierType, NotifierDTO } from 'app/types';
export const grafanaAlertNotifiersMock: NotifierDTO[] = [
{
export const grafanaAlertNotifiers: Record<GrafanaNotifierType, NotifierDTO> = {
dingding: {
type: 'dingding',
name: 'DingDing',
heading: 'DingDing settings',
@ -87,7 +87,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
kafka: {
type: 'kafka',
name: 'Kafka REST Proxy',
heading: 'Kafka settings',
@ -242,7 +242,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
email: {
type: 'email',
name: 'Email',
heading: 'Email settings',
@ -320,7 +320,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
pagerduty: {
type: 'pagerduty',
name: 'PagerDuty',
heading: 'PagerDuty settings',
@ -500,7 +500,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
victorops: {
type: 'victorops',
name: 'VictorOps',
heading: 'VictorOps settings',
@ -586,7 +586,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
pushover: {
type: 'pushover',
name: 'Pushover',
heading: 'Pushover settings',
@ -1011,7 +1011,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
slack: {
type: 'slack',
name: 'Slack',
heading: 'Slack settings',
@ -1242,7 +1242,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
sensugo: {
type: 'sensugo',
name: 'Sensu Go',
heading: 'Sensu Go Settings',
@ -1370,7 +1370,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
teams: {
type: 'teams',
name: 'Microsoft Teams',
heading: 'Teams settings',
@ -1447,7 +1447,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
telegram: {
type: 'telegram',
name: 'Telegram',
heading: 'Telegram API settings',
@ -1592,7 +1592,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
webhook: {
type: 'webhook',
name: 'Webhook',
heading: 'Webhook settings',
@ -1765,7 +1765,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
oncall: {
type: 'oncall',
name: 'Grafana OnCall',
heading: 'Grafana OnCall settings',
@ -1938,7 +1938,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
wecom: {
type: 'wecom',
name: 'WeCom',
heading: 'WeCom settings',
@ -2092,7 +2092,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
'prometheus-alertmanager': {
type: 'prometheus-alertmanager',
name: 'Alertmanager',
heading: 'Alertmanager Settings',
@ -2152,7 +2152,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
discord: {
type: 'discord',
name: 'Discord',
heading: 'Discord settings',
@ -2247,7 +2247,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
googlechat: {
type: 'googlechat',
name: 'Google Hangouts Chat',
heading: 'Google Hangouts Chat settings',
@ -2268,7 +2268,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
required: true,
validationRule: '',
secure: false,
secure: true,
dependsOn: '',
},
{
@ -2307,7 +2307,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
LINE: {
type: 'LINE',
name: 'LINE',
heading: 'LINE notify settings',
@ -2367,7 +2367,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
threema: {
type: 'threema',
name: 'Threema Gateway',
heading: 'Threema Gateway settings',
@ -2461,7 +2461,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
mqtt: {
type: 'mqtt',
name: 'MQTT',
heading: 'MQTT settings',
@ -2733,7 +2733,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
opsgenie: {
type: 'opsgenie',
name: 'OpsGenie',
heading: 'OpsGenie settings',
@ -2874,7 +2874,7 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
{
webex: {
type: 'webex',
name: 'Cisco Webex Teams',
heading: 'Webex settings',
@ -2951,4 +2951,254 @@ export const grafanaAlertNotifiersMock: NotifierDTO[] = [
},
],
},
];
sns: {
type: 'sns',
name: 'AWS SNS',
heading: 'Webex settings',
description: 'Sends notifications to AWS Simple Notification Service',
info: '',
options: [
{
element: 'input',
inputType: 'text',
label: 'The Amazon SNS API URL',
description: '',
placeholder: '',
propertyName: 'api_url',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'subform',
inputType: '',
label: 'SigV4 Authentication',
description: "Configures AWS's Signature Verification 4 signing process to sign requests",
placeholder: '',
propertyName: 'sigv4',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: [
{
element: 'input',
inputType: 'text',
label: 'Region',
description: 'The AWS region. If blank, the region from the default credentials chain is used.',
placeholder: '',
propertyName: 'region',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Access Key',
description: 'The AWS API access key.',
placeholder: '',
propertyName: 'access_key',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: true,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Secret Key',
description: 'The AWS API secret key.',
placeholder: '',
propertyName: 'secret_key',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: true,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Profile',
description: 'Named AWS profile used to authenticate',
placeholder: '',
propertyName: 'profile',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Role ARN',
description: 'AWS Role ARN, an alternative to using AWS API keys',
placeholder: '',
propertyName: 'role_arn',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
],
},
{
element: 'input',
inputType: 'text',
label: 'SNS topic ARN',
description:
"If you don't specify this value, you must specify a value for the phone_number or target_arn. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window.",
placeholder: '',
propertyName: 'topic_arn',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Phone number',
description:
"Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the topic_arn or target_arn",
placeholder: '',
propertyName: 'phone_number',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Target ARN',
description:
"The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the topic_arn or phone_number",
placeholder: '',
propertyName: 'target_arn',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'input',
inputType: 'text',
label: 'Subject',
description: 'Optional subject. You can use templates to customize this field',
placeholder: '{{ template "default.title" . }}',
propertyName: 'subject',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'textarea',
inputType: '',
label: 'Message',
description:
'Optional message. You can use templates to customize this field. Using a custom message will replace the default message',
placeholder: '{{ template "default.message" . }}',
propertyName: 'message',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
{
element: 'key_value_map',
inputType: 'text',
label: 'Attributes',
description: 'SNS message attributes',
placeholder: '',
propertyName: 'attributes',
selectOptions: null,
showWhen: {
field: '',
is: '',
},
required: false,
validationRule: '',
secure: false,
dependsOn: '',
subformOptions: undefined,
},
],
},
};
export const grafanaAlertNotifiersMock: NotifierDTO[] = Object.values(grafanaAlertNotifiers);

View File

@ -1,6 +1,7 @@
import { NotifierDTO } from 'app/types';
import { Receiver } from '../../../../plugins/datasource/alertmanager/types';
import { GrafanaManagedContactPoint, Receiver } from '../../../../plugins/datasource/alertmanager/types';
import { grafanaAlertNotifiers, grafanaAlertNotifiersMock } from '../mockGrafanaNotifiers';
import { CloudChannelValues, GrafanaChannelValues, ReceiverFormValues } from '../types/receiver-form';
import {
@ -9,6 +10,7 @@ import {
omitEmptyUnlessExisting,
omitTemporaryIdentifiers,
formValuesToCloudReceiver,
grafanaReceiverToFormValues,
} from './receiver-form';
describe('Receiver form utils', () => {
@ -250,3 +252,78 @@ describe('formValuesToCloudReceiver', () => {
expect(formValuesToCloudReceiver(formValues, defaults)).toEqual(expected);
});
});
describe('grafanaReceiverToFormValues', () => {
const { googlechat, slack, sns } = grafanaAlertNotifiers;
it('should convert fields from settings and secureFields', () => {
const slackReceiver: GrafanaManagedContactPoint = {
name: 'slack-receiver',
grafana_managed_receiver_configs: [
{
type: slack.type,
settings: {
recipient: '#alerting-ops',
},
secureFields: {
token: true,
},
},
],
};
const [formValues, _] = grafanaReceiverToFormValues(slackReceiver, grafanaAlertNotifiersMock);
expect(formValues.items[0].type).toBe(slack.type);
expect(formValues.items[0].settings.recipient).toBe('#alerting-ops');
expect(formValues.items[0].secureFields.token).toBe(true);
expect(formValues.items[0].secureSettings).toEqual({});
});
it('should convert nested settings and secureFields', () => {
const snsReceiver: GrafanaManagedContactPoint = {
name: 'sns-receiver',
grafana_managed_receiver_configs: [
{
type: sns.type,
settings: {
api_url: 'https://sns.example.com/',
phone_number: '+1234567890',
sigv4: { region: 'us-east-1' },
},
secureFields: {
'sigv4.access_key': true,
'sigv4.secret_key': true,
},
},
],
};
const [formValues, _] = grafanaReceiverToFormValues(snsReceiver, grafanaAlertNotifiersMock);
expect(formValues.items[0].settings.api_url).toBe('https://sns.example.com/');
expect(formValues.items[0].settings.phone_number).toBe('+1234567890');
expect(formValues.items[0].settings.sigv4.region).toBe('us-east-1');
expect(formValues.items[0].secureFields['sigv4.access_key']).toBe(true);
expect(formValues.items[0].secureFields['sigv4.secret_key']).toBe(true);
});
// Some receivers have migrated options that are now marked as secure but were standard fields in the past
// We need to handle the case where the field is still present in settings but marked as secure
it('should convert fields from settings to secureSettings for migrated options', () => {
const googleChatReceiver: GrafanaManagedContactPoint = {
name: 'googlechat-receiver',
grafana_managed_receiver_configs: [
{
type: googlechat.type,
settings: {
url: 'https://googlechat.example.com/',
},
},
],
};
const [formValues, _] = grafanaReceiverToFormValues(googleChatReceiver, grafanaAlertNotifiersMock);
expect(formValues.items[0].secureSettings.url).toBe('https://googlechat.example.com/');
expect(formValues.items[0].settings.url).toBeUndefined();
});
});

View File

@ -151,6 +151,13 @@ function grafanaChannelConfigToFormChannelValues(
disableResolveMessage: channel.disableResolveMessage,
};
notifier?.options.forEach((option) => {
if (option.secure && values.settings[option.propertyName]) {
values.secureSettings[option.propertyName] = values.settings[option.propertyName];
delete values.settings[option.propertyName];
}
});
return values;
}

View File

@ -38,9 +38,7 @@ export interface AlertRule {
export type GrafanaNotifierType =
| 'discord'
| 'hipchat'
| 'email'
| 'sensu'
| 'sensugo'
| 'googlechat'
| 'threema'
@ -57,7 +55,10 @@ export type GrafanaNotifierType =
| 'LINE'
| 'kafka'
| 'wecom'
| 'mqtt';
| 'webex'
| 'mqtt'
| 'oncall'
| 'sns';
export type CloudNotifierType =
| 'oncall' // Only FE implementation for now