Skip to content

Commit 3aa59e9

Browse files
authored
[alerting] migrates the old alerting consumer to be alerts (#69982) (#70052)
This PR migrates all old alerts with the `alerting` consumer to have `alerts` instead. This is because in 7.9 we changed the feature ID and we need these to remain in sync otherwise the RBAC work (#67157) will break old alerts.
1 parent a5b6788 commit 3aa59e9

10 files changed

Lines changed: 562 additions & 2 deletions

File tree

x-pack/plugins/alerts/server/saved_objects/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { SavedObjectsServiceSetup } from 'kibana/server';
88
import mappings from './mappings.json';
9+
import { getMigrations } from './migrations';
910
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
1011

1112
export function setupSavedObjects(
@@ -16,6 +17,7 @@ export function setupSavedObjects(
1617
name: 'alert',
1718
hidden: true,
1819
namespaceType: 'single',
20+
migrations: getMigrations(encryptedSavedObjects),
1921
mappings: mappings.alert,
2022
});
2123

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import uuid from 'uuid';
7+
import { getMigrations } from './migrations';
8+
import { RawAlert } from '../types';
9+
import { SavedObjectUnsanitizedDoc } from 'kibana/server';
10+
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
11+
import { migrationMocks } from 'src/core/server/mocks';
12+
13+
const { log } = migrationMocks.createContext();
14+
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
15+
16+
describe('7.9.0', () => {
17+
beforeEach(() => {
18+
jest.resetAllMocks();
19+
encryptedSavedObjectsSetup.createMigration.mockImplementation(
20+
(shouldMigrateWhenPredicate, migration) => migration
21+
);
22+
});
23+
24+
test('changes nothing on alerts by other plugins', () => {
25+
const migration790 = getMigrations(encryptedSavedObjectsSetup)['7.9.0'];
26+
const alert = getMockData({});
27+
expect(migration790(alert, { log })).toMatchObject(alert);
28+
29+
expect(encryptedSavedObjectsSetup.createMigration).toHaveBeenCalledWith(
30+
expect.any(Function),
31+
expect.any(Function)
32+
);
33+
});
34+
35+
test('migrates the consumer for alerting', () => {
36+
const migration790 = getMigrations(encryptedSavedObjectsSetup)['7.9.0'];
37+
const alert = getMockData({
38+
consumer: 'alerting',
39+
});
40+
expect(migration790(alert, { log })).toMatchObject({
41+
...alert,
42+
attributes: {
43+
...alert.attributes,
44+
consumer: 'alerts',
45+
},
46+
});
47+
});
48+
});
49+
50+
function getMockData(
51+
overwrites: Record<string, unknown> = {}
52+
): SavedObjectUnsanitizedDoc<RawAlert> {
53+
return {
54+
attributes: {
55+
enabled: true,
56+
name: 'abc',
57+
tags: ['foo'],
58+
alertTypeId: '123',
59+
consumer: 'bar',
60+
apiKey: '',
61+
apiKeyOwner: '',
62+
schedule: { interval: '10s' },
63+
throttle: null,
64+
params: {
65+
bar: true,
66+
},
67+
muteAll: false,
68+
mutedInstanceIds: [],
69+
createdBy: new Date().toISOString(),
70+
updatedBy: new Date().toISOString(),
71+
createdAt: new Date().toISOString(),
72+
actions: [
73+
{
74+
group: 'default',
75+
actionRef: '1',
76+
actionTypeId: '1',
77+
params: {
78+
foo: true,
79+
},
80+
},
81+
],
82+
...overwrites,
83+
},
84+
id: uuid.v4(),
85+
type: 'alert',
86+
};
87+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import {
7+
SavedObjectMigrationMap,
8+
SavedObjectUnsanitizedDoc,
9+
SavedObjectMigrationFn,
10+
} from '../../../../../src/core/server';
11+
import { RawAlert } from '../types';
12+
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
13+
14+
export function getMigrations(
15+
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
16+
): SavedObjectMigrationMap {
17+
return {
18+
'7.9.0': changeAlertingConsumer(encryptedSavedObjects),
19+
};
20+
}
21+
22+
/**
23+
* In v7.9.0 we changed the Alerting plugin so it uses the `consumer` value of `alerts`
24+
* prior to that we were using `alerting` and we need to keep these in sync
25+
*/
26+
function changeAlertingConsumer(
27+
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
28+
): SavedObjectMigrationFn<RawAlert, RawAlert> {
29+
const consumerMigration = new Map<string, string>();
30+
consumerMigration.set('alerting', 'alerts');
31+
32+
return encryptedSavedObjects.createMigration<RawAlert, RawAlert>(
33+
function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc<RawAlert> {
34+
return consumerMigration.has(doc.attributes.consumer);
35+
},
36+
(doc: SavedObjectUnsanitizedDoc<RawAlert>): SavedObjectUnsanitizedDoc<RawAlert> => {
37+
const {
38+
attributes: { consumer },
39+
} = doc;
40+
return {
41+
...doc,
42+
attributes: {
43+
...doc.attributes,
44+
consumer: consumerMigration.get(consumer) ?? consumer,
45+
},
46+
};
47+
}
48+
);
49+
}

x-pack/test/alerting_api_integration/common/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
8282
'localhost',
8383
'some.non.existent.com',
8484
])}`,
85+
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
8586
`--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`,
8687
'--xpack.eventLog.logEntries=true',
8788
`--xpack.actions.preconfigured=${JSON.stringify({

x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext)
1717

1818
describe('actionType not enabled', () => {
1919
// loads action PREWRITTEN_ACTION_ID with actionType DISABLED_ACTION_TYPE
20-
before(() => esArchiver.load('alerting'));
21-
after(() => esArchiver.unload('alerting'));
20+
before(() => esArchiver.load('actions'));
21+
after(() => esArchiver.unload('actions'));
2222

2323
it('should handle create action with disabled actionType request appropriately', async () => {
2424
const response = await supertest.post(`/api/actions/action`).set('kbn-xsrf', 'foo').send({

x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) {
2626
loadTestFile(require.resolve('./alerts_space1'));
2727
loadTestFile(require.resolve('./alerts_default_space'));
2828
loadTestFile(require.resolve('./builtin_alert_types'));
29+
loadTestFile(require.resolve('./migrations'));
2930
});
3031
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import expect from '@kbn/expect';
8+
import { getUrlPrefix } from '../../../common/lib';
9+
import { FtrProviderContext } from '../../../common/ftr_provider_context';
10+
11+
// eslint-disable-next-line import/no-default-export
12+
export default function createGetTests({ getService }: FtrProviderContext) {
13+
const supertest = getService('supertest');
14+
const esArchiver = getService('esArchiver');
15+
16+
describe('migrations', () => {
17+
before(async () => {
18+
await esArchiver.load('alerts');
19+
});
20+
21+
after(async () => {
22+
await esArchiver.unload('alerts');
23+
});
24+
25+
it('7.9.0 migrates the `alerting` consumer to be the `alerts`', async () => {
26+
const response = await supertest.get(
27+
`${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e`
28+
);
29+
30+
expect(response.status).to.eql(200);
31+
expect(response.body.consumer).to.equal('alerts');
32+
});
33+
});
34+
}
File renamed without changes.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"type": "doc",
3+
"value": {
4+
"id": "alert:74f3e6d7-b7bb-477d-ac28-92ee22728e6e",
5+
"index": ".kibana_1",
6+
"source": {
7+
"alert": {
8+
"actions": [
9+
],
10+
"alertTypeId": "example.always-firing",
11+
"apiKey": "QIUT8u0/kbOakEHSj50jDpVR90MrqOxanEscboYOoa8PxQvcA5jfHash+fqH3b+KNjJ1LpnBcisGuPkufY9j1e32gKzwGZV5Bfys87imHvygJvIM8uKiFF8bQ8Y4NTaxOJO9fAmZPrFy07ZcQMCAQz+DUTgBFqs=",
12+
"apiKeyOwner": "elastic",
13+
"consumer": "alerting",
14+
"createdAt": "2020-06-17T15:35:38.497Z",
15+
"createdBy": "elastic",
16+
"enabled": true,
17+
"muteAll": false,
18+
"mutedInstanceIds": [
19+
],
20+
"name": "always-firing-alert",
21+
"params": {
22+
},
23+
"schedule": {
24+
"interval": "1m"
25+
},
26+
"scheduledTaskId": "329798f0-b0b0-11ea-9510-fdf248d5f2a4",
27+
"tags": [
28+
],
29+
"throttle": null,
30+
"updatedBy": "elastic"
31+
},
32+
"migrationVersion": {
33+
"alert": "7.8.0"
34+
},
35+
"references": [
36+
],
37+
"type": "alert",
38+
"updated_at": "2020-06-17T15:35:39.839Z"
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)