Skip to content

Commit a7fb669

Browse files
authored
[8.19] [Response Ops][Alerting] Adding dangerouslyCreateAlertsInAllSpaces rule type option for alert creation (elastic#224507) (elastic#224938)
# Backport This will backport the following commits from `main` to `8.19`: - [[Response Ops][Alerting] Adding `dangerouslyCreateAlertsInAllSpaces` rule type option for alert creation (elastic#224507)](elastic#224507) <!--- Backport version: 10.0.1 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Ying Mao","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-06-23T17:41:27Z","message":"[Response Ops][Alerting] Adding `dangerouslyCreateAlertsInAllSpaces` rule type option for alert creation (elastic#224507)\n\nResolves https://github.com/elastic/kibana/issues/222104\n\n## Summary\n\nAdds optional flag when registering a rule type for \"dangerously\ncreating alerts in all spaces\". If a rule type opts into this flag,\nalerts created during rule execution will persist the `kibana.space_ids`\nfield as `\"*\"` instead of the space ID of the rule. Note that we store\n`kibana.space_ids` as a string array, so the final alert document will\nhave\n\n```\n'kibana.space_ids': ['*']\n```\n\nThis PR just adds the flag and updates the code to respect the flag. It\ndoes not opt any rule types into using it. You can look at the\nfunctional tests to see example test rule types that use it.\n\nBecause the streams rule type that we expect to be the first user of\nthis flag uses the `persistenceRuleTypeWrapper` in the rule registry for\nwriting alerts, we also had to update the rule registry code.\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Elastic Machine <[email protected]>","sha":"e1b02be28b654a8a1714cf6ab19f64b5ba16048c","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Alerting","release_note:skip","Team:ResponseOps","backport:version","v9.1.0","v8.19.0"],"title":"[Response Ops][Alerting] Adding `dangerouslyCreateAlertsInAllSpaces` rule type option for alert creation","number":224507,"url":"https://github.com/elastic/kibana/pull/224507","mergeCommit":{"message":"[Response Ops][Alerting] Adding `dangerouslyCreateAlertsInAllSpaces` rule type option for alert creation (elastic#224507)\n\nResolves https://github.com/elastic/kibana/issues/222104\n\n## Summary\n\nAdds optional flag when registering a rule type for \"dangerously\ncreating alerts in all spaces\". If a rule type opts into this flag,\nalerts created during rule execution will persist the `kibana.space_ids`\nfield as `\"*\"` instead of the space ID of the rule. Note that we store\n`kibana.space_ids` as a string array, so the final alert document will\nhave\n\n```\n'kibana.space_ids': ['*']\n```\n\nThis PR just adds the flag and updates the code to respect the flag. It\ndoes not opt any rule types into using it. You can look at the\nfunctional tests to see example test rule types that use it.\n\nBecause the streams rule type that we expect to be the first user of\nthis flag uses the `persistenceRuleTypeWrapper` in the rule registry for\nwriting alerts, we also had to update the rule registry code.\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Elastic Machine <[email protected]>","sha":"e1b02be28b654a8a1714cf6ab19f64b5ba16048c"}},"sourceBranch":"main","suggestedTargetBranches":["8.19"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/224507","number":224507,"mergeCommit":{"message":"[Response Ops][Alerting] Adding `dangerouslyCreateAlertsInAllSpaces` rule type option for alert creation (elastic#224507)\n\nResolves https://github.com/elastic/kibana/issues/222104\n\n## Summary\n\nAdds optional flag when registering a rule type for \"dangerously\ncreating alerts in all spaces\". If a rule type opts into this flag,\nalerts created during rule execution will persist the `kibana.space_ids`\nfield as `\"*\"` instead of the space ID of the rule. Note that we store\n`kibana.space_ids` as a string array, so the final alert document will\nhave\n\n```\n'kibana.space_ids': ['*']\n```\n\nThis PR just adds the flag and updates the code to respect the flag. It\ndoes not opt any rule types into using it. You can look at the\nfunctional tests to see example test rule types that use it.\n\nBecause the streams rule type that we expect to be the first user of\nthis flag uses the `persistenceRuleTypeWrapper` in the rule registry for\nwriting alerts, we also had to update the rule registry code.\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Elastic Machine <[email protected]>","sha":"e1b02be28b654a8a1714cf6ab19f64b5ba16048c"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
1 parent 38ad3e8 commit a7fb669

29 files changed

+768
-37
lines changed

x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import {
6464
getMaintenanceWindowAlertsQuery,
6565
getContinualAlertsQuery,
6666
isAlertImproving,
67+
shouldCreateAlertsInAllSpaces,
6768
} from './lib';
6869
import { isValidAlertIndexName } from '../alerts_service';
6970
import { resolveAlertConflicts } from './lib/alert_conflict_resolver';
@@ -488,6 +489,11 @@ export class AlertsClient<
488489
const currentTime = this.startedAtString ?? new Date().toISOString();
489490
const esClient = await this.options.elasticsearchClientPromise;
490491

492+
const createAlertsInAllSpaces = shouldCreateAlertsInAllSpaces({
493+
ruleTypeId: this.ruleType.id,
494+
ruleTypeAlertDef: this.ruleType.alerts,
495+
logger: this.options.logger,
496+
});
491497
const { rawActiveAlerts, rawRecoveredAlerts } = this.getRawAlertInstancesForState();
492498

493499
const activeAlerts = this.legacyAlertsClient.getProcessedAlerts(ALERT_STATUS_ACTIVE);
@@ -526,6 +532,7 @@ export class AlertsClient<
526532
timestamp: currentTime,
527533
payload: this.reportedAlerts[id],
528534
kibanaVersion: this.options.kibanaVersion,
535+
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
529536
})
530537
);
531538
} else {
@@ -548,6 +555,7 @@ export class AlertsClient<
548555
timestamp: currentTime,
549556
payload: this.reportedAlerts[id],
550557
kibanaVersion: this.options.kibanaVersion,
558+
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
551559
})
552560
);
553561
}
@@ -582,6 +590,7 @@ export class AlertsClient<
582590
payload: this.reportedAlerts[id],
583591
recoveryActionGroup: this.options.ruleType.recoveryActionGroup.id,
584592
kibanaVersion: this.options.kibanaVersion,
593+
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
585594
})
586595
: buildUpdatedRecoveredAlert<AlertData>({
587596
alert: trackedAlert,

x-pack/platform/plugins/shared/alerting/server/alerts_client/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@
88
export { type LegacyAlertsClientParams, LegacyAlertsClient } from './legacy_alerts_client';
99
export { AlertsClient } from './alerts_client';
1010
export type { AlertRuleData } from './types';
11-
export { sanitizeBulkErrorResponse, initializeAlertsClient } from './lib';
11+
export {
12+
sanitizeBulkErrorResponse,
13+
initializeAlertsClient,
14+
shouldCreateAlertsInAllSpaces,
15+
} from './lib';
1216
export { AlertsClientError } from './alerts_client_error';

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_new_alert.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,41 @@ describe('buildNewAlert', () => {
105105
});
106106
});
107107

