[RAC][Observability] preserve lifecycle alert changes for active alerts (#110124)

* preserve lifecycle changes for active alerts

* fix failing tests

* fix failing lifecycle executor tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
mgiota 2021-08-31 09:42:33 +02:00 committed by GitHub
parent 02538b6b41
commit b7ad4268d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 12 deletions

View file

@ -17,6 +17,7 @@ import {
ALERT_STATUS,
ALERT_STATUS_ACTIVE,
ALERT_STATUS_RECOVERED,
ALERT_WORKFLOW_STATUS,
ALERT_UUID,
EVENT_ACTION,
EVENT_KIND,
@ -118,7 +119,7 @@ describe('createLifecycleExecutor', () => {
);
});
it('overwrites existing documents for repeatedly firing alerts', async () => {
it('updates existing documents for repeatedly firing alerts', async () => {
const logger = loggerMock.create();
const ruleDataClientMock = createRuleDataClientMock();
ruleDataClientMock.getReader().search.mockResolvedValue({
@ -126,17 +127,35 @@ describe('createLifecycleExecutor', () => {
hits: [
{
fields: {
'@timestamp': '',
[ALERT_ID]: 'TEST_ALERT_0',
[ALERT_UUID]: 'ALERT_0_UUID',
[ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME',
[ALERT_RULE_CONSUMER]: 'CONSUMER',
[ALERT_RULE_NAME]: 'NAME',
[ALERT_RULE_PRODUCER]: 'PRODUCER',
[ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID',
labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc
[ALERT_RULE_UUID]: 'RULE_UUID',
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
[ALERT_WORKFLOW_STATUS]: 'closed',
[SPACE_IDS]: ['fake-space-id'],
labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc
},
},
{
fields: {
'@timestamp': '',
[ALERT_ID]: 'TEST_ALERT_1',
[ALERT_UUID]: 'ALERT_1_UUID',
[ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME',
[ALERT_RULE_CONSUMER]: 'CONSUMER',
[ALERT_RULE_NAME]: 'NAME',
[ALERT_RULE_PRODUCER]: 'PRODUCER',
[ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID',
[ALERT_RULE_UUID]: 'RULE_UUID',
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
[ALERT_WORKFLOW_STATUS]: 'open',
[SPACE_IDS]: ['fake-space-id'],
labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc
},
},
@ -188,14 +207,19 @@ describe('createLifecycleExecutor', () => {
{ index: { _id: 'TEST_ALERT_0_UUID' } },
expect.objectContaining({
[ALERT_ID]: 'TEST_ALERT_0',
[ALERT_WORKFLOW_STATUS]: 'closed',
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
labels: { LABEL_0_KEY: 'LABEL_0_VALUE' },
[EVENT_ACTION]: 'active',
[EVENT_KIND]: 'signal',
}),
{ index: { _id: 'TEST_ALERT_1_UUID' } },
expect.objectContaining({
[ALERT_ID]: 'TEST_ALERT_1',
[ALERT_WORKFLOW_STATUS]: 'open',
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
[EVENT_ACTION]: 'active',
[EVENT_KIND]: 'signal',
}),
@ -216,8 +240,6 @@ describe('createLifecycleExecutor', () => {
});
it('updates existing documents for recovered alerts', async () => {
// NOTE: the documents should actually also be updated for recurring,
// active alerts (see elastic/kibana#108670)
const logger = loggerMock.create();
const ruleDataClientMock = createRuleDataClientMock();
ruleDataClientMock.getReader().search.mockResolvedValue({

View file

@ -182,19 +182,17 @@ export const createLifecycleExecutor = (
const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))];
const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter(
(trackedAlertState) => !currentAlerts[trackedAlertState.alertId]
);
const trackedAlertStates = Object.values(state.trackedAlerts);
logger.debug(
`Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)`
`Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStates.length} previous)`
);
const alertsDataMap: Record<string, Partial<ParsedTechnicalFields>> = {
...currentAlerts,
};
if (trackedAlertStatesOfRecovered.length) {
if (trackedAlertStates.length) {
const { hits } = await ruleDataClient.getReader().search({
body: {
query: {
@ -207,7 +205,7 @@ export const createLifecycleExecutor = (
},
{
terms: {
[ALERT_UUID]: trackedAlertStatesOfRecovered.map(
[ALERT_UUID]: trackedAlertStates.map(
(trackedAlertState) => trackedAlertState.alertUuid
),
},
@ -215,7 +213,7 @@ export const createLifecycleExecutor = (
],
},
},
size: trackedAlertStatesOfRecovered.length,
size: trackedAlertStates.length,
collapse: {
field: ALERT_UUID,
},

View file

@ -263,6 +263,36 @@ describe('createLifecycleRuleTypeFactory', () => {
},
]);
// TODO mock the resolved value before calling alertWithLifecycle again
const lastOpbeansNodeDoc = helpers.ruleDataClientMock
.getWriter()
.bulk.mock.calls[0][0].body?.concat()
.reverse()
.find(
(doc: any) => !('index' in doc) && doc['service.name'] === 'opbeans-node'
) as Record<string, any>;
const stored = mapValues(lastOpbeansNodeDoc, (val) => {
return castArray(val);
});
helpers.ruleDataClientMock.getReader().search.mockResolvedValueOnce({
hits: {
hits: [{ fields: stored } as any],
total: {
value: 1,
relation: 'eq',
},
},
took: 0,
timed_out: false,
_shards: {
failed: 0,
successful: 1,
total: 1,
},
});
await helpers.alertWithLifecycle([
{
id: 'opbeans-java',
@ -274,6 +304,7 @@ describe('createLifecycleRuleTypeFactory', () => {
id: 'opbeans-node',
fields: {
'service.name': 'opbeans-node',
'kibana.alert.workflow_status': 'closed',
},
},
]);
@ -281,7 +312,6 @@ describe('createLifecycleRuleTypeFactory', () => {
it('writes the correct alerts', () => {
expect(helpers.ruleDataClientMock.getWriter().bulk).toHaveBeenCalledTimes(2);
const body = helpers.ruleDataClientMock.getWriter().bulk.mock.calls[1][0].body!;
const documents = body.filter((op: any) => !('index' in op)) as any[];