Skip to content

Commit

Permalink
Implement new methods in IRuleExecutionLogClient
Browse files Browse the repository at this point in the history
  • Loading branch information
banderror committed Nov 1, 2021
1 parent 8fd0215 commit fb92607
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ export const ruleExecutionLogClientMock = {
create: (): jest.Mocked<IRuleExecutionLogClient> => ({
find: jest.fn(),
findBulk: jest.fn(),
update: jest.fn(),
delete: jest.fn(),

getLastFailures: jest.fn(),
getCurrentStatus: jest.fn(),
getCurrentStatusBulk: jest.fn(),

deleteCurrentStatus: jest.fn(),

logStatusChange: jest.fn(),
logExecutionMetrics: jest.fn(),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@

import { sum } from 'lodash';
import { SavedObjectsClientContract } from '../../../../../../../../src/core/server';
import { IEventLogService } from '../../../../../../event_log/server';
import { IEventLogClient, IEventLogService } from '../../../../../../event_log/server';
import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas';
import { IRuleStatusSOAttributes } from '../../rules/types';
import { SavedObjectsAdapter } from '../saved_objects_adapter/saved_objects_adapter';
import {
FindBulkExecutionLogArgs,
FindExecutionLogArgs,
GetCurrentStatusArgs,
GetCurrentStatusBulkArgs,
GetCurrentStatusBulkResult,
GetLastFailuresArgs,
IRuleExecutionLogClient,
LogExecutionMetricsArgs,
LogStatusChangeArgs,
UpdateExecutionLogArgs,
} from '../types';
import { EventLogClient } from './event_log_client';

const MAX_LAST_FAILURES = 5;

export class EventLogAdapter implements IRuleExecutionLogClient {
private eventLogClient: EventLogClient;
/**
Expand All @@ -28,38 +35,44 @@ export class EventLogAdapter implements IRuleExecutionLogClient {
*/
private savedObjectsAdapter: IRuleExecutionLogClient;

constructor(eventLogService: IEventLogService, savedObjectsClient: SavedObjectsClientContract) {
this.eventLogClient = new EventLogClient(eventLogService);
constructor(
eventLogService: IEventLogService,
eventLogClient: IEventLogClient,
savedObjectsClient: SavedObjectsClientContract
) {
this.eventLogClient = new EventLogClient(eventLogService, eventLogClient);
this.savedObjectsAdapter = new SavedObjectsAdapter(savedObjectsClient);
}

/** @deprecated */
public async find(args: FindExecutionLogArgs) {
return this.savedObjectsAdapter.find(args);
}

/** @deprecated */
public async findBulk(args: FindBulkExecutionLogArgs) {
return this.savedObjectsAdapter.findBulk(args);
}

public async update(args: UpdateExecutionLogArgs) {
const { attributes, spaceId, ruleId, ruleName, ruleType } = args;
public getLastFailures(args: GetLastFailuresArgs): Promise<IRuleStatusSOAttributes[]> {
const { ruleId } = args;
return this.eventLogClient.getLastStatusChanges({
ruleId,
count: MAX_LAST_FAILURES,
includeStatuses: [RuleExecutionStatus.failed],
});
}

await this.savedObjectsAdapter.update(args);
public getCurrentStatus(args: GetCurrentStatusArgs): Promise<IRuleStatusSOAttributes> {
return this.savedObjectsAdapter.getCurrentStatus(args);
}

// EventLog execution events are immutable, so we just log a status change istead of updating previous
if (attributes.status) {
this.eventLogClient.logStatusChange({
ruleName,
ruleType,
ruleId,
newStatus: attributes.status,
spaceId,
});
}
public getCurrentStatusBulk(args: GetCurrentStatusBulkArgs): Promise<GetCurrentStatusBulkResult> {
return this.savedObjectsAdapter.getCurrentStatusBulk(args);
}

public async delete(id: string) {
await this.savedObjectsAdapter.delete(id);
public async deleteCurrentStatus(ruleId: string): Promise<void> {
await this.savedObjectsAdapter.deleteCurrentStatus(ruleId);

// EventLog execution events are immutable, nothing to do here
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import { SavedObjectsUtils } from '../../../../../../../../src/core/server';
import {
IEventLogClient,
IEventLogger,
IEventLogService,
SAVED_OBJECT_REL_PRIMARY,
} from '../../../../../../event_log/server';
import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas';
import { IRuleStatusSOAttributes } from '../../rules/types';
import { LogStatusChangeArgs } from '../types';
import {
RuleExecutionLogAction,
Expand All @@ -29,13 +31,6 @@ const statusSeverityDict: Record<RuleExecutionStatus, number> = {
[RuleExecutionStatus.failed]: 30,
};

interface FindExecutionLogArgs {
ruleIds: string[];
spaceId: string;
logsCount?: number;
statuses?: RuleExecutionStatus[];
}

interface LogExecutionMetricsArgs {
ruleId: string;
ruleName: string;
Expand All @@ -50,24 +45,80 @@ interface EventLogExecutionMetrics {
executionGapDuration?: number;
}

interface GetLastStatusChangesArgs {
ruleId: string;
count: number;
includeStatuses?: RuleExecutionStatus[];
}

interface IExecLogEventLogClient {
find: (args: FindExecutionLogArgs) => Promise<{}>;
getLastStatusChanges(args: GetLastStatusChangesArgs): Promise<IRuleStatusSOAttributes[]>;
logStatusChange: (args: LogStatusChangeArgs) => void;
logExecutionMetrics: (args: LogExecutionMetricsArgs) => void;
}

export class EventLogClient implements IExecLogEventLogClient {
private readonly eventLogClient: IEventLogClient;
private readonly eventLogger: IEventLogger;
private sequence = 0;
private eventLogger: IEventLogger;

constructor(eventLogService: IEventLogService) {
constructor(eventLogService: IEventLogService, eventLogClient: IEventLogClient) {
this.eventLogClient = eventLogClient;
this.eventLogger = eventLogService.getLogger({
event: { provider: RULE_EXECUTION_LOG_PROVIDER },
});
}

public async find({ ruleIds, spaceId, statuses, logsCount = 1 }: FindExecutionLogArgs) {
return {}; // TODO implement
public async getLastStatusChanges(
args: GetLastStatusChangesArgs
): Promise<IRuleStatusSOAttributes[]> {
const soType = ALERT_SAVED_OBJECT_TYPE;
const soIds = [args.ruleId];
const count = args.count;
const includeStatuses = (args.includeStatuses ?? []).map((status) => `"${status}"`);

const filterBy: string[] = [
`event.provider: ${RULE_EXECUTION_LOG_PROVIDER}`,
'event.kind: event',
`event.action: ${RuleExecutionLogAction['status-change']}`,
includeStatuses.length > 0
? `kibana.alert.rule.execution.status:${includeStatuses.join(' ')}`
: '',
];

const kqlFilter = filterBy
.filter(Boolean)
.map((item) => `(${item})`)
.join(' and ');

const findResult = await this.eventLogClient.findEventsBySavedObjectIds(soType, soIds, {
page: 1,
per_page: count,
sort_field: '@timestamp',
sort_order: 'desc',
filter: kqlFilter,
});

return findResult.data.map((event) => {
const statusDate = event?.['@timestamp'] ?? new Date().toISOString();
const status = event?.kibana?.alert?.rule?.execution?.status as
| RuleExecutionStatus
| undefined;
const message = event?.message ?? '';

return {
statusDate,
status,
lastFailureAt: status === RuleExecutionStatus.failed ? statusDate : undefined,
lastFailureMessage: status === RuleExecutionStatus.failed ? message : undefined,
lastSuccessAt: status !== RuleExecutionStatus.failed ? statusDate : undefined,
lastSuccessMessage: status !== RuleExecutionStatus.failed ? message : undefined,
lastLookBackDate: undefined,
gap: undefined,
bulkCreateTimeDurations: undefined,
searchAfterTimeDurations: undefined,
};
});
}

public logExecutionMetrics({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

import { SavedObjectsClientContract } from '../../../../../../../src/core/server';
import { IEventLogService } from '../../../../../event_log/server';
import { IEventLogClient, IEventLogService } from '../../../../../event_log/server';
import { IRuleStatusSOAttributes } from '../rules/types';
import { EventLogAdapter } from './event_log_adapter/event_log_adapter';
import { SavedObjectsAdapter } from './saved_objects_adapter/saved_objects_adapter';
import {
Expand All @@ -15,58 +16,61 @@ import {
FindExecutionLogArgs,
IRuleExecutionLogClient,
LogStatusChangeArgs,
UpdateExecutionLogArgs,
UnderlyingLogClient,
GetLastFailuresArgs,
GetCurrentStatusArgs,
GetCurrentStatusBulkArgs,
GetCurrentStatusBulkResult,
} from './types';
import { truncateMessage } from './utils/normalization';

export interface RuleExecutionLogClientArgs {
savedObjectsClient: SavedObjectsClientContract;
eventLogService: IEventLogService;
interface ConstructorParams {
underlyingClient: UnderlyingLogClient;
eventLogService: IEventLogService;
eventLogClient: IEventLogClient;
savedObjectsClient: SavedObjectsClientContract;
}

export class RuleExecutionLogClient implements IRuleExecutionLogClient {
private client: IRuleExecutionLogClient;

constructor({
savedObjectsClient,
eventLogService,
underlyingClient,
}: RuleExecutionLogClientArgs) {
constructor(params: ConstructorParams) {
const { underlyingClient, eventLogService, eventLogClient, savedObjectsClient } = params;

switch (underlyingClient) {
case UnderlyingLogClient.savedObjects:
this.client = new SavedObjectsAdapter(savedObjectsClient);
break;
case UnderlyingLogClient.eventLog:
this.client = new EventLogAdapter(eventLogService, savedObjectsClient);
this.client = new EventLogAdapter(eventLogService, eventLogClient, savedObjectsClient);
break;
}
}

/** @deprecated */
public find(args: FindExecutionLogArgs) {
return this.client.find(args);
}

/** @deprecated */
public findBulk(args: FindBulkExecutionLogArgs) {
return this.client.findBulk(args);
}

public async update(args: UpdateExecutionLogArgs) {
const { lastFailureMessage, lastSuccessMessage, ...restAttributes } = args.attributes;
public getLastFailures(args: GetLastFailuresArgs): Promise<IRuleStatusSOAttributes[]> {
return this.client.getLastFailures(args);
}

return this.client.update({
...args,
attributes: {
lastFailureMessage: truncateMessage(lastFailureMessage),
lastSuccessMessage: truncateMessage(lastSuccessMessage),
...restAttributes,
},
});
public getCurrentStatus(args: GetCurrentStatusArgs): Promise<IRuleStatusSOAttributes> {
return this.client.getCurrentStatus(args);
}

public getCurrentStatusBulk(args: GetCurrentStatusBulkArgs): Promise<GetCurrentStatusBulkResult> {
return this.client.getCurrentStatusBulk(args);
}

public async delete(id: string) {
return this.client.delete(id);
public deleteCurrentStatus(ruleId: string): Promise<void> {
return this.client.deleteCurrentStatus(ruleId);
}

public async logExecutionMetrics(args: LogExecutionMetricsArgs) {
Expand Down
Loading

0 comments on commit fb92607

Please sign in to comment.