108+
test(`should set kibana.space_ids to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
109+
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A');
110+
legacyAlert.scheduleActions('default');
111+
112+
expect(
113+
buildNewAlert<{}, {}, {}, 'default', 'recovered'>({
114+
legacyAlert,
115+
rule: alertRule,
116+
timestamp: '2023-03-28T12:27:28.159Z',
117+
kibanaVersion: '8.9.0',
118+
dangerouslyCreateAlertsInAllSpaces: true,
119+
})
120+
).toEqual({
121+
...alertRule,
122+
[TIMESTAMP]: '2023-03-28T12:27:28.159Z',
123+
[EVENT_ACTION]: 'open',
124+
[EVENT_KIND]: 'signal',
125+
[ALERT_ACTION_GROUP]: 'default',
126+
[ALERT_CONSECUTIVE_MATCHES]: 0,
127+
[ALERT_FLAPPING]: false,
128+
[ALERT_FLAPPING_HISTORY]: [],
129+
[ALERT_INSTANCE_ID]: 'alert-A',
130+
[ALERT_MAINTENANCE_WINDOW_IDS]: [],
131+
[ALERT_PENDING_RECOVERED_COUNT]: 0,
132+
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-28T12:27:28.159Z',
133+
[ALERT_SEVERITY_IMPROVING]: false,
134+
[ALERT_STATUS]: 'active',
135+
[ALERT_UUID]: legacyAlert.getUuid(),
136+
[ALERT_WORKFLOW_STATUS]: 'open',
137+
[SPACE_IDS]: ['*'],
138+
[VERSION]: '8.9.0',
139+
[TAGS]: ['rule-', '-tags'],
140+
});
141+
});
142+
108143
test('should include flapping history and maintenance window ids if set', () => {
109144
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A');
110145
legacyAlert.scheduleActions('default');

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_new_alert.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ interface BuildNewAlertOpts<
5252
runTimestamp?: string;
5353
timestamp: string;
5454
kibanaVersion: string;
55+
dangerouslyCreateAlertsInAllSpaces?: boolean;
5556
}
5657

5758
/**
@@ -72,6 +73,7 @@ export const buildNewAlert = <
7273
timestamp,
7374
payload,
7475
kibanaVersion,
76+
dangerouslyCreateAlertsInAllSpaces,
7577
}: BuildNewAlertOpts<
7678
AlertData,
7779
LegacyState,
@@ -109,7 +111,7 @@ export const buildNewAlert = <
109111
[ALERT_TIME_RANGE]: { gte: legacyAlert.getState().start },
110112
}
111113
: {}),
112-
[SPACE_IDS]: rule[SPACE_IDS],
114+
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
113115
[VERSION]: kibanaVersion,
114116
[TAGS]: Array.from(
115117
new Set([...((cleanedPayload?.tags as string[]) ?? []), ...(rule[ALERT_RULE_TAGS] ?? [])])

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_ongoing_alert.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,88 @@ for (const flattened of [true, false]) {
244244
});
245245
});
246246

247+
test(`should return alert document with kibana.space_ids set to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
248+
const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', {
249+
meta: { uuid: 'abcdefg' },
250+
});
251+
legacyAlert
252+
.scheduleActions('error')
253+
.replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' });
254+
legacyAlert.setFlappingHistory([false, false, true, true]);
255+
legacyAlert.setMaintenanceWindowIds(['maint-xyz']);
256+
257+
const alert = flattened
258+
? {
259+
...existingAlert,
260+
[ALERT_FLAPPING_HISTORY]: [true, false, false, false, true, true],
261+
[ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'],
262+
}
263+
: {
264+
...existingAlert,
265+
kibana: {
266+
// @ts-expect-error
267+
...existingAlert.kibana,
268+
alert: {
269+
// @ts-expect-error
270+
...existingAlert.kibana.alert,
271+
flapping_history: [true, false, false, false, true, true],
272+
maintenance_window_ids: ['maint-1', 'maint-321'],
273+
},
274+
},
275+
};
276+
277+
expect(
278+
buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({
279+
// @ts-expect-error
280+
alert,
281+
legacyAlert,
282+
rule: alertRule,
283+
isImproving: null,
284+
timestamp: '2023-03-29T12:27:28.159Z',
285+
kibanaVersion: '8.9.0',
286+
dangerouslyCreateAlertsInAllSpaces: true,
287+
})
288+
).toEqual({
289+
...alertRule,
290+
[TIMESTAMP]: '2023-03-29T12:27:28.159Z',
291+
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-29T12:27:28.159Z',
292+
[EVENT_ACTION]: 'active',
293+
[ALERT_ACTION_GROUP]: 'error',
294+
[ALERT_CONSECUTIVE_MATCHES]: 0,
295+
[ALERT_FLAPPING]: false,
296+
[ALERT_FLAPPING_HISTORY]: [false, false, true, true],
297+
[ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-xyz'],
298+
[ALERT_PENDING_RECOVERED_COUNT]: 0,
299+
[ALERT_PREVIOUS_ACTION_GROUP]: 'error',
300+
[ALERT_STATUS]: 'active',
301+
[ALERT_WORKFLOW_STATUS]: 'open',
302+
[ALERT_DURATION]: 36000,
303+
[ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' },
304+
[SPACE_IDS]: ['*'],
305+
[VERSION]: '8.9.0',
306+
[TAGS]: ['rule-', '-tags'],
307+
...(flattened
308+
? {
309+
[EVENT_KIND]: 'signal',
310+
[ALERT_INSTANCE_ID]: 'alert-A',
311+
[ALERT_START]: '2023-03-28T12:27:28.159Z',
312+
[ALERT_UUID]: 'abcdefg',
313+
}
314+
: {
315+
event: {
316+
kind: 'signal',
317+
},
318+
kibana: {
319+
alert: {
320+
instance: { id: 'alert-A' },
321+
start: '2023-03-28T12:27:28.159Z',
322+
uuid: 'abcdefg',
323+
},
324+
},
325+
}),
326+
});
327+
});
328+
247329
test('should return alert document with updated isImproving', () => {
248330
const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', {
249331
meta: { uuid: 'abcdefg' },

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_ongoing_alert.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ interface BuildOngoingAlertOpts<
5050
runTimestamp?: string;
5151
timestamp: string;
5252
kibanaVersion: string;
53+
dangerouslyCreateAlertsInAllSpaces?: boolean;
5354
}
5455

5556
/**
@@ -72,6 +73,7 @@ export const buildOngoingAlert = <
7273
runTimestamp,
7374
timestamp,
7475
kibanaVersion,
76+
dangerouslyCreateAlertsInAllSpaces,
7577
}: BuildOngoingAlertOpts<
7678
AlertData,
7779
LegacyState,
@@ -122,7 +124,7 @@ export const buildOngoingAlert = <
122124
: {}),
123125
...(isImproving != null ? { [ALERT_SEVERITY_IMPROVING]: isImproving } : {}),
124126
[ALERT_PREVIOUS_ACTION_GROUP]: get(alert, ALERT_ACTION_GROUP),
125-
[SPACE_IDS]: rule[SPACE_IDS],
127+
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
126128
[VERSION]: kibanaVersion,
127129
[TAGS]: Array.from(
128130
new Set([

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_recovered_alert.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,74 @@ for (const flattened of [true, false]) {
185185
});
186186
});
187187

188+
test(`should return alert document with kibana.space_ids set to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
189+
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', {
190+
meta: { uuid: 'abcdefg' },
191+
});
192+
legacyAlert.scheduleActions('default').replaceState({
193+
start: '2023-03-28T12:27:28.159Z',
194+
end: '2023-03-30T12:27:28.159Z',
195+
duration: '36000000',
196+
});
197+
198+
expect(
199+
buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({
200+
// @ts-expect-error
201+
alert: existingAlert,
202+
legacyAlert,
203+
rule: alertRule,
204+
recoveryActionGroup: 'recovered',
205+
timestamp: '2023-03-29T12:27:28.159Z',
206+
kibanaVersion: '8.9.0',
207+
dangerouslyCreateAlertsInAllSpaces: true,
208+
})
209+
).toEqual({
210+
[TIMESTAMP]: '2023-03-29T12:27:28.159Z',
211+
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-29T12:27:28.159Z',
212+
// @ts-ignore
213+
[ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28',
214+
[EVENT_ACTION]: 'close',
215+
[ALERT_ACTION_GROUP]: 'recovered',
216+
[ALERT_CONSECUTIVE_MATCHES]: 0,
217+
[ALERT_FLAPPING]: false,
218+
[ALERT_FLAPPING_HISTORY]: [],
219+
[ALERT_SEVERITY_IMPROVING]: true,
220+
[ALERT_PREVIOUS_ACTION_GROUP]: 'default',
221+
[ALERT_MAINTENANCE_WINDOW_IDS]: [],
222+
[ALERT_PENDING_RECOVERED_COUNT]: 0,
223+
[ALERT_STATUS]: 'recovered',
224+
[ALERT_WORKFLOW_STATUS]: 'open',
225+
[ALERT_DURATION]: 36000,
226+
[ALERT_START]: '2023-03-28T12:27:28.159Z',
227+
[ALERT_END]: '2023-03-30T12:27:28.159Z',
228+
[ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' },
229+
// @ts-expect-error
230+
[SPACE_IDS]: ['*'],
231+
[VERSION]: '8.9.0',
232+
[TAGS]: ['rule-', '-tags'],
233+
...(flattened
234+
? {
235+
...alertRule,
236+
[EVENT_KIND]: 'signal',
237+
[ALERT_INSTANCE_ID]: 'alert-A',
238+
[ALERT_UUID]: 'abcdefg',
239+
[SPACE_IDS]: ['*'],
240+
}
241+
: {
242+
event: {
243+
kind: 'signal',
244+
},
245+
kibana: {
246+
alert: {
247+
instance: { id: 'alert-A' },
248+
rule: omit(rule, 'execution'),
249+
uuid: 'abcdefg',
250+
},
251+
},
252+
}),
253+
});
254+
});
255+
188256
test('should return alert document with updated payload if specified', () => {
189257
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', {
190258
meta: { uuid: 'abcdefg' },

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/build_recovered_alert.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface BuildRecoveredAlertOpts<
5454
payload?: DeepPartial<AlertData>;
5555
timestamp: string;
5656
kibanaVersion: string;
57+
dangerouslyCreateAlertsInAllSpaces?: boolean;
5758
}
5859

5960
/**
@@ -76,6 +77,7 @@ export const buildRecoveredAlert = <
7677
runTimestamp,
7778
recoveryActionGroup,
7879
kibanaVersion,
80+
dangerouslyCreateAlertsInAllSpaces,
7981
}: BuildRecoveredAlertOpts<
8082
AlertData,
8183
LegacyState,
@@ -126,7 +128,7 @@ export const buildRecoveredAlert = <
126128
}
127129
: {}),
128130

129-
[SPACE_IDS]: rule[SPACE_IDS],
131+
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
130132
// Set latest kibana version
131133
[VERSION]: kibanaVersion,
132134
[TAGS]: Array.from(

x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ export { expandFlattenedAlert } from './format_alert';
2020
export { sanitizeBulkErrorResponse } from './sanitize_bulk_response';
2121
export { initializeAlertsClient } from './initialize_alerts_client';
2222
export { isAlertImproving } from './is_alert_improving';
23+
export { shouldCreateAlertsInAllSpaces } from './should_create_alerts_in_all_spaces';

0 commit comments

Comments
 (0)