From 927bf7cb09a5eb9796e66f4a097fc1a6010d44ba Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Wed, 8 Sep 2021 21:28:37 -0700 Subject: [PATCH 01/14] [Alerting] Active alerts do not recover after re-enabling a rule --- x-pack/plugins/alerting/server/plugin.ts | 1 + .../server/rules_client/rules_client.ts | 95 ++++++++++++++++++- .../alerting/server/rules_client_factory.ts | 6 +- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index bb42beba6e237..8b0e8e83e4796 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -365,6 +365,7 @@ export class AlertingPlugin { eventLog: plugins.eventLog, kibanaVersion: this.kibanaVersion, authorization: alertingAuthorizationClientFactory, + eventLogger: this.eventLogger, }); const getRulesClientWithRequest = (request: KibanaRequest) => { diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index c3e21e02cdb7d..ab20785f94b17 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -7,7 +7,7 @@ import Semver from 'semver'; import Boom from '@hapi/boom'; -import { omit, isEqual, map, uniq, pick, truncate, trim } from 'lodash'; +import { omit, isEqual, map, uniq, pick, truncate, trim, mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; import { estypes } from '@elastic/elasticsearch'; import { @@ -38,6 +38,9 @@ import { AlertWithLegacyId, SanitizedAlertWithLegacyId, PartialAlertWithLegacyId, + RawAlertInstance, + AlertInstanceState, + AlertType, } from '../types'; import { validateAlertTypeParams, @@ -60,7 +63,7 @@ import { AlertingAuthorizationFilterType, AlertingAuthorizationFilterOpts, } from '../authorization'; -import { IEventLogClient } from '../../../event_log/server'; +import { IEventLogClient, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log'; import { IEvent } from '../../../event_log/server'; @@ -73,6 +76,8 @@ import { ruleAuditEvent, RuleAuditAction } from './audit_events'; import { KueryNode, nodeBuilder } from '../../../../../src/plugins/data/common'; import { mapSortField } from './lib'; import { getAlertExecutionStatusPending } from '../lib/alert_execution_status'; +import { AlertInstance } from '../alert_instance'; +import { EVENT_LOG_ACTIONS } from '../plugin'; export interface RegistryAlertTypeWithAuth extends RegistryRuleType { authorizedConsumers: string[]; @@ -101,6 +106,7 @@ export interface ConstructorOptions { getEventLogClient: () => Promise; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; auditLogger?: AuditLogger; + eventLogger?: IEventLogger; } export interface MuteOptions extends IndexType { @@ -215,6 +221,7 @@ export class RulesClient { private readonly encryptedSavedObjectsClient: EncryptedSavedObjectsClient; private readonly kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; private readonly auditLogger?: AuditLogger; + private readonly eventLogger?: IEventLogger; constructor({ ruleTypeRegistry, @@ -232,6 +239,7 @@ export class RulesClient { getEventLogClient, kibanaVersion, auditLogger, + eventLogger, }: ConstructorOptions) { this.logger = logger; this.getUserName = getUserName; @@ -248,6 +256,7 @@ export class RulesClient { this.getEventLogClient = getEventLogClient; this.kibanaVersion = kibanaVersion; this.auditLogger = auditLogger; + this.eventLogger = eventLogger; } public async create({ @@ -503,7 +512,7 @@ export class RulesClient { // default duration of instance summary is 60 * alert interval const dateNow = new Date(); - const durationMillis = parseDuration(alert.schedule.interval) * 60; + const durationMillis = parseDuration(alert.schedule.interval) * 360; const defaultDateStart = new Date(dateNow.valueOf() - durationMillis); const parsedDateStart = parseDate(dateStart, 'dateStart', defaultDateStart); @@ -1177,6 +1186,37 @@ export class RulesClient { version = alert.version; } + const { state } = taskInstanceToAlertTaskInstance( + await this.taskManager.get(attributes.scheduledTaskId!), + (attributes as unknown) as SanitizedAlert + ); + + const recoveredAlertInstances = mapValues, AlertInstance>( + state.alertInstances!, + (rawAlertInstance) => new AlertInstance(rawAlertInstance) + ); + const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); + + for (const instanceId of recoveredAlertInstanceIds) { + const { group: actionGroup, subgroup: actionSubgroup } = + recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; + const inststate = recoveredAlertInstances[instanceId].getState(); + const message = `instance '${instanceId}' has recovered`; + + this.logInstanceEvent( + id, + attributes.name, + this.ruleTypeRegistry.get(attributes.alertTypeId), + instanceId, + EVENT_LOG_ACTIONS.recoveredInstance, + message, + inststate, + actionGroup, + actionSubgroup, + this.namespace + ); + } + try { await this.authorization.ensureAuthorized({ ruleTypeId: attributes.alertTypeId, @@ -1236,6 +1276,55 @@ export class RulesClient { } } + private async logInstanceEvent( + ruleId: string, + ruleName: string, + ruleType: AlertType, + instanceId: string, + action: string, + message: string, + state: AlertInstanceState, + group?: string, + subgroup?: string, + namespace?: string + ) { + const event: IEvent = { + event: { + action, + kind: 'alert', + category: [ruleType.producer], + ...(state?.start ? { start: state.start as string } : {}), + ...(state?.end ? { end: state.end as string } : {}), + ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), + }, + kibana: { + alerting: { + instance_id: instanceId, + ...(group ? { action_group_id: group } : {}), + ...(subgroup ? { action_subgroup: subgroup } : {}), + }, + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: ruleId, + type_id: ruleType.id, + namespace, + }, + ], + }, + message, + rule: { + id: ruleId, + license: ruleType.minimumLicenseRequired, + category: ruleType.id, + ruleset: ruleType.producer, + name: ruleName, + }, + }; + this.eventLogger!.logEvent(event); + } + public async muteAll({ id }: { id: string }): Promise { return await retryIfConflicts( this.logger, diff --git a/x-pack/plugins/alerting/server/rules_client_factory.ts b/x-pack/plugins/alerting/server/rules_client_factory.ts index 7961d3761d3ef..1e9a021a0be51 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.ts @@ -17,7 +17,7 @@ import { RuleTypeRegistry, SpaceIdToNamespaceFunction } from './types'; import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server'; import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; import { TaskManagerStartContract } from '../../task_manager/server'; -import { IEventLogClientService } from '../../../plugins/event_log/server'; +import { IEventLogClientService, IEventLogger } from '../../../plugins/event_log/server'; import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory'; export interface RulesClientFactoryOpts { logger: Logger; @@ -32,6 +32,7 @@ export interface RulesClientFactoryOpts { eventLog: IEventLogClientService; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; authorization: AlertingAuthorizationClientFactory; + eventLogger?: IEventLogger; } export class RulesClientFactory { @@ -48,6 +49,7 @@ export class RulesClientFactory { private eventLog!: IEventLogClientService; private kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; private authorization!: AlertingAuthorizationClientFactory; + private eventLogger?: IEventLogger; public initialize(options: RulesClientFactoryOpts) { if (this.isInitialized) { @@ -66,6 +68,7 @@ export class RulesClientFactory { this.eventLog = options.eventLog; this.kibanaVersion = options.kibanaVersion; this.authorization = options.authorization; + this.eventLogger = options.eventLogger; } public create(request: KibanaRequest, savedObjects: SavedObjectsServiceStart): RulesClient { @@ -123,6 +126,7 @@ export class RulesClientFactory { async getEventLogClient() { return eventLog.getClient(request); }, + eventLogger: this.eventLogger, }); } } From ef7ea30cd02da07cfb1cbaae8ff6c037df793592 Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Wed, 13 Oct 2021 16:56:06 -0700 Subject: [PATCH 02/14] created reusable lib file for generating event log object --- ...eate_alert_event_log_record_object.test.ts | 197 ++++++++++++++++++ .../create_alert_event_log_record_object.ts | 76 +++++++ .../server/rules_client/rules_client.ts | 89 ++------ .../server/rules_client/tests/disable.test.ts | 120 +++++++++++ .../task_runner/create_execution_handler.ts | 64 +++--- .../server/task_runner/task_runner.ts | 53 ++--- 6 files changed, 465 insertions(+), 134 deletions(-) create mode 100644 x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts create mode 100644 x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts new file mode 100644 index 0000000000000..c7b09395c8fea --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -0,0 +1,197 @@ +/* + * 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 { createAlertEventLogRecordObject } from './create_alert_event_log_record_object'; +import { UntypedNormalizedAlertType } from '../rule_type_registry'; +import { RecoveredActionGroup } from '../types'; + +describe('createAlertEventLogRecordObject', () => { + const ruleType: jest.Mocked = { + id: 'test', + name: 'My test alert', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', + }; + + test('created alert event "execute-start"', async () => { + expect( + createAlertEventLogRecordObject({ + ruleId: '1', + ruleType, + action: 'execute-start', + runDateString: '1970-01-01T00:00:00.000Z', + task: { + scheduled: '1970-01-01T00:00:00.000Z', + scheduleDelay: 0, + }, + savedObjects: [ + { + id: '1', + type: 'alert', + typeId: ruleType.id, + }, + ], + }) + ).toBe({ + '@timestamp': '1970-01-01T00:00:00.000Z', + event: { + action: 'execute-start', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: undefined, + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + }, + message: 'alert execution start', + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); + }); + + test('created alert event "recovered-instance"', async () => { + expect( + createAlertEventLogRecordObject({ + ruleId: '1', + ruleName: 'test name', + ruleType, + action: 'recovered-instance', + instanceId: 'test1', + group: 'group 1', + message: 'message text here', + namespace: 'default', + subgroup: 'subgroup value', + state: { + start: '1970-01-01T00:00:00.000Z', + end: '1970-01-01T00:05:00.000Z', + duration: 5, + }, + savedObjects: [ + { + id: '1', + type: 'alert', + typeId: ruleType.id, + }, + ], + }) + ).toBe({ + event: { + action: 'recovered-instance', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: 'default', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + ], + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + }, + message: 'alert execution start', + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + name: 'test name', + }, + }); + }); + + test('created alert event "execute-action"', async () => { + expect( + createAlertEventLogRecordObject({ + ruleId: '1', + ruleName: 'test name', + ruleType, + action: 'recovered-instance', + instanceId: 'test1', + group: 'group 1', + message: 'action execution start', + namespace: 'default', + subgroup: 'subgroup value', + state: { + start: '1970-01-01T00:00:00.000Z', + end: '1970-01-01T00:05:00.000Z', + duration: 5, + }, + savedObjects: [ + { + id: '1', + type: 'alert', + typeId: ruleType.id, + }, + { + id: '2', + type: 'acion', + typeId: '.email', + }, + ], + }) + ).toBe({ + event: { + action: 'recovered-instance', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + saved_objects: [ + { + id: '1', + namespace: 'default', + rel: 'primary', + type: 'alert', + type_id: 'test', + }, + { + id: '2', + namespace: 'default', + rel: 'primary', + type: 'action', + type_id: '.email', + }, + ], + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + }, + message: 'action execution start', + rule: { + category: 'test', + id: '1', + license: 'basic', + ruleset: 'alerts', + name: 'test name', + }, + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts new file mode 100644 index 0000000000000..0a0154240092b --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -0,0 +1,76 @@ +/* + * 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 { AlertInstanceState } from '../types'; +import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { UntypedNormalizedAlertType } from '../rule_type_registry'; + +export type Event = Exclude; + +interface CreateAlertEventLogRecordParams { + ruleId: string; + ruleType: UntypedNormalizedAlertType; + action: string; + ruleName?: string; + instanceId?: string; + message?: string; + state?: AlertInstanceState; + group?: string; + subgroup?: string; + namespace?: string; + runDateString?: string; + task?: { + scheduled?: string; + scheduleDelay?: number; + }; + savedObjects: Array<{ + type: string; + id: string; + typeId: string; + }>; +} + +export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { + const { ruleType, action, state, message, task, ruleId, group, subgroup, namespace } = params; + const event: Event = { + // explicitly set execute timestamp so it will be before other events + // generated here (new-instance, schedule-action, etc) + '@timestamp': params.runDateString, + event: { + action, + kind: 'alert', + category: [ruleType.producer], + ...(state?.start ? { start: state.start as string } : {}), + ...(state?.end ? { end: state.end as string } : {}), + ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), + }, + kibana: { + alerting: { + instance_id: params.instanceId, + ...(group ? { action_group_id: group } : {}), + ...(subgroup ? { action_subgroup: subgroup } : {}), + }, + saved_objects: params.savedObjects.map((so) => ({ + rel: SAVED_OBJECT_REL_PRIMARY, + type: so.type, + id: so.id, + type_id: so.typeId, + namespace, + })), + ...(task ? { task: { scheduled: task.scheduled, schedule_delay: task.scheduleDelay } } : {}), + }, + message, + rule: { + id: ruleId, + license: ruleType.minimumLicenseRequired, + category: ruleType.id, + ruleset: ruleType.producer, + name: params.ruleName, + }, + }; + return event; +} diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index e08c5bf569ae2..5049faa8cfb41 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -39,8 +39,6 @@ import { SanitizedAlertWithLegacyId, PartialAlertWithLegacyId, RawAlertInstance, - AlertInstanceState, - AlertType, } from '../types'; import { validateAlertTypeParams, @@ -63,10 +61,9 @@ import { AlertingAuthorizationFilterType, AlertingAuthorizationFilterOpts, } from '../authorization'; -import { IEventLogClient, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { IEvent, IEventLogClient, IEventLogger } from '../../../event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log'; -import { IEvent } from '../../../event_log/server'; import { AuditLogger } from '../../../security/server'; import { parseDuration } from '../../common/parse_duration'; import { retryIfConflicts } from '../lib/retry_if_conflicts'; @@ -78,6 +75,7 @@ import { mapSortField } from './lib'; import { getAlertExecutionStatusPending } from '../lib/alert_execution_status'; import { AlertInstance } from '../alert_instance'; import { EVENT_LOG_ACTIONS } from '../plugin'; +import { createAlertEventLogRecordObject } from '../lib/create_alert_event_log_record_object'; export interface RegistryAlertTypeWithAuth extends RegistryRuleType { authorizedConsumers: string[]; @@ -519,9 +517,9 @@ export class RulesClient { entity: AlertingAuthorizationEntity.Rule, }); - // default duration of instance summary is 60 * alert interval + // default duration of instance summary is 60 * alert interval, but no longer than 5 mins const dateNow = new Date(); - const durationMillis = parseDuration(alert.schedule.interval) * 360; + const durationMillis = parseDuration(alert.schedule.interval) * 60; const defaultDateStart = new Date(dateNow.valueOf() - durationMillis); const parsedDateStart = parseDate(dateStart, 'dateStart', defaultDateStart); @@ -1210,7 +1208,7 @@ export class RulesClient { const { state } = taskInstanceToAlertTaskInstance( await this.taskManager.get(attributes.scheduledTaskId!), - (attributes as unknown) as SanitizedAlert + attributes as unknown as SanitizedAlert ); const recoveredAlertInstances = mapValues, AlertInstance>( @@ -1225,18 +1223,26 @@ export class RulesClient { const inststate = recoveredAlertInstances[instanceId].getState(); const message = `instance '${instanceId}' has recovered`; - this.logInstanceEvent( - id, - attributes.name, - this.ruleTypeRegistry.get(attributes.alertTypeId), + const event = createAlertEventLogRecordObject({ + ruleId: id, + ruleName: attributes.name, + ruleType: this.ruleTypeRegistry.get(attributes.alertTypeId), instanceId, - EVENT_LOG_ACTIONS.recoveredInstance, + action: EVENT_LOG_ACTIONS.recoveredInstance, message, - inststate, - actionGroup, - actionSubgroup, - this.namespace - ); + state: inststate, + group: actionGroup, + subgroup: actionSubgroup, + namespace: this.namespace, + savedObjects: [ + { + id, + type: 'alert', + typeId: attributes.alertTypeId, + }, + ], + }); + this.eventLogger!.logEvent(event); } try { @@ -1298,55 +1304,6 @@ export class RulesClient { } } - private async logInstanceEvent( - ruleId: string, - ruleName: string, - ruleType: AlertType, - instanceId: string, - action: string, - message: string, - state: AlertInstanceState, - group?: string, - subgroup?: string, - namespace?: string - ) { - const event: IEvent = { - event: { - action, - kind: 'alert', - category: [ruleType.producer], - ...(state?.start ? { start: state.start as string } : {}), - ...(state?.end ? { end: state.end as string } : {}), - ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), - }, - kibana: { - alerting: { - instance_id: instanceId, - ...(group ? { action_group_id: group } : {}), - ...(subgroup ? { action_subgroup: subgroup } : {}), - }, - saved_objects: [ - { - rel: SAVED_OBJECT_REL_PRIMARY, - type: 'alert', - id: ruleId, - type_id: ruleType.id, - namespace, - }, - ], - }, - message, - rule: { - id: ruleId, - license: ruleType.minimumLicenseRequired, - category: ruleType.id, - ruleset: ruleType.producer, - name: ruleName, - }, - }; - this.eventLogger!.logEvent(event); - } - public async muteAll({ id }: { id: string }): Promise { return await retryIfConflicts( this.logger, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index 6b9b2021db68b..3ee7cb3aec7b2 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -18,6 +18,8 @@ import { InvalidatePendingApiKey } from '../../types'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { auditServiceMock } from '../../../../security/server/audit/index.mock'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { eventLoggerMock } from '../../../../event_log/server/event_logger.mock'; +import { TaskStatus } from '../../../../task_manager/server'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -26,6 +28,7 @@ const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); const authorization = alertingAuthorizationMock.create(); const actionsAuthorization = actionsAuthorizationMock.create(); const auditLogger = auditServiceMock.create().asScoped(httpServerMock.createKibanaRequest()); +const eventLogger = eventLoggerMock.create(); const kibanaVersion = 'v7.10.0'; const rulesClientParams: jest.Mocked = { @@ -44,6 +47,7 @@ const rulesClientParams: jest.Mocked = { getEventLogClient: jest.fn(), kibanaVersion, auditLogger, + eventLogger, }; beforeEach(() => { @@ -217,6 +221,122 @@ describe('disable()', () => { ).toBe('123'); }); + test('disables the rule with calling event log to "recover" the alert instances from the task state', async () => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'api_key_pending_invalidation', + attributes: { + apiKeyId: '123', + createdAt: '2019-02-12T21:01:22.479Z', + }, + references: [], + }); + const scheduledTaskId = 'task-123'; + taskManager.get.mockResolvedValue({ + id: scheduledTaskId, + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: { + alertInstances: { + '1': { + meta: { + lastScheduledActions: { + group: 'default', + subgroup: 'newSubgroup', + date: new Date().toISOString(), + }, + }, + state: { bar: false }, + }, + }, + }, + params: {}, + ownerId: null, + }); + await rulesClient.disable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + scheduledTaskId: null, + apiKey: null, + apiKeyOwner: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + { + version: '123', + } + ); + expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); + expect( + (unsecuredSavedObjectsClient.create.mock.calls[0][1] as InvalidatePendingApiKey).apiKeyId + ).toBe('123'); + + expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); + expect(eventLogger.logEvent.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "@timestamp": "1970-01-01T00:00:00.000Z", + "event": Object { + "action": "execute-start", + "category": Array [ + "alerts", + ], + "kind": "alert", + }, + "kibana": Object { + "saved_objects": Array [ + Object { + "id": "1", + "namespace": undefined, + "rel": "primary", + "type": "alert", + "type_id": "test", + }, + ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, + }, + "message": "alert execution start: \\"1\\"", + "rule": Object { + "category": "test", + "id": "1", + "license": "basic", + "ruleset": "alerts", + }, + } + `); + }); + test('falls back when getDecryptedAsInternalUser throws an error', async () => { encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 51301a80b1664..9d363da1e6788 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -21,8 +21,9 @@ import { AlertInstanceContext, RawAlert, } from '../types'; -import { NormalizedAlertType } from '../rule_type_registry'; +import { NormalizedAlertType, UntypedNormalizedAlertType } from '../rule_type_registry'; import { isEphemeralTaskRejectedDueToCapacityError } from '../../../task_manager/server'; +import { createAlertEventLogRecordObject } from '../lib/create_alert_event_log_record_object'; export interface CreateExecutionHandlerOptions< Params extends AlertTypeParams, @@ -201,43 +202,34 @@ export function createExecutionHandler< await actionsClient.enqueueExecution(enqueueOptions); } - const event: IEvent = { - event: { - action: EVENT_LOG_ACTIONS.executeAction, - kind: 'alert', - category: [alertType.producer], - }, - kibana: { - alerting: { - instance_id: alertInstanceId, - action_group_id: actionGroup, - action_subgroup: actionSubgroup, + const event = createAlertEventLogRecordObject({ + ruleId: alertId, + ruleType: alertType as UntypedNormalizedAlertType, + action: EVENT_LOG_ACTIONS.executeAction, + instanceId: alertInstanceId, + group: actionGroup, + subgroup: actionSubgroup, + ruleName: alertName, + savedObjects: [ + { + type: 'alert', + id: alertId, + typeId: alertType.id, }, - saved_objects: [ - { - rel: SAVED_OBJECT_REL_PRIMARY, - type: 'alert', - id: alertId, - type_id: alertType.id, - ...namespace, - }, - { type: 'action', id: action.id, type_id: action.actionTypeId, ...namespace }, - ], - }, - rule: { - id: alertId, - license: alertType.minimumLicenseRequired, - category: alertType.id, - ruleset: alertType.producer, - name: alertName, - }, - }; + { + type: 'action', + id: action.id, + typeId: action.actionTypeId, + }, + ], + ...namespace, + message: `alert: ${alertLabel} instanceId: '${alertInstanceId}' scheduled ${ + actionSubgroup + ? `actionGroup(subgroup): '${actionGroup}(${actionSubgroup})'` + : `actionGroup: '${actionGroup}'` + } action: ${actionLabel}`, + }); - event.message = `alert: ${alertLabel} instanceId: '${alertInstanceId}' scheduled ${ - actionSubgroup - ? `actionGroup(subgroup): '${actionGroup}(${actionSubgroup})'` - : `actionGroup: '${actionGroup}'` - } action: ${actionLabel}`; eventLogger.logEvent(event); } }; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index edf9bfe1b4846..86691d56ba6bb 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -49,16 +49,18 @@ import { AlertInstanceContext, WithoutReservedActionGroups, } from '../../common'; -import { NormalizedAlertType } from '../rule_type_registry'; +import { NormalizedAlertType, UntypedNormalizedAlertType } from '../rule_type_registry'; import { getEsErrorMessage } from '../lib/errors'; +import { + createAlertEventLogRecordObject, + Event, +} from '../lib/create_alert_event_log_record_object'; const FALLBACK_RETRY_INTERVAL = '5m'; // 1,000,000 nanoseconds in 1 millisecond const Millis2Nanos = 1000 * 1000; -type Event = Exclude; - interface AlertTaskRunResult { state: AlertTaskState; schedule: IntervalSchedule | undefined; @@ -517,37 +519,24 @@ export class TaskRunner< const namespace = this.context.spaceIdToNamespace(spaceId); const eventLogger = this.context.eventLogger; const scheduleDelay = runDate.getTime() - this.taskInstance.runAt.getTime(); - const event: IEvent = { - // explicitly set execute timestamp so it will be before other events - // generated here (new-instance, schedule-action, etc) - '@timestamp': runDateString, - event: { - action: EVENT_LOG_ACTIONS.execute, - kind: 'alert', - category: [this.alertType.producer], + + const event = createAlertEventLogRecordObject({ + ruleId: alertId, + ruleType: this.alertType as UntypedNormalizedAlertType, + action: EVENT_LOG_ACTIONS.execute, + namespace, + task: { + scheduled: this.taskInstance.runAt.toISOString(), + scheduleDelay: Millis2Nanos * scheduleDelay, }, - kibana: { - saved_objects: [ - { - rel: SAVED_OBJECT_REL_PRIMARY, - type: 'alert', - id: alertId, - type_id: this.alertType.id, - namespace, - }, - ], - task: { - scheduled: this.taskInstance.runAt.toISOString(), - schedule_delay: Millis2Nanos * scheduleDelay, + savedObjects: [ + { + id: alertId, + type: 'alert', + typeId: this.alertType.id, }, - }, - rule: { - id: alertId, - license: this.alertType.minimumLicenseRequired, - category: this.alertType.id, - ruleset: this.alertType.producer, - }, - }; + ], + }); eventLogger.startTiming(event); From c516cd1ff97fb63078d129a97e534a4d74a9fb7d Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Wed, 13 Oct 2021 17:03:00 -0700 Subject: [PATCH 03/14] comment fix --- x-pack/plugins/alerting/server/rules_client/rules_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 5049faa8cfb41..ce1838464e79b 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -517,7 +517,7 @@ export class RulesClient { entity: AlertingAuthorizationEntity.Rule, }); - // default duration of instance summary is 60 * alert interval, but no longer than 5 mins + // default duration of instance summary is 60 * alert interval const dateNow = new Date(); const durationMillis = parseDuration(alert.schedule.interval) * 60; const defaultDateStart = new Date(dateNow.valueOf() - durationMillis); From 0929cd214032aabc4fa035114ecc4e15e4e39426 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Wed, 13 Oct 2021 19:09:55 -0700 Subject: [PATCH 04/14] fixed tests --- .../create_alert_event_log_record_object.ts | 23 ++++++++++++------- .../server/rules_client/rules_client.ts | 3 ++- .../create_execution_handler.test.ts | 1 - .../task_runner/create_execution_handler.ts | 1 + .../server/task_runner/task_runner.test.ts | 3 --- .../server/task_runner/task_runner.ts | 2 ++ 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 0a0154240092b..132df0001a3c1 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -31,15 +31,26 @@ interface CreateAlertEventLogRecordParams { type: string; id: string; typeId: string; + relation?: string; }>; } export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { const { ruleType, action, state, message, task, ruleId, group, subgroup, namespace } = params; + const alerting = + params.instanceId || group || subgroup + ? { + alerting: { + ...(params.instanceId ? { instance_id: params.instanceId } : {}), + ...(group ? { action_group_id: group } : {}), + ...(subgroup ? { action_subgroup: subgroup } : {}), + }, + } + : undefined; const event: Event = { // explicitly set execute timestamp so it will be before other events // generated here (new-instance, schedule-action, etc) - '@timestamp': params.runDateString, + ...(params.runDateString ? { '@timestamp': params.runDateString } : {}), event: { action, kind: 'alert', @@ -49,13 +60,9 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), }, kibana: { - alerting: { - instance_id: params.instanceId, - ...(group ? { action_group_id: group } : {}), - ...(subgroup ? { action_subgroup: subgroup } : {}), - }, + ...(alerting ? alerting : {}), saved_objects: params.savedObjects.map((so) => ({ - rel: SAVED_OBJECT_REL_PRIMARY, + ...(so.relation ? { rel: so.relation } : {}), type: so.type, id: so.id, type_id: so.typeId, @@ -69,7 +76,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor license: ruleType.minimumLicenseRequired, category: ruleType.id, ruleset: ruleType.producer, - name: params.ruleName, + ...(params.ruleName ? { name: params.ruleName } : {}), }, }; return event; diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index ce1838464e79b..a9516945595ed 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -61,7 +61,7 @@ import { AlertingAuthorizationFilterType, AlertingAuthorizationFilterOpts, } from '../authorization'; -import { IEvent, IEventLogClient, IEventLogger } from '../../../event_log/server'; +import { IEvent, IEventLogClient, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log'; import { AuditLogger } from '../../../security/server'; @@ -1239,6 +1239,7 @@ export class RulesClient { id, type: 'alert', typeId: attributes.alertTypeId, + relation: SAVED_OBJECT_REL_PRIMARY }, ], }); diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index e3946599aed85..244dcb85b13e9 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -175,7 +175,6 @@ test('enqueues execution per selected action', async () => { "kibana": Object { "alerting": Object { "action_group_id": "default", - "action_subgroup": undefined, "instance_id": "2", }, "saved_objects": Array [ diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 9d363da1e6788..3b89adaf2f747 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -215,6 +215,7 @@ export function createExecutionHandler< type: 'alert', id: alertId, typeId: alertType.id, + relation: SAVED_OBJECT_REL_PRIMARY }, { type: 'action', diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index c5ccc909eff46..07c4d0371c718 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -1379,7 +1379,6 @@ describe('Task Runner', () => { "kibana": Object { "alerting": Object { "action_group_id": "default", - "action_subgroup": undefined, "instance_id": "1", }, "saved_objects": Array [ @@ -1676,7 +1675,6 @@ describe('Task Runner', () => { "kibana": Object { "alerting": Object { "action_group_id": "recovered", - "action_subgroup": undefined, "instance_id": "2", }, "saved_objects": Array [ @@ -1717,7 +1715,6 @@ describe('Task Runner', () => { "kibana": Object { "alerting": Object { "action_group_id": "default", - "action_subgroup": undefined, "instance_id": "1", }, "saved_objects": Array [ diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 86691d56ba6bb..1d626ecbfd859 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -521,6 +521,7 @@ export class TaskRunner< const scheduleDelay = runDate.getTime() - this.taskInstance.runAt.getTime(); const event = createAlertEventLogRecordObject({ + runDateString, ruleId: alertId, ruleType: this.alertType as UntypedNormalizedAlertType, action: EVENT_LOG_ACTIONS.execute, @@ -534,6 +535,7 @@ export class TaskRunner< id: alertId, type: 'alert', typeId: this.alertType.id, + relation: SAVED_OBJECT_REL_PRIMARY, }, ], }); From 0f46f7a9c2986058d6900fbdc54e56a980774c90 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Wed, 13 Oct 2021 20:43:57 -0700 Subject: [PATCH 05/14] fixed tests --- ...eate_alert_event_log_record_object.test.ts | 43 ++++++---- .../create_alert_event_log_record_object.ts | 2 +- .../server/rules_client/tests/disable.test.ts | 81 +++++++++++-------- .../rules_client_conflict_retries.test.ts | 16 ++++ .../task_runner/create_execution_handler.ts | 4 +- 5 files changed, 94 insertions(+), 52 deletions(-) diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index c7b09395c8fea..1793f7c879b2b 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -38,10 +38,11 @@ describe('createAlertEventLogRecordObject', () => { id: '1', type: 'alert', typeId: ruleType.id, + relation: 'primary', }, ], }) - ).toBe({ + ).toStrictEqual({ '@timestamp': '1970-01-01T00:00:00.000Z', event: { action: 'execute-start', @@ -58,8 +59,11 @@ describe('createAlertEventLogRecordObject', () => { type_id: 'test', }, ], + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, }, - message: 'alert execution start', rule: { category: 'test', id: '1', @@ -91,16 +95,25 @@ describe('createAlertEventLogRecordObject', () => { id: '1', type: 'alert', typeId: ruleType.id, + relation: 'primary', }, ], }) - ).toBe({ + ).toStrictEqual({ event: { action: 'recovered-instance', category: ['alerts'], + duration: 5, + end: '1970-01-01T00:05:00.000Z', kind: 'alert', + start: '1970-01-01T00:00:00.000Z', }, kibana: { + alerting: { + action_group_id: 'group 1', + action_subgroup: 'subgroup value', + instance_id: 'test1', + }, saved_objects: [ { id: '1', @@ -110,12 +123,8 @@ describe('createAlertEventLogRecordObject', () => { type_id: 'test', }, ], - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, }, - message: 'alert execution start', + message: 'message text here', rule: { category: 'test', id: '1', @@ -148,21 +157,30 @@ describe('createAlertEventLogRecordObject', () => { id: '1', type: 'alert', typeId: ruleType.id, + relation: 'primary', }, { id: '2', - type: 'acion', + type: 'action', typeId: '.email', }, ], }) - ).toBe({ + ).toStrictEqual({ event: { action: 'recovered-instance', category: ['alerts'], + duration: 5, + end: '1970-01-01T00:05:00.000Z', kind: 'alert', + start: '1970-01-01T00:00:00.000Z', }, kibana: { + alerting: { + action_group_id: 'group 1', + action_subgroup: 'subgroup value', + instance_id: 'test1', + }, saved_objects: [ { id: '1', @@ -174,15 +192,10 @@ describe('createAlertEventLogRecordObject', () => { { id: '2', namespace: 'default', - rel: 'primary', type: 'action', type_id: '.email', }, ], - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, }, message: 'action execution start', rule: { diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 132df0001a3c1..9c81b8c3ba2fb 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -70,7 +70,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor })), ...(task ? { task: { scheduled: task.scheduled, schedule_delay: task.scheduleDelay } } : {}), }, - message, + ...(message ? { message } : {}), rule: { id: ruleId, license: ruleType.minimumLicenseRequired, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index 3ee7cb3aec7b2..32a404485f7c3 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -52,6 +52,21 @@ const rulesClientParams: jest.Mocked = { beforeEach(() => { getBeforeSetup(rulesClientParams, taskManager, ruleTypeRegistry); + taskManager.get.mockResolvedValue({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: { + alertId: '1', + }, + ownerId: null, + }); (auditLogger.log as jest.Mock).mockClear(); }); @@ -255,7 +270,9 @@ describe('disable()', () => { }, }, }, - params: {}, + params: { + alertId: '1', + }, ownerId: null, }); await rulesClient.disable({ id: '1' }); @@ -300,41 +317,37 @@ describe('disable()', () => { (unsecuredSavedObjectsClient.create.mock.calls[0][1] as InvalidatePendingApiKey).apiKeyId ).toBe('123'); - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.logEvent.mock.calls[0][0]).toMatchInlineSnapshot(` - Object { - "@timestamp": "1970-01-01T00:00:00.000Z", - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", + expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); + expect(eventLogger.logEvent.mock.calls[0][0]).toStrictEqual({ + event: { + action: 'recovered-instance', + category: ['alerts'], + kind: 'alert', + }, + kibana: { + alerting: { + action_group_id: 'default', + action_subgroup: 'newSubgroup', + instance_id: '1', }, - "kibana": Object { - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", + saved_objects: [ + { + id: '1', + namespace: 'default', + rel: 'primary', + type: 'alert', + type_id: 'myType', }, - }, - "message": "alert execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - } - `); + ], + }, + message: 'instance \'1\' has recovered', + rule: { + category: '123', + id: '1', + license: 'basic', + ruleset: 'alerts', + }, + }); }); test('falls back when getDecryptedAsInternalUser throws an error', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts index dfc55efad41c6..0b35f250ba3c6 100644 --- a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts @@ -325,6 +325,22 @@ beforeEach(() => { params: {}, }); + taskManager.get.mockResolvedValue({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: { + alertId: '1', + }, + ownerId: null, + }); + const actionsClient = actionsClientMock.create(); actionsClient.getBulk.mockResolvedValue([]); rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 3b89adaf2f747..652e032a1cbb0 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -10,7 +10,7 @@ import { asSavedObjectExecutionSource, PluginStartContract as ActionsPluginStartContract, } from '../../../actions/server'; -import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; import { injectActionParams } from './inject_action_params'; import { @@ -215,7 +215,7 @@ export function createExecutionHandler< type: 'alert', id: alertId, typeId: alertType.id, - relation: SAVED_OBJECT_REL_PRIMARY + relation: SAVED_OBJECT_REL_PRIMARY, }, { type: 'action', From 7c02a5731610abe81cb14347ef61761b55f070c0 Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Thu, 14 Oct 2021 09:49:23 -0700 Subject: [PATCH 06/14] fixed typecheck --- .../server/lib/create_alert_event_log_record_object.ts | 2 +- .../plugins/alerting/server/rules_client/rules_client.ts | 9 +++++++-- .../alerting/server/rules_client/tests/disable.test.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 9c81b8c3ba2fb..4eb818e6c9fed 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -6,7 +6,7 @@ */ import { AlertInstanceState } from '../types'; -import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { IEvent } from '../../../event_log/server'; import { UntypedNormalizedAlertType } from '../rule_type_registry'; export type Event = Exclude; diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index a9516945595ed..2a752831e0d86 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -61,7 +61,12 @@ import { AlertingAuthorizationFilterType, AlertingAuthorizationFilterOpts, } from '../authorization'; -import { IEvent, IEventLogClient, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { + IEvent, + IEventLogClient, + IEventLogger, + SAVED_OBJECT_REL_PRIMARY, +} from '../../../event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log'; import { AuditLogger } from '../../../security/server'; @@ -1239,7 +1244,7 @@ export class RulesClient { id, type: 'alert', typeId: attributes.alertTypeId, - relation: SAVED_OBJECT_REL_PRIMARY + relation: SAVED_OBJECT_REL_PRIMARY, }, ], }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index 32a404485f7c3..d72af527b7232 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -340,7 +340,7 @@ describe('disable()', () => { }, ], }, - message: 'instance \'1\' has recovered', + message: "instance '1' has recovered", rule: { category: '123', id: '1', From 45ecc80f037a7ae5e0aa36a029159b572475e550 Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Thu, 14 Oct 2021 09:52:30 -0700 Subject: [PATCH 07/14] fixed due to comments --- .../server/lib/create_alert_event_log_record_object.test.ts | 2 +- .../server/lib/create_alert_event_log_record_object.ts | 6 ++---- x-pack/plugins/alerting/server/rules_client/rules_client.ts | 2 +- x-pack/plugins/alerting/server/task_runner/task_runner.ts | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index 1793f7c879b2b..1b4ba7cb412f3 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -28,7 +28,7 @@ describe('createAlertEventLogRecordObject', () => { ruleId: '1', ruleType, action: 'execute-start', - runDateString: '1970-01-01T00:00:00.000Z', + timestamp: '1970-01-01T00:00:00.000Z', task: { scheduled: '1970-01-01T00:00:00.000Z', scheduleDelay: 0, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 4eb818e6c9fed..12300211cb0bb 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -22,7 +22,7 @@ interface CreateAlertEventLogRecordParams { group?: string; subgroup?: string; namespace?: string; - runDateString?: string; + timestamp?: string; task?: { scheduled?: string; scheduleDelay?: number; @@ -48,9 +48,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor } : undefined; const event: Event = { - // explicitly set execute timestamp so it will be before other events - // generated here (new-instance, schedule-action, etc) - ...(params.runDateString ? { '@timestamp': params.runDateString } : {}), + ...(params.timestamp ? { '@timestamp': params.timestamp } : {}), event: { action, kind: 'alert', diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 2a752831e0d86..997c49f21ba94 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1217,7 +1217,7 @@ export class RulesClient { ); const recoveredAlertInstances = mapValues, AlertInstance>( - state.alertInstances!, + state.alertInstances ?? {}, (rawAlertInstance) => new AlertInstance(rawAlertInstance) ); const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 1d626ecbfd859..8b93d3fa17211 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -521,7 +521,7 @@ export class TaskRunner< const scheduleDelay = runDate.getTime() - this.taskInstance.runAt.getTime(); const event = createAlertEventLogRecordObject({ - runDateString, + timestamp: runDateString, ruleId: alertId, ruleType: this.alertType as UntypedNormalizedAlertType, action: EVENT_LOG_ACTIONS.execute, From f04e68c04b57e653544f5d2eb8e7396de1e21af5 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 14 Oct 2021 09:53:11 -0700 Subject: [PATCH 08/14] Apply suggestions from code review Co-authored-by: ymao1 --- x-pack/plugins/alerting/server/rules_client/rules_client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 997c49f21ba94..d70e0603407b1 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1225,7 +1225,7 @@ export class RulesClient { for (const instanceId of recoveredAlertInstanceIds) { const { group: actionGroup, subgroup: actionSubgroup } = recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; - const inststate = recoveredAlertInstances[instanceId].getState(); + const instance = recoveredAlertInstances[instanceId].getState(); const message = `instance '${instanceId}' has recovered`; const event = createAlertEventLogRecordObject({ @@ -1235,7 +1235,7 @@ export class RulesClient { instanceId, action: EVENT_LOG_ACTIONS.recoveredInstance, message, - state: inststate, + state: instance, group: actionGroup, subgroup: actionSubgroup, namespace: this.namespace, From e3390a06b87cdd01383b3aebbf123dfa241af2e2 Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Thu, 14 Oct 2021 09:55:27 -0700 Subject: [PATCH 09/14] fixed due to comments --- .../server/rules_client/rules_client.ts | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 997c49f21ba94..7baa663748491 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1211,46 +1211,47 @@ export class RulesClient { version = alert.version; } - const { state } = taskInstanceToAlertTaskInstance( - await this.taskManager.get(attributes.scheduledTaskId!), - attributes as unknown as SanitizedAlert - ); + if (this.eventLogger && attributes.scheduledTaskId) { + const { state } = taskInstanceToAlertTaskInstance( + await this.taskManager.get(attributes.scheduledTaskId), + attributes as unknown as SanitizedAlert + ); - const recoveredAlertInstances = mapValues, AlertInstance>( - state.alertInstances ?? {}, - (rawAlertInstance) => new AlertInstance(rawAlertInstance) - ); - const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); - - for (const instanceId of recoveredAlertInstanceIds) { - const { group: actionGroup, subgroup: actionSubgroup } = - recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; - const inststate = recoveredAlertInstances[instanceId].getState(); - const message = `instance '${instanceId}' has recovered`; - - const event = createAlertEventLogRecordObject({ - ruleId: id, - ruleName: attributes.name, - ruleType: this.ruleTypeRegistry.get(attributes.alertTypeId), - instanceId, - action: EVENT_LOG_ACTIONS.recoveredInstance, - message, - state: inststate, - group: actionGroup, - subgroup: actionSubgroup, - namespace: this.namespace, - savedObjects: [ - { - id, - type: 'alert', - typeId: attributes.alertTypeId, - relation: SAVED_OBJECT_REL_PRIMARY, - }, - ], - }); - this.eventLogger!.logEvent(event); + const recoveredAlertInstances = mapValues, AlertInstance>( + state.alertInstances ?? {}, + (rawAlertInstance) => new AlertInstance(rawAlertInstance) + ); + const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); + + for (const instanceId of recoveredAlertInstanceIds) { + const { group: actionGroup, subgroup: actionSubgroup } = + recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; + const inststate = recoveredAlertInstances[instanceId].getState(); + const message = `instance '${instanceId}' has recovered`; + + const event = createAlertEventLogRecordObject({ + ruleId: id, + ruleName: attributes.name, + ruleType: this.ruleTypeRegistry.get(attributes.alertTypeId), + instanceId, + action: EVENT_LOG_ACTIONS.recoveredInstance, + message, + state: inststate, + group: actionGroup, + subgroup: actionSubgroup, + namespace: this.namespace, + savedObjects: [ + { + id, + type: 'alert', + typeId: attributes.alertTypeId, + relation: SAVED_OBJECT_REL_PRIMARY, + }, + ], + }); + this.eventLogger.logEvent(event); + } } - try { await this.authorization.ensureAuthorized({ ruleTypeId: attributes.alertTypeId, From ef343d655ea89f6309ab6e1ddde44778b42e8bfe Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Thu, 14 Oct 2021 11:23:22 -0700 Subject: [PATCH 10/14] fixed due to comments --- .../tests/alerting/disable.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 66f01000ede5e..6fa4e2d21a4b0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -16,7 +16,9 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, + getEventLog, } from '../../../common/lib'; +import { validateEvent } from '../../../spaces_only/tests/alerting/event_log'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { @@ -376,6 +378,64 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it('should create recovered-instance events for all alert instances', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + rule_type_id: 'test.noop', + consumer: 'alerts', + enabled: true, + }) + ) + .expect(200); + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const ruleId = createdRule.id; + + await alertUtils.getDisableRequest(ruleId); + + const events = await retry.try(async () => { + // there can be a successful execute before the error one + const someEvents = await getEventLog({ + getService, + spaceId: space.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([['recovered-instance', { equal: 2 }]]), + }); + const errorEvents = someEvents.filter( + (event) => event?.kibana?.alerting?.status === 'error' + ); + if (errorEvents.length === 0) { + throw new Error('no execute/error events yet'); + } + return errorEvents; + }); + + const event = events[0]; + expect(event).to.be.ok(); + + validateEvent(event, { + spaceId: space.id, + savedObjects: [{ type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.noop' }], + outcome: 'failure', + message: `test.noop:${ruleId}: execution failed`, + errorMessage: 'Unable to decrypt attribute "apiKey"', + status: 'error', + reason: 'decrypt', + shouldHaveTask: true, + rule: { + id: ruleId, + category: createdRule.body.rule_type_id, + license: 'basic', + ruleset: 'alertsFixture', + }, + }); + }); }); } }); From cbcd252b7ffea491954750bd880081a9bbaf11eb Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Thu, 14 Oct 2021 15:23:52 -0700 Subject: [PATCH 11/14] fixed due to comments --- .../server/rules_client/rules_client.ts | 2 +- .../tests/alerting/disable.ts | 42 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index c8c8e39cc4a55..bde0c35028582 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1227,7 +1227,7 @@ export class RulesClient { const { group: actionGroup, subgroup: actionSubgroup } = recoveredAlertInstances[instanceId].getLastScheduledActions() ?? {}; const instanceState = recoveredAlertInstances[instanceId].getState(); - const message = `instance '${instanceId}' has recovered`; + const message = `instance '${instanceId}' has recovered due to the rule was disabled`; const event = createAlertEventLogRecordObject({ ruleId: id, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 6fa4e2d21a4b0..9bcf60f230ec6 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -380,14 +380,22 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }); it('should create recovered-instance events for all alert instances', async () => { + // pattern of when the alert should fire + const pattern = { + instance: [false, true, true, false, false, true, true, true], + }; const { body: createdRule } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - rule_type_id: 'test.noop', - consumer: 'alerts', - enabled: true, + rule_type_id: 'test.patternFiring', + schedule: { interval: '1s' }, + throttle: null, + params: { + pattern, + }, + actions: [], }) ) .expect(200); @@ -398,22 +406,19 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte await alertUtils.getDisableRequest(ruleId); const events = await retry.try(async () => { - // there can be a successful execute before the error one - const someEvents = await getEventLog({ + return await getEventLog({ getService, spaceId: space.id, type: 'alert', id: ruleId, provider: 'alerting', - actions: new Map([['recovered-instance', { equal: 2 }]]), + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute', { gte: 9 }], + ['new-instance', { equal: 2 }], + ['recovered-instance', { equal: 2 }], + ]), }); - const errorEvents = someEvents.filter( - (event) => event?.kibana?.alerting?.status === 'error' - ); - if (errorEvents.length === 0) { - throw new Error('no execute/error events yet'); - } - return errorEvents; }); const event = events[0]; @@ -421,13 +426,10 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte validateEvent(event, { spaceId: space.id, - savedObjects: [{ type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.noop' }], - outcome: 'failure', - message: `test.noop:${ruleId}: execution failed`, - errorMessage: 'Unable to decrypt attribute "apiKey"', - status: 'error', - reason: 'decrypt', - shouldHaveTask: true, + savedObjects: [ + { type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.patternFiring' }, + ], + message: '', rule: { id: ruleId, category: createdRule.body.rule_type_id, From 189b1cda142b900a1f5b06b9f884a9dc8363fd6c Mon Sep 17 00:00:00 2001 From: YulNaumenko Date: Sun, 17 Oct 2021 12:53:55 -0700 Subject: [PATCH 12/14] fixed tests --- ...eate_alert_event_log_record_object.test.ts | 4 +- .../server/rules_client/tests/disable.test.ts | 2 +- .../tests/alerting/disable.ts | 62 ------------- .../spaces_only/tests/alerting/disable.ts | 86 +++++++++++++++++++ 4 files changed, 89 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index 1b4ba7cb412f3..0731886bcaeb0 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -141,7 +141,7 @@ describe('createAlertEventLogRecordObject', () => { ruleId: '1', ruleName: 'test name', ruleType, - action: 'recovered-instance', + action: 'execute-action', instanceId: 'test1', group: 'group 1', message: 'action execution start', @@ -168,7 +168,7 @@ describe('createAlertEventLogRecordObject', () => { }) ).toStrictEqual({ event: { - action: 'recovered-instance', + action: 'execute-action', category: ['alerts'], duration: 5, end: '1970-01-01T00:05:00.000Z', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index d72af527b7232..c518d385dd747 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -340,7 +340,7 @@ describe('disable()', () => { }, ], }, - message: "instance '1' has recovered", + message: "instance '1' has recovered due to the rule was disabled", rule: { category: '123', id: '1', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 9bcf60f230ec6..66f01000ede5e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -16,9 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, - getEventLog, } from '../../../common/lib'; -import { validateEvent } from '../../../spaces_only/tests/alerting/event_log'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { @@ -378,66 +376,6 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); - - it('should create recovered-instance events for all alert instances', async () => { - // pattern of when the alert should fire - const pattern = { - instance: [false, true, true, false, false, true, true, true], - }; - const { body: createdRule } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestAlertData({ - rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, - throttle: null, - params: { - pattern, - }, - actions: [], - }) - ) - .expect(200); - objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); - - const ruleId = createdRule.id; - - await alertUtils.getDisableRequest(ruleId); - - const events = await retry.try(async () => { - return await getEventLog({ - getService, - spaceId: space.id, - type: 'alert', - id: ruleId, - provider: 'alerting', - actions: new Map([ - // make sure the counts of the # of events per type are as expected - ['execute', { gte: 9 }], - ['new-instance', { equal: 2 }], - ['recovered-instance', { equal: 2 }], - ]), - }); - }); - - const event = events[0]; - expect(event).to.be.ok(); - - validateEvent(event, { - spaceId: space.id, - savedObjects: [ - { type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.patternFiring' }, - ], - message: '', - rule: { - id: ruleId, - category: createdRule.body.rule_type_id, - license: 'basic', - ruleset: 'alertsFixture', - }, - }); - }); }); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index 7e93cf453929b..a769e0d7327b3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -14,12 +14,16 @@ import { getUrlPrefix, getTestAlertData, ObjectRemover, + getEventLog, } from '../../../common/lib'; +import { validateEvent } from './event_log'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { const es = getService('es'); const supertestWithoutAuth = getService('supertestWithoutAuth'); + const retry = getService('retry'); + const supertest = getService('supertest'); describe('disable', () => { const objectRemover = new ObjectRemover(supertestWithoutAuth); @@ -75,6 +79,75 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }); }); + it('should create recovered-instance events for all alert instances', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send({ + enabled: true, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.cumulative-firing', + consumer: 'alertsFixture', + schedule: { interval: '5s' }, + throttle: '5s', + actions: [], + params: {}, + notify_when: 'onThrottleInterval', + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + // wait for alert to actually execute + await retry.try(async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/state` + ); + + expect(response.status).to.eql(200); + expect(response.body).to.key('alerts', 'rule_type_state', 'previous_started_at'); + expect(response.body.rule_type_state.runCount).to.greaterThan(1); + }); + + await alertUtils.getDisableRequest(createdAlert.id); + const ruleId = createdAlert.id; + + // wait for the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['recovered-instance', { equal: 2 }], + ]), + }); + }); + + const event = events[0]; + expect(event).to.be.ok(); + + validateEvent(event, { + spaceId: Spaces.space1.id, + savedObjects: [ + { type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.cumulative-firing' }, + ], + message: "instance 'instance-0' has recovered due to the rule was disabled", + shouldHaveEventEnd: false, + shouldHaveTask: false, + rule: { + id: ruleId, + category: createdAlert.rule_type_id, + license: 'basic', + ruleset: 'alertsFixture', + name: 'abc', + }, + }); + }); + describe('legacy', () => { it('should handle disable alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth @@ -106,4 +179,17 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }); }); }); + + async function waitForEvents(id: string, actions: string[]) { + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces.space1.id, + type: 'alert', + id, + provider: 'alerting', + actions: new Map(actions.map((action) => [action, { gte: 1 }])), + }); + }); + } } From 0bcf3cbf67efca34b109c018cc9dcecde251e390 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Sun, 17 Oct 2021 14:17:52 -0700 Subject: [PATCH 13/14] Update disable.ts --- .../spaces_only/tests/alerting/disable.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index a769e0d7327b3..6a03ce73f40ea 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -179,17 +179,3 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }); }); }); - - async function waitForEvents(id: string, actions: string[]) { - await retry.try(async () => { - return await getEventLog({ - getService, - spaceId: Spaces.space1.id, - type: 'alert', - id, - provider: 'alerting', - actions: new Map(actions.map((action) => [action, { gte: 1 }])), - }); - }); - } -} From eca124073fe5844d96f3f786f0b8e92be27e185c Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Sun, 17 Oct 2021 14:26:22 -0700 Subject: [PATCH 14/14] Update disable.ts --- .../spaces_only/tests/alerting/disable.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index 6a03ce73f40ea..fa94eed46dc3f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -179,3 +179,4 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }); }); }); +}