mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Response Ops][Alerting] Adding dangerouslyCreateAlertsInAllSpaces
rule type option for alert creation (#224507)
Resolves https://github.com/elastic/kibana/issues/222104 ## Summary Adds optional flag when registering a rule type for "dangerously creating alerts in all spaces". If a rule type opts into this flag, alerts created during rule execution will persist the `kibana.space_ids` field as `"*"` instead of the space ID of the rule. Note that we store `kibana.space_ids` as a string array, so the final alert document will have ``` 'kibana.space_ids': ['*'] ``` This PR just adds the flag and updates the code to respect the flag. It does not opt any rule types into using it. You can look at the functional tests to see example test rule types that use it. Because the streams rule type that we expect to be the first user of this flag uses the `persistenceRuleTypeWrapper` in the rule registry for writing alerts, we also had to update the rule registry code. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
3af496d11a
commit
e1b02be28b
29 changed files with 768 additions and 37 deletions
|
@ -64,6 +64,7 @@ import {
|
|||
getMaintenanceWindowAlertsQuery,
|
||||
getContinualAlertsQuery,
|
||||
isAlertImproving,
|
||||
shouldCreateAlertsInAllSpaces,
|
||||
} from './lib';
|
||||
import { isValidAlertIndexName } from '../alerts_service';
|
||||
import { resolveAlertConflicts } from './lib/alert_conflict_resolver';
|
||||
|
@ -488,6 +489,11 @@ export class AlertsClient<
|
|||
const currentTime = this.startedAtString ?? new Date().toISOString();
|
||||
const esClient = await this.options.elasticsearchClientPromise;
|
||||
|
||||
const createAlertsInAllSpaces = shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: this.ruleType.id,
|
||||
ruleTypeAlertDef: this.ruleType.alerts,
|
||||
logger: this.options.logger,
|
||||
});
|
||||
const { rawActiveAlerts, rawRecoveredAlerts } = this.getRawAlertInstancesForState();
|
||||
|
||||
const activeAlerts = this.legacyAlertsClient.getProcessedAlerts(ALERT_STATUS_ACTIVE);
|
||||
|
@ -526,6 +532,7 @@ export class AlertsClient<
|
|||
timestamp: currentTime,
|
||||
payload: this.reportedAlerts[id],
|
||||
kibanaVersion: this.options.kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
@ -548,6 +555,7 @@ export class AlertsClient<
|
|||
timestamp: currentTime,
|
||||
payload: this.reportedAlerts[id],
|
||||
kibanaVersion: this.options.kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -582,6 +590,7 @@ export class AlertsClient<
|
|||
payload: this.reportedAlerts[id],
|
||||
recoveryActionGroup: this.options.ruleType.recoveryActionGroup.id,
|
||||
kibanaVersion: this.options.kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
|
||||
})
|
||||
: buildUpdatedRecoveredAlert<AlertData>({
|
||||
alert: trackedAlert,
|
||||
|
|
|
@ -8,5 +8,9 @@
|
|||
export { type LegacyAlertsClientParams, LegacyAlertsClient } from './legacy_alerts_client';
|
||||
export { AlertsClient } from './alerts_client';
|
||||
export type { AlertRuleData } from './types';
|
||||
export { sanitizeBulkErrorResponse, initializeAlertsClient } from './lib';
|
||||
export {
|
||||
sanitizeBulkErrorResponse,
|
||||
initializeAlertsClient,
|
||||
shouldCreateAlertsInAllSpaces,
|
||||
} from './lib';
|
||||
export { AlertsClientError } from './alerts_client_error';
|
||||
|
|
|
@ -105,6 +105,41 @@ describe('buildNewAlert', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test(`should set kibana.space_ids to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A');
|
||||
legacyAlert.scheduleActions('default');
|
||||
|
||||
expect(
|
||||
buildNewAlert<{}, {}, {}, 'default', 'recovered'>({
|
||||
legacyAlert,
|
||||
rule: alertRule,
|
||||
timestamp: '2023-03-28T12:27:28.159Z',
|
||||
kibanaVersion: '8.9.0',
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
})
|
||||
).toEqual({
|
||||
...alertRule,
|
||||
[TIMESTAMP]: '2023-03-28T12:27:28.159Z',
|
||||
[EVENT_ACTION]: 'open',
|
||||
[EVENT_KIND]: 'signal',
|
||||
[ALERT_ACTION_GROUP]: 'default',
|
||||
[ALERT_CONSECUTIVE_MATCHES]: 0,
|
||||
[ALERT_FLAPPING]: false,
|
||||
[ALERT_FLAPPING_HISTORY]: [],
|
||||
[ALERT_INSTANCE_ID]: 'alert-A',
|
||||
[ALERT_MAINTENANCE_WINDOW_IDS]: [],
|
||||
[ALERT_PENDING_RECOVERED_COUNT]: 0,
|
||||
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-28T12:27:28.159Z',
|
||||
[ALERT_SEVERITY_IMPROVING]: false,
|
||||
[ALERT_STATUS]: 'active',
|
||||
[ALERT_UUID]: legacyAlert.getUuid(),
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[SPACE_IDS]: ['*'],
|
||||
[VERSION]: '8.9.0',
|
||||
[TAGS]: ['rule-', '-tags'],
|
||||
});
|
||||
});
|
||||
|
||||
test('should include flapping history and maintenance window ids if set', () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A');
|
||||
legacyAlert.scheduleActions('default');
|
||||
|
|
|
@ -52,6 +52,7 @@ interface BuildNewAlertOpts<
|
|||
runTimestamp?: string;
|
||||
timestamp: string;
|
||||
kibanaVersion: string;
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,7 @@ export const buildNewAlert = <
|
|||
timestamp,
|
||||
payload,
|
||||
kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces,
|
||||
}: BuildNewAlertOpts<
|
||||
AlertData,
|
||||
LegacyState,
|
||||
|
@ -109,7 +111,7 @@ export const buildNewAlert = <
|
|||
[ALERT_TIME_RANGE]: { gte: legacyAlert.getState().start },
|
||||
}
|
||||
: {}),
|
||||
[SPACE_IDS]: rule[SPACE_IDS],
|
||||
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
|
||||
[VERSION]: kibanaVersion,
|
||||
[TAGS]: Array.from(
|
||||
new Set([...((cleanedPayload?.tags as string[]) ?? []), ...(rule[ALERT_RULE_TAGS] ?? [])])
|
||||
|
|
|
@ -244,6 +244,88 @@ for (const flattened of [true, false]) {
|
|||
});
|
||||
});
|
||||
|
||||
test(`should return alert document with kibana.space_ids set to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', {
|
||||
meta: { uuid: 'abcdefg' },
|
||||
});
|
||||
legacyAlert
|
||||
.scheduleActions('error')
|
||||
.replaceState({ start: '2023-03-28T12:27:28.159Z', duration: '36000000' });
|
||||
legacyAlert.setFlappingHistory([false, false, true, true]);
|
||||
legacyAlert.setMaintenanceWindowIds(['maint-xyz']);
|
||||
|
||||
const alert = flattened
|
||||
? {
|
||||
...existingAlert,
|
||||
[ALERT_FLAPPING_HISTORY]: [true, false, false, false, true, true],
|
||||
[ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-1', 'maint-321'],
|
||||
}
|
||||
: {
|
||||
...existingAlert,
|
||||
kibana: {
|
||||
// @ts-expect-error
|
||||
...existingAlert.kibana,
|
||||
alert: {
|
||||
// @ts-expect-error
|
||||
...existingAlert.kibana.alert,
|
||||
flapping_history: [true, false, false, false, true, true],
|
||||
maintenance_window_ids: ['maint-1', 'maint-321'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
buildOngoingAlert<{}, {}, {}, 'error' | 'warning', 'recovered'>({
|
||||
// @ts-expect-error
|
||||
alert,
|
||||
legacyAlert,
|
||||
rule: alertRule,
|
||||
isImproving: null,
|
||||
timestamp: '2023-03-29T12:27:28.159Z',
|
||||
kibanaVersion: '8.9.0',
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
})
|
||||
).toEqual({
|
||||
...alertRule,
|
||||
[TIMESTAMP]: '2023-03-29T12:27:28.159Z',
|
||||
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-29T12:27:28.159Z',
|
||||
[EVENT_ACTION]: 'active',
|
||||
[ALERT_ACTION_GROUP]: 'error',
|
||||
[ALERT_CONSECUTIVE_MATCHES]: 0,
|
||||
[ALERT_FLAPPING]: false,
|
||||
[ALERT_FLAPPING_HISTORY]: [false, false, true, true],
|
||||
[ALERT_MAINTENANCE_WINDOW_IDS]: ['maint-xyz'],
|
||||
[ALERT_PENDING_RECOVERED_COUNT]: 0,
|
||||
[ALERT_PREVIOUS_ACTION_GROUP]: 'error',
|
||||
[ALERT_STATUS]: 'active',
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DURATION]: 36000,
|
||||
[ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z' },
|
||||
[SPACE_IDS]: ['*'],
|
||||
[VERSION]: '8.9.0',
|
||||
[TAGS]: ['rule-', '-tags'],
|
||||
...(flattened
|
||||
? {
|
||||
[EVENT_KIND]: 'signal',
|
||||
[ALERT_INSTANCE_ID]: 'alert-A',
|
||||
[ALERT_START]: '2023-03-28T12:27:28.159Z',
|
||||
[ALERT_UUID]: 'abcdefg',
|
||||
}
|
||||
: {
|
||||
event: {
|
||||
kind: 'signal',
|
||||
},
|
||||
kibana: {
|
||||
alert: {
|
||||
instance: { id: 'alert-A' },
|
||||
start: '2023-03-28T12:27:28.159Z',
|
||||
uuid: 'abcdefg',
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test('should return alert document with updated isImproving', () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'error' | 'warning'>('alert-A', {
|
||||
meta: { uuid: 'abcdefg' },
|
||||
|
|
|
@ -50,6 +50,7 @@ interface BuildOngoingAlertOpts<
|
|||
runTimestamp?: string;
|
||||
timestamp: string;
|
||||
kibanaVersion: string;
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,7 @@ export const buildOngoingAlert = <
|
|||
runTimestamp,
|
||||
timestamp,
|
||||
kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces,
|
||||
}: BuildOngoingAlertOpts<
|
||||
AlertData,
|
||||
LegacyState,
|
||||
|
@ -122,7 +124,7 @@ export const buildOngoingAlert = <
|
|||
: {}),
|
||||
...(isImproving != null ? { [ALERT_SEVERITY_IMPROVING]: isImproving } : {}),
|
||||
[ALERT_PREVIOUS_ACTION_GROUP]: get(alert, ALERT_ACTION_GROUP),
|
||||
[SPACE_IDS]: rule[SPACE_IDS],
|
||||
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
|
||||
[VERSION]: kibanaVersion,
|
||||
[TAGS]: Array.from(
|
||||
new Set([
|
||||
|
|
|
@ -185,6 +185,74 @@ for (const flattened of [true, false]) {
|
|||
});
|
||||
});
|
||||
|
||||
test(`should return alert document with kibana.space_ids set to '*' if dangerouslyCreateAlertsInAllSpaces=true`, () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', {
|
||||
meta: { uuid: 'abcdefg' },
|
||||
});
|
||||
legacyAlert.scheduleActions('default').replaceState({
|
||||
start: '2023-03-28T12:27:28.159Z',
|
||||
end: '2023-03-30T12:27:28.159Z',
|
||||
duration: '36000000',
|
||||
});
|
||||
|
||||
expect(
|
||||
buildRecoveredAlert<{}, {}, {}, 'default', 'recovered'>({
|
||||
// @ts-expect-error
|
||||
alert: existingAlert,
|
||||
legacyAlert,
|
||||
rule: alertRule,
|
||||
recoveryActionGroup: 'recovered',
|
||||
timestamp: '2023-03-29T12:27:28.159Z',
|
||||
kibanaVersion: '8.9.0',
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
})
|
||||
).toEqual({
|
||||
[TIMESTAMP]: '2023-03-29T12:27:28.159Z',
|
||||
[ALERT_RULE_EXECUTION_TIMESTAMP]: '2023-03-29T12:27:28.159Z',
|
||||
// @ts-ignore
|
||||
[ALERT_RULE_EXECUTION_UUID]: '5f6aa57d-3e22-484e-bae8-cbed868f4d28',
|
||||
[EVENT_ACTION]: 'close',
|
||||
[ALERT_ACTION_GROUP]: 'recovered',
|
||||
[ALERT_CONSECUTIVE_MATCHES]: 0,
|
||||
[ALERT_FLAPPING]: false,
|
||||
[ALERT_FLAPPING_HISTORY]: [],
|
||||
[ALERT_SEVERITY_IMPROVING]: true,
|
||||
[ALERT_PREVIOUS_ACTION_GROUP]: 'default',
|
||||
[ALERT_MAINTENANCE_WINDOW_IDS]: [],
|
||||
[ALERT_PENDING_RECOVERED_COUNT]: 0,
|
||||
[ALERT_STATUS]: 'recovered',
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DURATION]: 36000,
|
||||
[ALERT_START]: '2023-03-28T12:27:28.159Z',
|
||||
[ALERT_END]: '2023-03-30T12:27:28.159Z',
|
||||
[ALERT_TIME_RANGE]: { gte: '2023-03-28T12:27:28.159Z', lte: '2023-03-30T12:27:28.159Z' },
|
||||
// @ts-expect-error
|
||||
[SPACE_IDS]: ['*'],
|
||||
[VERSION]: '8.9.0',
|
||||
[TAGS]: ['rule-', '-tags'],
|
||||
...(flattened
|
||||
? {
|
||||
...alertRule,
|
||||
[EVENT_KIND]: 'signal',
|
||||
[ALERT_INSTANCE_ID]: 'alert-A',
|
||||
[ALERT_UUID]: 'abcdefg',
|
||||
[SPACE_IDS]: ['*'],
|
||||
}
|
||||
: {
|
||||
event: {
|
||||
kind: 'signal',
|
||||
},
|
||||
kibana: {
|
||||
alert: {
|
||||
instance: { id: 'alert-A' },
|
||||
rule: omit(rule, 'execution'),
|
||||
uuid: 'abcdefg',
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test('should return alert document with updated payload if specified', () => {
|
||||
const legacyAlert = new LegacyAlert<{}, {}, 'default'>('alert-A', {
|
||||
meta: { uuid: 'abcdefg' },
|
||||
|
|
|
@ -54,6 +54,7 @@ interface BuildRecoveredAlertOpts<
|
|||
payload?: DeepPartial<AlertData>;
|
||||
timestamp: string;
|
||||
kibanaVersion: string;
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,6 +77,7 @@ export const buildRecoveredAlert = <
|
|||
runTimestamp,
|
||||
recoveryActionGroup,
|
||||
kibanaVersion,
|
||||
dangerouslyCreateAlertsInAllSpaces,
|
||||
}: BuildRecoveredAlertOpts<
|
||||
AlertData,
|
||||
LegacyState,
|
||||
|
@ -126,7 +128,7 @@ export const buildRecoveredAlert = <
|
|||
}
|
||||
: {}),
|
||||
|
||||
[SPACE_IDS]: rule[SPACE_IDS],
|
||||
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : rule[SPACE_IDS],
|
||||
// Set latest kibana version
|
||||
[VERSION]: kibanaVersion,
|
||||
[TAGS]: Array.from(
|
||||
|
|
|
@ -20,3 +20,4 @@ export { expandFlattenedAlert } from './format_alert';
|
|||
export { sanitizeBulkErrorResponse } from './sanitize_bulk_response';
|
||||
export { initializeAlertsClient } from './initialize_alerts_client';
|
||||
export { isAlertImproving } from './is_alert_improving';
|
||||
export { shouldCreateAlertsInAllSpaces } from './should_create_alerts_in_all_spaces';
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { shouldCreateAlertsInAllSpaces } from './should_create_alerts_in_all_spaces';
|
||||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
|
||||
const logger = loggingSystemMock.createLogger();
|
||||
|
||||
describe('shouldCreateAlertsInAllSpaces', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('returns false if alert definition is undefined', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
logger,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
it('returns false if dangerouslyCreateAlertsInAllSpaces is undefined', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
ruleTypeAlertDef: {
|
||||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
shouldWrite: true,
|
||||
isSpaceAware: true,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns false if dangerouslyCreateAlertsInAllSpaces is false', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
ruleTypeAlertDef: {
|
||||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
shouldWrite: true,
|
||||
dangerouslyCreateAlertsInAllSpaces: false,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
it('returns true if dangerouslyCreateAlertsInAllSpaces is true and isSpaceAware is undefined', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
ruleTypeAlertDef: {
|
||||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
shouldWrite: true,
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
).toBe(true);
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
it('returns true if dangerouslyCreateAlertsInAllSpaces is true and isSpaceAware is false', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
ruleTypeAlertDef: {
|
||||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
shouldWrite: true,
|
||||
isSpaceAware: false,
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
).toBe(true);
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
it('returns false and logs warning if dangerouslyCreateAlertsInAllSpaces is true and isSpaceAware is true', () => {
|
||||
expect(
|
||||
shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: 'test.rule-type',
|
||||
ruleTypeAlertDef: {
|
||||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
shouldWrite: true,
|
||||
isSpaceAware: true,
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
`Rule type \"test.rule-type\" is space aware but also has \"dangerouslyCreateAlertsInAllSpaces\" set to true. This is not supported so alerts will be created with the space ID of the rule.`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { UntypedRuleTypeAlerts } from '../../types';
|
||||
|
||||
interface ShouldCreateAlertsInAllSpacesOpts {
|
||||
ruleTypeId: string;
|
||||
ruleTypeAlertDef?: UntypedRuleTypeAlerts;
|
||||
logger: Logger;
|
||||
}
|
||||
export const shouldCreateAlertsInAllSpaces = ({
|
||||
ruleTypeId,
|
||||
ruleTypeAlertDef,
|
||||
logger,
|
||||
}: ShouldCreateAlertsInAllSpacesOpts): boolean => {
|
||||
const dangerouslyCreateAlertsInAllSpaces = ruleTypeAlertDef?.dangerouslyCreateAlertsInAllSpaces;
|
||||
const isSpaceAware = ruleTypeAlertDef?.isSpaceAware;
|
||||
|
||||
if (dangerouslyCreateAlertsInAllSpaces === true) {
|
||||
if (isSpaceAware === true) {
|
||||
logger.warn(
|
||||
`Rule type "${ruleTypeId}" is space aware but also has "dangerouslyCreateAlertsInAllSpaces" set to true. This is not supported so alerts will be created with the space ID of the rule.`
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
// alerts will be created for all spaces
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
|
@ -70,7 +70,11 @@ export {
|
|||
isValidAlertIndexName,
|
||||
InstallShutdownError,
|
||||
} from './alerts_service';
|
||||
export { sanitizeBulkErrorResponse, AlertsClientError } from './alerts_client';
|
||||
export {
|
||||
sanitizeBulkErrorResponse,
|
||||
AlertsClientError,
|
||||
shouldCreateAlertsInAllSpaces,
|
||||
} from './alerts_client';
|
||||
export { getDataStreamAdapter } from './alerts_service/lib/data_stream_adapter';
|
||||
export type { ConnectorAdapter } from './connector_adapters/types';
|
||||
|
||||
|
|
|
@ -273,6 +273,12 @@ export interface IRuleTypeAlerts<AlertData extends RuleAlertData = never> {
|
|||
*/
|
||||
isSpaceAware?: boolean;
|
||||
|
||||
/**
|
||||
* Optional flag to indicate that these alerts should not be space aware. When set
|
||||
* to true, alerts for this rule type will be created with the `*` space id.
|
||||
*/
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean;
|
||||
|
||||
/**
|
||||
* Optional secondary alias to use. This alias should not include the namespace.
|
||||
*/
|
||||
|
@ -363,6 +369,8 @@ export type UntypedRuleType = RuleType<
|
|||
AlertInstanceContext
|
||||
>;
|
||||
|
||||
export type UntypedRuleTypeAlerts = IRuleTypeAlerts<RuleAlertData>;
|
||||
|
||||
export interface RuleMeta extends SavedObjectAttributes {
|
||||
versionApiKeyLastmodified?: string;
|
||||
}
|
||||
|
|
|
@ -113,8 +113,11 @@ describe('AlertsClient', () => {
|
|||
"type": "function",
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "space-1",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"space-1",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
|
@ -204,8 +207,11 @@ describe('AlertsClient', () => {
|
|||
"type": "function",
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "space-1",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"space-1",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
|
|
|
@ -304,8 +304,11 @@ describe('find()', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "test_default_space_id",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"test_default_space_id",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
|
@ -524,8 +527,11 @@ describe('find()', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "test_default_space_id",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"test_default_space_id",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -149,8 +149,11 @@ describe('get()', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "test_default_space_id",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"test_default_space_id",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -251,8 +251,11 @@ describe('getAlertSummary()', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"kibana.space_ids": "test_default_space_id",
|
||||
"terms": Object {
|
||||
"kibana.space_ids": Array [
|
||||
"test_default_space_id",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
|
|
|
@ -8,8 +8,8 @@ import { getSpacesFilter } from '.';
|
|||
describe('getSpacesFilter()', () => {
|
||||
it('should return a spaces filter', () => {
|
||||
expect(getSpacesFilter('1')).toStrictEqual({
|
||||
term: {
|
||||
'kibana.space_ids': '1',
|
||||
terms: {
|
||||
'kibana.space_ids': ['1', '*'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
import { SPACE_IDS } from '../../common/technical_rule_data_field_names';
|
||||
|
||||
export function getSpacesFilter(spaceId?: string) {
|
||||
return spaceId ? { term: { [SPACE_IDS]: spaceId } } : undefined;
|
||||
return spaceId ? { terms: { [SPACE_IDS]: [spaceId, '*'] } } : undefined;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
import { sortBy } from 'lodash';
|
||||
import dateMath from '@elastic/datemath';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import type { RuleExecutorOptions } from '@kbn/alerting-plugin/server';
|
||||
import {
|
||||
shouldCreateAlertsInAllSpaces,
|
||||
type RuleExecutorOptions,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { chunk, partition } from 'lodash';
|
||||
import {
|
||||
ALERT_INSTANCE_ID,
|
||||
|
@ -28,6 +31,7 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
import { mapKeys, snakeCase } from 'lodash/fp';
|
||||
|
||||
import type { UntypedRuleTypeAlerts } from '@kbn/alerting-plugin/server/types';
|
||||
import type { IRuleDataClient } from '..';
|
||||
import { getCommonAlertFields } from './get_common_alert_fields';
|
||||
import type { CreatePersistenceRuleTypeWrapper } from './persistence_types';
|
||||
|
@ -56,13 +60,15 @@ const augmentAlerts = async <T>({
|
|||
options,
|
||||
kibanaVersion,
|
||||
currentTimeOverride,
|
||||
dangerouslyCreateAlertsInAllSpaces,
|
||||
}: {
|
||||
alerts: Array<{ _id: string; _source: T }>;
|
||||
options: RuleExecutorOptions<any, any, any, any, any>;
|
||||
kibanaVersion: string;
|
||||
currentTimeOverride: Date | undefined;
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean;
|
||||
}) => {
|
||||
const commonRuleFields = getCommonAlertFields(options);
|
||||
const commonRuleFields = getCommonAlertFields(options, dangerouslyCreateAlertsInAllSpaces);
|
||||
const maintenanceWindowIds: string[] =
|
||||
alerts.length > 0 ? await options.services.getMaintenanceWindowIds() : [];
|
||||
|
||||
|
@ -246,6 +252,11 @@ export const getUpdatedSuppressionBoundaries = <T extends SuppressionBoundaries>
|
|||
export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper =
|
||||
({ logger, ruleDataClient, formatAlert }) =>
|
||||
(type) => {
|
||||
const createAlertsInAllSpaces = shouldCreateAlertsInAllSpaces({
|
||||
ruleTypeId: type.id,
|
||||
ruleTypeAlertDef: type.alerts as unknown as UntypedRuleTypeAlerts,
|
||||
logger,
|
||||
});
|
||||
return {
|
||||
...type,
|
||||
executor: async (options) => {
|
||||
|
@ -307,6 +318,7 @@ export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper
|
|||
options,
|
||||
kibanaVersion: ruleDataClient.kibanaVersion,
|
||||
currentTimeOverride: undefined,
|
||||
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
|
||||
});
|
||||
|
||||
const response = await ruleDataClientWriter.bulk({
|
||||
|
@ -573,6 +585,7 @@ export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper
|
|||
options,
|
||||
kibanaVersion: ruleDataClient.kibanaVersion,
|
||||
currentTimeOverride,
|
||||
dangerouslyCreateAlertsInAllSpaces: createAlertsInAllSpaces,
|
||||
});
|
||||
|
||||
const bulkResponse = await ruleDataClientWriter.bulk({
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import {
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_CATEGORY,
|
||||
ALERT_RULE_CONSUMER,
|
||||
ALERT_RULE_EXECUTION_UUID,
|
||||
ALERT_RULE_NAME,
|
||||
ALERT_RULE_PRODUCER,
|
||||
ALERT_RULE_REVISION,
|
||||
ALERT_RULE_TYPE_ID,
|
||||
ALERT_RULE_UUID,
|
||||
SPACE_IDS,
|
||||
ALERT_RULE_TAGS,
|
||||
TIMESTAMP,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { getCommonAlertFields } from './get_common_alert_fields';
|
||||
|
||||
describe('getCommonAlertFields', () => {
|
||||
test('should correctly return common alert fields', () => {
|
||||
expect(
|
||||
getCommonAlertFields({
|
||||
executionId: '1234',
|
||||
params: { foo: 'bar' },
|
||||
// @ts-expect-error - incomplete rule definition for testing
|
||||
rule: {
|
||||
ruleTypeName: 'Test Rule',
|
||||
consumer: 'test-consumer',
|
||||
name: 'Test Rule Name',
|
||||
producer: 'test-producer',
|
||||
revision: 1,
|
||||
ruleTypeId: 'test.rule-type',
|
||||
id: 'rule-id',
|
||||
tags: ['test-tag'],
|
||||
},
|
||||
startedAt: new Date('2023-10-01T00:00:00Z'),
|
||||
spaceId: 'default',
|
||||
})
|
||||
).toEqual({
|
||||
[ALERT_RULE_PARAMETERS]: { foo: 'bar' },
|
||||
[ALERT_RULE_CATEGORY]: 'Test Rule',
|
||||
[ALERT_RULE_CONSUMER]: 'test-consumer',
|
||||
[ALERT_RULE_EXECUTION_UUID]: '1234',
|
||||
[ALERT_RULE_NAME]: 'Test Rule Name',
|
||||
[ALERT_RULE_PRODUCER]: 'test-producer',
|
||||
[ALERT_RULE_REVISION]: 1,
|
||||
[ALERT_RULE_TYPE_ID]: 'test.rule-type',
|
||||
[ALERT_RULE_UUID]: 'rule-id',
|
||||
[SPACE_IDS]: ['default'],
|
||||
[ALERT_RULE_TAGS]: ['test-tag'],
|
||||
[TIMESTAMP]: '2023-10-01T00:00:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
test(`should set kibana.space_ids to '*' when dangerouslyCreateAlertsInAllSpaces=true`, () => {
|
||||
expect(
|
||||
getCommonAlertFields(
|
||||
{
|
||||
executionId: '1234',
|
||||
params: { foo: 'bar' },
|
||||
// @ts-expect-error - incomplete rule definition for testing
|
||||
rule: {
|
||||
ruleTypeName: 'Test Rule',
|
||||
consumer: 'test-consumer',
|
||||
name: 'Test Rule Name',
|
||||
producer: 'test-producer',
|
||||
revision: 1,
|
||||
ruleTypeId: 'test.rule-type',
|
||||
id: 'rule-id',
|
||||
tags: ['test-tag'],
|
||||
},
|
||||
startedAt: new Date('2023-10-01T00:00:00Z'),
|
||||
spaceId: 'default',
|
||||
},
|
||||
true
|
||||
)
|
||||
).toEqual({
|
||||
[ALERT_RULE_PARAMETERS]: { foo: 'bar' },
|
||||
[ALERT_RULE_CATEGORY]: 'Test Rule',
|
||||
[ALERT_RULE_CONSUMER]: 'test-consumer',
|
||||
[ALERT_RULE_EXECUTION_UUID]: '1234',
|
||||
[ALERT_RULE_NAME]: 'Test Rule Name',
|
||||
[ALERT_RULE_PRODUCER]: 'test-producer',
|
||||
[ALERT_RULE_REVISION]: 1,
|
||||
[ALERT_RULE_TYPE_ID]: 'test.rule-type',
|
||||
[ALERT_RULE_UUID]: 'rule-id',
|
||||
[SPACE_IDS]: ['*'],
|
||||
[ALERT_RULE_TAGS]: ['test-tag'],
|
||||
[TIMESTAMP]: '2023-10-01T00:00:00.000Z',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -24,7 +24,8 @@ import type { RuleExecutorOptions } from '@kbn/alerting-plugin/server';
|
|||
import type { CommonAlertFieldsLatest } from '../../common/schemas';
|
||||
|
||||
export const getCommonAlertFields = (
|
||||
options: RuleExecutorOptions<any, any, any, any, any>
|
||||
options: RuleExecutorOptions<any, any, any, any, any>,
|
||||
dangerouslyCreateAlertsInAllSpaces?: boolean
|
||||
): CommonAlertFieldsLatest => {
|
||||
return {
|
||||
[ALERT_RULE_PARAMETERS]: options.params,
|
||||
|
@ -36,7 +37,7 @@ export const getCommonAlertFields = (
|
|||
[ALERT_RULE_REVISION]: options.rule.revision,
|
||||
[ALERT_RULE_TYPE_ID]: options.rule.ruleTypeId,
|
||||
[ALERT_RULE_UUID]: options.rule.id,
|
||||
[SPACE_IDS]: [options.spaceId],
|
||||
[SPACE_IDS]: dangerouslyCreateAlertsInAllSpaces === true ? ['*'] : [options.spaceId],
|
||||
[ALERT_RULE_TAGS]: options.rule.tags,
|
||||
[TIMESTAMP]: options.startedAt.toISOString(),
|
||||
};
|
||||
|
|
|
@ -80,6 +80,8 @@ const testRuleTypes = [
|
|||
'test.waitingRule',
|
||||
'test.patternFiringAutoRecoverFalse',
|
||||
'test.severity',
|
||||
'test.dangerouslyCreateAlertsInAllSpaces',
|
||||
'test.persistenceDangerouslyCreateAlertsInAllSpaces',
|
||||
];
|
||||
|
||||
const testAlertingFeatures = testRuleTypes.map((ruleTypeId) => ({
|
||||
|
|
|
@ -19,6 +19,9 @@ import type {
|
|||
RuleTypeParams,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers';
|
||||
import { Dataset, createPersistenceRuleTypeWrapper } from '@kbn/rule-registry-plugin/server';
|
||||
import { mappingFromFieldMap } from '@kbn/alerting-plugin/common';
|
||||
import { alertFieldMap } from '@kbn/alerts-as-data-utils';
|
||||
import type { FixtureStartDeps, FixtureSetupDeps } from './plugin';
|
||||
|
||||
export const EscapableStrings = {
|
||||
|
@ -1295,6 +1298,112 @@ export function defineRuleTypes(
|
|||
{ alerting, ruleRegistry }: Pick<FixtureSetupDeps, 'alerting' | 'ruleRegistry'>,
|
||||
logger: Logger
|
||||
) {
|
||||
const ruleDataClient = ruleRegistry.ruleDataService.initializeIndex({
|
||||
feature: 'AlertingExample',
|
||||
registrationContext: 'test.dangerouslycreatealertsinallspaces',
|
||||
dataset: Dataset.alerts,
|
||||
componentTemplateRefs: [],
|
||||
componentTemplates: [
|
||||
{
|
||||
name: 'mappings',
|
||||
mappings: mappingFromFieldMap(alertFieldMap, false),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const persistenceRuleTypeWrapper = createPersistenceRuleTypeWrapper({
|
||||
ruleDataClient,
|
||||
logger,
|
||||
formatAlert: undefined,
|
||||
});
|
||||
|
||||
const dangerouslyCreateAlertsInAllSpacesPersistenceRuleType = persistenceRuleTypeWrapper({
|
||||
id: 'test.persistenceDangerouslyCreateAlertsInAllSpaces',
|
||||
name: 'Test Persistence Rule Type - All Spaces',
|
||||
validate: { params: schema.any() },
|
||||
defaultActionGroupId: 'default',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
minimumLicenseRequired: 'basic',
|
||||
category: 'kibana',
|
||||
producer: 'alertsFixture',
|
||||
solution: 'stack',
|
||||
isExportable: true,
|
||||
async executor(ruleExecutorOptions) {
|
||||
const { services } = ruleExecutorOptions;
|
||||
const { alertWithPersistence } = services;
|
||||
|
||||
// generate some alerts
|
||||
const alerts = range(0, 5).map((i) => {
|
||||
const id = uuidv4();
|
||||
return {
|
||||
_id: id,
|
||||
_source: {
|
||||
original_source: {
|
||||
_id: `${id}-${i}`,
|
||||
'@timestamp': new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await alertWithPersistence(alerts, true, 100);
|
||||
|
||||
return { state: {} };
|
||||
},
|
||||
autoRecoverAlerts: false,
|
||||
alerts: {
|
||||
context: 'test.dangerouslycreatealertsinallspaces',
|
||||
mappings: { dynamic: false, fieldMap: { ...alertFieldMap } },
|
||||
shouldWrite: false,
|
||||
isSpaceAware: false,
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
},
|
||||
});
|
||||
|
||||
const dangerouslyCreateAlertsInAllSpacesRuleType: RuleType<
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
'default',
|
||||
'recovered',
|
||||
{ original_source: { _id: string } }
|
||||
> = {
|
||||
id: 'test.dangerouslyCreateAlertsInAllSpaces',
|
||||
name: 'Test Alerts Client Rule Type - All Spaces',
|
||||
validate: { params: schema.any() },
|
||||
defaultActionGroupId: 'default',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
minimumLicenseRequired: 'basic',
|
||||
category: 'kibana',
|
||||
producer: 'alertsFixture',
|
||||
solution: 'stack',
|
||||
isExportable: true,
|
||||
async executor(ruleExecutorOptions) {
|
||||
const { services } = ruleExecutorOptions;
|
||||
const { alertsClient } = services;
|
||||
|
||||
range(0, 5).forEach((i) => {
|
||||
alertsClient?.report({
|
||||
id: `instance-${i}`,
|
||||
actionGroup: 'default',
|
||||
payload: { original_source: { _id: `instance-${i}` } },
|
||||
});
|
||||
});
|
||||
|
||||
return { state: {} };
|
||||
},
|
||||
autoRecoverAlerts: false,
|
||||
alerts: {
|
||||
context: 'test.dangerouslycreatealertsinallspaces',
|
||||
mappings: { dynamic: false, fieldMap: { ...alertFieldMap } },
|
||||
shouldWrite: true,
|
||||
isSpaceAware: false,
|
||||
dangerouslyCreateAlertsInAllSpaces: true,
|
||||
},
|
||||
};
|
||||
|
||||
const noopRuleType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.noop',
|
||||
name: 'Test: Noop',
|
||||
|
@ -1567,4 +1676,6 @@ export function defineRuleTypes(
|
|||
alerting.registerType(getWaitingRuleType(logger));
|
||||
alerting.registerType(getSeverityRuleType());
|
||||
alerting.registerType(getInternalRuleType());
|
||||
alerting.registerType(dangerouslyCreateAlertsInAllSpacesPersistenceRuleType);
|
||||
alerting.registerType(dangerouslyCreateAlertsInAllSpacesRuleType);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/logging",
|
||||
"@kbn/alerting-api-integration-helpers",
|
||||
"@kbn/alerts-as-data-utils",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import type { Alert } from '@kbn/alerts-as-data-utils';
|
||||
import type { SearchHit } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { SPACE_IDS } from '@kbn/rule-data-utils';
|
||||
import { getEventLog, ObjectRemover, getTestRuleData } from '../../../../common/lib';
|
||||
import type { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
import { Spaces } from '../../../scenarios';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function dangerouslyCreateAlertsInAllSpacesTests({
|
||||
getService,
|
||||
}: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const retry = getService('retry');
|
||||
|
||||
const alertsAsDataIndex = '.alerts-test.dangerouslycreatealertsinallspaces.alerts-default';
|
||||
|
||||
describe('dangerouslyCreateAlertsInAllSpaces', () => {
|
||||
const objectRemover = new ObjectRemover(supertestWithoutAuth);
|
||||
|
||||
afterEach(async () => {
|
||||
await objectRemover.removeAll();
|
||||
await es.deleteByQuery({
|
||||
index: alertsAsDataIndex,
|
||||
query: { match_all: {} },
|
||||
conflicts: 'proceed',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates alerts with space_id "*" for persistence rule type with dangerouslyCreateAlertsInAllSpaces enabled', async () => {
|
||||
const createdRule = await supertestWithoutAuth
|
||||
.post(`/api/alerting/rule`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send(
|
||||
getTestRuleData({
|
||||
rule_type_id: 'test.persistenceDangerouslyCreateAlertsInAllSpaces',
|
||||
schedule: { interval: '1d' },
|
||||
throttle: null,
|
||||
params: {},
|
||||
actions: [],
|
||||
})
|
||||
);
|
||||
|
||||
expect(createdRule.status).to.eql(200);
|
||||
const ruleId = createdRule.body.id;
|
||||
objectRemover.add(Spaces.default.id, ruleId, 'rule', 'alerting');
|
||||
|
||||
// Wait for the event log execute doc so we can get the execution UUID
|
||||
await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 1 }]]));
|
||||
|
||||
// Query for alerts
|
||||
const alertDocs = await queryForAlertDocs<Alert>();
|
||||
|
||||
for (let i = 0; i < alertDocs.length; ++i) {
|
||||
const source: Alert = alertDocs[i]._source!;
|
||||
expect(source[SPACE_IDS]).to.eql(['*']);
|
||||
}
|
||||
});
|
||||
|
||||
it('creates alerts with space_id "*" for alerting framework rule type with dangerouslyCreateAlertsInAllSpaces enabled', async () => {
|
||||
const createdRule = await supertestWithoutAuth
|
||||
.post(`/api/alerting/rule`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send(
|
||||
getTestRuleData({
|
||||
rule_type_id: 'test.dangerouslyCreateAlertsInAllSpaces',
|
||||
schedule: { interval: '1d' },
|
||||
throttle: null,
|
||||
params: {},
|
||||
actions: [],
|
||||
})
|
||||
);
|
||||
|
||||
expect(createdRule.status).to.eql(200);
|
||||
const ruleId = createdRule.body.id;
|
||||
objectRemover.add(Spaces.default.id, ruleId, 'rule', 'alerting');
|
||||
|
||||
// Wait for the event log execute doc so we can get the execution UUID
|
||||
await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 1 }]]));
|
||||
|
||||
// Query for alerts
|
||||
const alertDocs = await queryForAlertDocs<Alert>();
|
||||
|
||||
for (let i = 0; i < alertDocs.length; ++i) {
|
||||
const source: Alert = alertDocs[i]._source!;
|
||||
expect(source[SPACE_IDS]).to.eql(['*']);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function queryForAlertDocs<T>(): Promise<Array<SearchHit<T>>> {
|
||||
const searchResult = await es.search({
|
||||
index: alertsAsDataIndex,
|
||||
query: { match_all: {} },
|
||||
});
|
||||
return searchResult.hits.hits as Array<SearchHit<T>>;
|
||||
}
|
||||
|
||||
async function waitForEventLogDocs(
|
||||
id: string,
|
||||
actions: Map<string, { gte: number } | { equal: number }>
|
||||
) {
|
||||
return await retry.try(async () => {
|
||||
return await getEventLog({
|
||||
getService,
|
||||
spaceId: Spaces.default.id,
|
||||
type: 'alert',
|
||||
id,
|
||||
provider: 'alerting',
|
||||
actions,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -18,5 +18,6 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC
|
|||
loadTestFile(require.resolve('./builtin_alert_types'));
|
||||
loadTestFile(require.resolve('./maintenance_window_flows'));
|
||||
loadTestFile(require.resolve('./maintenance_window_scoped_query'));
|
||||
loadTestFile(require.resolve('./dangerously_create_alerts_in_all_spaces'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ export const createRule = async ({
|
|||
objectRemover,
|
||||
overwrites,
|
||||
}: {
|
||||
actionId: string;
|
||||
actionId?: string;
|
||||
pattern?: { instance: boolean[] };
|
||||
supertest: SuperTestAgent;
|
||||
objectRemover: ObjectRemover;
|
||||
|
@ -43,18 +43,20 @@ export const createRule = async ({
|
|||
params: {
|
||||
pattern,
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
id: actionId,
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: actionId,
|
||||
group: 'recovered',
|
||||
params: {},
|
||||
},
|
||||
],
|
||||
actions: actionId
|
||||
? [
|
||||
{
|
||||
id: actionId,
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: actionId,
|
||||
group: 'recovered',
|
||||
params: {},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
...overwrites,
|
||||
})
|
||||
)
|
||||
|
|
|
@ -34,6 +34,11 @@ jest.mock('../utils/utils', () => ({
|
|||
checkForFrozenIndices: jest.fn(async () => []),
|
||||
}));
|
||||
|
||||
jest.mock('@kbn/alerting-plugin/server', () => ({
|
||||
...jest.requireActual('@kbn/alerting-plugin/server'),
|
||||
shouldCreateAlertsInAllSpaces: jest.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
jest.mock('../utils/get_list_client', () => ({
|
||||
getListClient: jest.fn().mockReturnValue({
|
||||
listClient: jest.fn(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue