mirror of https://github.com/grafana/grafana
Alerting: Use useProduceNewAlertmanagerConfiguration for contact points (#88456)
parent
02f608ed05
commit
41b175e7ae
@ -1,19 +0,0 @@ |
||||
interface RequestState { |
||||
error?: unknown; |
||||
|
||||
isUninitialized: boolean; |
||||
isSuccess: boolean; |
||||
isError: boolean; |
||||
isLoading: boolean; |
||||
} |
||||
|
||||
// @TODO what to do with the other props that we get from RTKQ's state such as originalArgs, etc?
|
||||
export function mergeRequestStates(...states: RequestState[]): RequestState { |
||||
return { |
||||
error: states.find((s) => s.error), |
||||
isUninitialized: states.every((s) => s.isUninitialized), |
||||
isSuccess: states.every((s) => s.isSuccess), |
||||
isError: states.some((s) => s.isError), |
||||
isLoading: states.some((s) => s.isLoading), |
||||
}; |
||||
} |
@ -0,0 +1,99 @@ |
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
||||
|
||||
exports[`receivers adding receivers should be able to add a new Alertmanager receiver 1`] = ` |
||||
{ |
||||
"alertmanager_config": { |
||||
"receivers": [ |
||||
{ |
||||
"email_configs": [ |
||||
{ |
||||
"to": "address@domain.com", |
||||
}, |
||||
], |
||||
"grafana_managed_receiver_configs": [], |
||||
"name": "new contact point", |
||||
}, |
||||
], |
||||
}, |
||||
"template_files": {}, |
||||
} |
||||
`; |
||||
|
||||
exports[`receivers adding receivers should be able to add a new Grafana Alertmanager receiver 1`] = ` |
||||
{ |
||||
"alertmanager_config": { |
||||
"receivers": [ |
||||
{ |
||||
"email_configs": [], |
||||
"grafana_managed_receiver_configs": [ |
||||
{ |
||||
"disableResolveMessage": false, |
||||
"name": "emea-oncall", |
||||
"settings": { |
||||
"url": "https://oncall.example.com", |
||||
}, |
||||
"type": "oncall", |
||||
}, |
||||
], |
||||
"name": "another contact point", |
||||
}, |
||||
], |
||||
}, |
||||
"template_files": {}, |
||||
} |
||||
`; |
||||
|
||||
exports[`receivers should delete a receiver 1`] = ` |
||||
{ |
||||
"alertmanager_config": { |
||||
"receivers": [ |
||||
{ |
||||
"name": "another receiver", |
||||
}, |
||||
], |
||||
}, |
||||
"template_files": {}, |
||||
} |
||||
`; |
||||
|
||||
exports[`receivers updating receivers should allow renaming a receiver and update routes 1`] = ` |
||||
{ |
||||
"alertmanager_config": { |
||||
"receivers": [ |
||||
{ |
||||
"email_configs": [], |
||||
"grafana_managed_receiver_configs": [], |
||||
"name": "receiver 2", |
||||
}, |
||||
], |
||||
"route": { |
||||
"receiver": "receiver 2", |
||||
"routes": [ |
||||
{ |
||||
"receiver": "receiver 2", |
||||
}, |
||||
], |
||||
}, |
||||
}, |
||||
"template_files": {}, |
||||
} |
||||
`; |
||||
|
||||
exports[`receivers updating receivers should allow updating an existing receiver 1`] = ` |
||||
{ |
||||
"alertmanager_config": { |
||||
"receivers": [ |
||||
{ |
||||
"email_configs": [ |
||||
{ |
||||
"to": "address+1@domain.com", |
||||
}, |
||||
], |
||||
"grafana_managed_receiver_configs": [], |
||||
"name": "existing receiver", |
||||
}, |
||||
], |
||||
}, |
||||
"template_files": {}, |
||||
} |
||||
`; |
@ -0,0 +1,138 @@ |
||||
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; |
||||
|
||||
import { AlertmanagerReceiverBuilder } from '../../mockApi'; |
||||
|
||||
import { addReceiverAction, deleteReceiverAction, receiversReducer, updateReceiverAction } from './receivers'; |
||||
|
||||
describe('receivers', () => { |
||||
const initialConfig: AlertManagerCortexConfig = { |
||||
alertmanager_config: {}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
it('should delete a receiver', () => { |
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [{ name: 'my receiver' }, { name: 'another receiver' }], |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const action = deleteReceiverAction('my receiver'); |
||||
expect(receiversReducer(config, action)).toMatchSnapshot(); |
||||
}); |
||||
|
||||
describe('adding receivers', () => { |
||||
it('should be able to add a new Alertmanager receiver', () => { |
||||
const newReceiver = new AlertmanagerReceiverBuilder() |
||||
.withName('new contact point') |
||||
.addEmailConfig((b) => b.withTo('address@domain.com')) |
||||
.build(); |
||||
|
||||
const action = addReceiverAction(newReceiver); |
||||
expect(receiversReducer(initialConfig, action)).toMatchSnapshot(); |
||||
}); |
||||
|
||||
it('should be able to add a new Grafana Alertmanager receiver', () => { |
||||
const newReceiver = new AlertmanagerReceiverBuilder() |
||||
.withName('another contact point') |
||||
.addGrafanaReceiverConfig((receiver) => |
||||
receiver.withType('oncall').withName('emea-oncall').addSetting('url', 'https://oncall.example.com') |
||||
) |
||||
.build(); |
||||
|
||||
const action = addReceiverAction(newReceiver); |
||||
expect(receiversReducer(initialConfig, action)).toMatchSnapshot(); |
||||
}); |
||||
|
||||
it('should throw if we add a receiver with duplicate name', () => { |
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [{ name: 'my receiver' }], |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const newReceiver = new AlertmanagerReceiverBuilder().withName('my receiver').build(); |
||||
const action = addReceiverAction(newReceiver); |
||||
|
||||
expect(() => { |
||||
receiversReducer(config, action); |
||||
}).toThrow(/duplicate receiver/i); |
||||
}); |
||||
}); |
||||
|
||||
describe('updating receivers', () => { |
||||
it('should throw if updating a receiver that does not exist', () => { |
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [{ name: 'my receiver' }], |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const updatedReceiver = new AlertmanagerReceiverBuilder().withName('my receiver').build(); |
||||
const action = updateReceiverAction({ name: 'does not exist', receiver: updatedReceiver }); |
||||
|
||||
expect(() => { |
||||
receiversReducer(config, action); |
||||
}).toThrow(/expected receiver .+ to exist/i); |
||||
}); |
||||
|
||||
it('should throw if renaming a receiver to an existing name', () => { |
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [{ name: 'receiver 1' }, { name: 'receiver 2' }], |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const updatedReceiver = new AlertmanagerReceiverBuilder().withName('receiver 1').build(); |
||||
const action = updateReceiverAction({ name: 'receiver 2', receiver: updatedReceiver }); |
||||
|
||||
expect(() => { |
||||
receiversReducer(config, action); |
||||
}).toThrow(/duplicate receiver name/i); |
||||
}); |
||||
|
||||
it('should allow renaming a receiver and update routes', () => { |
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [{ name: 'receiver 1' }], |
||||
route: { |
||||
receiver: 'receiver 1', |
||||
routes: [{ receiver: 'receiver 1' }], |
||||
}, |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const updatedReceiver = new AlertmanagerReceiverBuilder().withName('receiver 2').build(); |
||||
const action = updateReceiverAction({ name: 'receiver 1', receiver: updatedReceiver }); |
||||
|
||||
expect(receiversReducer(config, action)).toMatchSnapshot(); |
||||
}); |
||||
|
||||
it('should allow updating an existing receiver', () => { |
||||
const existingReceiver = new AlertmanagerReceiverBuilder() |
||||
.withName('existing receiver') |
||||
.addEmailConfig((build) => build.withTo('address@domain.com')) |
||||
.build(); |
||||
|
||||
const config: AlertManagerCortexConfig = { |
||||
alertmanager_config: { |
||||
receivers: [existingReceiver], |
||||
}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
const updatedReceiver = new AlertmanagerReceiverBuilder() |
||||
.withName('existing receiver') |
||||
.addEmailConfig((build) => build.withTo('address+1@domain.com')) |
||||
.build(); |
||||
const action = updateReceiverAction({ name: 'existing receiver', receiver: updatedReceiver }); |
||||
|
||||
expect(receiversReducer(config, action)).toMatchSnapshot(); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,74 @@ |
||||
import { createAction, createReducer } from '@reduxjs/toolkit'; |
||||
import { remove } from 'lodash'; |
||||
|
||||
import { AlertManagerCortexConfig, Receiver } from 'app/plugins/datasource/alertmanager/types'; |
||||
|
||||
import { renameReceiverInRoute } from '../../utils/notification-policies'; |
||||
|
||||
export const addReceiverAction = createAction<Receiver>('receiver/add'); |
||||
export const updateReceiverAction = createAction<{ name: string; receiver: Receiver }>('receiver/update'); |
||||
export const deleteReceiverAction = createAction<string>('receiver/delete'); |
||||
|
||||
const initialState: AlertManagerCortexConfig = { |
||||
alertmanager_config: {}, |
||||
template_files: {}, |
||||
}; |
||||
|
||||
/** |
||||
* This reducer will manage action related to receiver (Contact points) and make sure all operations on the alertmanager |
||||
* configuration happen immutably and only mutate what they need. |
||||
*/ |
||||
export const receiversReducer = createReducer(initialState, (builder) => { |
||||
builder |
||||
// add a new receiver
|
||||
.addCase(addReceiverAction, (draft, { payload: newReceiver }) => { |
||||
// ensure the receivers are always an array
|
||||
const currentReceivers = (draft.alertmanager_config.receivers = draft.alertmanager_config.receivers ?? []); |
||||
|
||||
// check if the name doesn't already exist
|
||||
const nameExists = currentReceivers.some((receiver) => receiver.name === newReceiver.name); |
||||
if (nameExists) { |
||||
throw new Error(`Duplicate receiver name ${newReceiver.name}`); |
||||
} |
||||
|
||||
// add the receiver
|
||||
currentReceivers.push(newReceiver); |
||||
}) |
||||
// upate an existing receiver
|
||||
.addCase(updateReceiverAction, (draft, { payload }) => { |
||||
const { name, receiver } = payload; |
||||
const renaming = name !== receiver.name; |
||||
|
||||
const receivers = draft.alertmanager_config.receivers ?? []; |
||||
|
||||
const targetIndex = receivers.findIndex((receiver) => receiver.name === name); |
||||
const targetExists = targetIndex > -1; |
||||
|
||||
// check if the receiver we want to update exists
|
||||
if (!targetExists) { |
||||
throw new Error(`Expected receiver ${name} to exist, but did not find it in the config`); |
||||
} |
||||
|
||||
// check if the new name doesn't already exist
|
||||
if (renaming) { |
||||
const nameExists = receivers.some((oldReceiver) => oldReceiver.name === receiver.name); |
||||
if (nameExists) { |
||||
throw new Error(`Duplicate receiver name ${receiver.name}`); |
||||
} |
||||
} |
||||
|
||||
// overwrite the receiver with the new one
|
||||
receivers[targetIndex] = receiver; |
||||
|
||||
// check if we need to update routes if the contact point was renamed
|
||||
const routeTree = draft.alertmanager_config.route; |
||||
|
||||
if (routeTree && renaming) { |
||||
draft.alertmanager_config.route = renameReceiverInRoute(routeTree, name, receiver.name); |
||||
} |
||||
}) |
||||
// delete a receiver from the alertmanager configuration
|
||||
.addCase(deleteReceiverAction, (draft, { payload: name }) => { |
||||
remove(draft.alertmanager_config.receivers ?? [], (receiver) => receiver.name === name); |
||||
}); |
||||
}); |
Loading…
Reference in new issue