Skip to content

Commit

Permalink
[Response Ops][Alerting] Initial implementation of FAAD `AlertsClient…
Browse files Browse the repository at this point in the history
…` for writing generic AAD documents (#156946)

Resolves #156442

## Summary

1. Adds `shouldWriteAlerts` flag to rule type registration which
defaults to `false` if not set. This prevents duplicate AAD documents
from being written for the rule registry rule types that had to register
with the framework in order to get their resources installed on startup.
2. Initial implementation of `AlertsClient` which primarily functions as
a proxy to the `LegacyAlertsClient`. It does 2 additional thing:
a. When initialized with the active & recovered alerts from the previous
execution (de-serialized from the task manager state), it queries the
AAD index for the corresponding alert document.
b. When returning the alerts to serialize into the task manager state,
it builds the alert document and bulk upserts into the AAD index.

This PR does not opt any rule types into writing these generic docs but
adds an example functional test that does. To test it out with the ES
query rule type, add the following

```
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts
index 214d2ee4b76..0439a576b03 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts
@@ -187,5 +187,12 @@ export function getRuleType(
     },
     producer: STACK_ALERTS_FEATURE_ID,
     doesSetRecoveryContext: true,
+    alerts: {
+      context: 'stack',
+      shouldWrite: true,
+      mappings: {
+        fieldMap: {},
+      },
+    },
   };
 }
```

## To Verify
- Verify that rule registry rule types still work as expected
- Verify that non rule-registry rule types still work as expected
- Modify a rule type to register with FAAD and write alerts and verify
that the alert documents look as expected.

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
ymao1 and kibanamachine authored May 24, 2023
1 parent e77b45f commit 282305f
Show file tree
Hide file tree
Showing 43 changed files with 3,932 additions and 320 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/alerting/common/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { RuleSnooze } from './rule_snooze_type';
export type RuleTypeState = Record<string, unknown>;
export type RuleTypeParams = Record<string, unknown>;

// rule type defined alert fields to persist in alerts index
export type RuleAlertData = Record<string, unknown>;

export interface IntervalSchedule extends SavedObjectAttributes {
interval: string;
}
Expand Down
19 changes: 0 additions & 19 deletions x-pack/plugins/alerting/server/alert/create_alert_factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand Down Expand Up @@ -59,7 +58,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand All @@ -84,7 +82,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
alertFactory.create('1');
expect(alerts).toMatchObject({
Expand All @@ -106,7 +103,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 3,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});

expect(alertFactory.hasReachedAlertLimit()).toBe(false);
Expand All @@ -127,7 +123,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand Down Expand Up @@ -171,7 +166,6 @@ describe('createAlertFactory()', () => {
canSetRecoveryContext: true,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: ['test-id-1'],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand All @@ -190,11 +184,6 @@ describe('createAlertFactory()', () => {
const recoveredAlerts = getRecoveredAlertsFn!();
expect(Array.isArray(recoveredAlerts)).toBe(true);
expect(recoveredAlerts.length).toEqual(2);
expect(processAlerts).toHaveBeenLastCalledWith(
expect.objectContaining({
maintenanceWindowIds: ['test-id-1'],
})
);
});

test('returns empty array if no recovered alerts', () => {
Expand All @@ -205,7 +194,6 @@ describe('createAlertFactory()', () => {
maxAlerts: 1000,
canSetRecoveryContext: true,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand Down Expand Up @@ -233,7 +221,6 @@ describe('createAlertFactory()', () => {
maxAlerts: 1000,
canSetRecoveryContext: true,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand All @@ -260,7 +247,6 @@ describe('createAlertFactory()', () => {
maxAlerts: 1000,
canSetRecoveryContext: false,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toMatchObject({
Expand Down Expand Up @@ -289,7 +275,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});

const limit = alertFactory.alertLimit.getValue();
Expand All @@ -308,7 +293,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});

const limit = alertFactory.alertLimit.getValue();
Expand All @@ -324,7 +308,6 @@ describe('createAlertFactory()', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});

const limit = alertFactory.alertLimit.getValue();
Expand All @@ -341,7 +324,6 @@ describe('createAlertFactory()', () => {
maxAlerts: 1000,
canSetRecoveryContext: true,
autoRecoverAlerts: false,
maintenanceWindowIds: [],
});
const result = alertFactory.create('1');
expect(result).toEqual({
Expand Down Expand Up @@ -373,7 +355,6 @@ describe('getPublicAlertFactory', () => {
logger,
maxAlerts: 1000,
autoRecoverAlerts: true,
maintenanceWindowIds: [],
});

expect(alertFactory.create).toBeDefined();
Expand Down
5 changes: 2 additions & 3 deletions x-pack/plugins/alerting/server/alert/create_alert_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export interface CreateAlertFactoryOpts<
logger: Logger;
maxAlerts: number;
autoRecoverAlerts: boolean;
maintenanceWindowIds: string[];
canSetRecoveryContext?: boolean;
}

Expand All @@ -67,7 +66,6 @@ export function createAlertFactory<
logger,
maxAlerts,
autoRecoverAlerts,
maintenanceWindowIds,
canSetRecoveryContext = false,
}: CreateAlertFactoryOpts<State, Context>): AlertFactory<State, Context, ActionGroupIds> {
// Keep track of which alerts we started with so we can determine which have recovered
Expand Down Expand Up @@ -154,7 +152,8 @@ export function createAlertFactory<
autoRecoverAlerts,
// flappingSettings.enabled is false, as we only want to use this function to get the recovered alerts
flappingSettings: DISABLE_FLAPPING_SETTINGS,
maintenanceWindowIds,
// no maintenance window IDs are passed as we only want to use this function to get recovered alerts
maintenanceWindowIds: [],
});
return Object.keys(currentRecoveredAlerts ?? {}).map(
(alertId: string) => currentRecoveredAlerts[alertId]
Expand Down
23 changes: 23 additions & 0 deletions x-pack/plugins/alerting/server/alerts_client/alerts_client.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
const createAlertsClientMock = () => {
return jest.fn().mockImplementation(() => {
return {
processAndLogAlerts: jest.fn(),
getTrackedAlerts: jest.fn(),
getProcessedAlerts: jest.fn(),
getAlertsToSerialize: jest.fn(),
hasReachedAlertLimit: jest.fn(),
checkLimitUsage: jest.fn(),
getExecutorServices: jest.fn(),
};
});
};

export const alertsClientMock = {
create: createAlertsClientMock(),
};
Loading

0 comments on commit 282305f

Please sign in to comment.