Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] [Response Ops] Onboard detection rules to use alerting framework summaries. #147539

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
600af4f
Get and inject the alerts summary data in to actions
ersin-erdal Dec 12, 2022
e64eb64
Fix throttling action bug
ersin-erdal Dec 12, 2022
20e2ed1
Fix throttling action bug
ersin-erdal Dec 12, 2022
0525af6
Fix throttling action bug
ersin-erdal Dec 12, 2022
4204595
fix hasThrottle check
ersin-erdal Dec 12, 2022
7d21829
fix typos
ersin-erdal Dec 13, 2022
3d137a1
change summary action execution order, move action functions to helper
ersin-erdal Dec 13, 2022
7a8b83c
Merge branch 'main' into 143376-alerts-summary
ersin-erdal Dec 13, 2022
eda6aa3
Initial commit to set frequency within each rule action
ymao1 Dec 13, 2022
441427f
Commenting out schedule actions inside create security rule type wrap…
ymao1 Dec 14, 2022
f267e88
add integration tests
ersin-erdal Dec 14, 2022
3728778
Merge remote-tracking branch 'origin/143376-alerts-summary' into 1433…
ersin-erdal Dec 14, 2022
c0fdb1c
Merge branch 'main' of https://github.com/elastic/kibana into poc/onb…
ymao1 Dec 14, 2022
81b1a67
Merge branch 'pr/147360' into poc/onboard-detection-rules
ymao1 Dec 14, 2022
8281156
add getSummarizedAlerts check
ersin-erdal Dec 14, 2022
25213b6
skip scheduling summary action when there is no alerts
ersin-erdal Dec 14, 2022
526f376
Adding autoRecoverAlerts flag to rule type to determine whether recov…
ymao1 Dec 14, 2022
66e1d4a
merge
ersin-erdal Dec 14, 2022
d7b8c43
refactor integration tests
ersin-erdal Dec 14, 2022
b289217
Moving security context variables to server. Adding summaryBuilder fu…
ymao1 Dec 14, 2022
b1c0cc9
Merge branch 'pr/147360' into poc/onboard-detection-rules
ymao1 Dec 14, 2022
e732c7b
Merging in main
ymao1 Dec 15, 2022
24881f1
Aliasing context variables
ymao1 Dec 15, 2022
82fd509
Cleanup
ymao1 Dec 15, 2022
9bd69a0
Fixing result_link
ymao1 Dec 16, 2022
63393ed
Merging in main
ymao1 Dec 20, 2022
acd919b
Formatting alerts
ymao1 Dec 20, 2022
8320dd6
Setting throttle interval always
ymao1 Dec 20, 2022
4d86684
Add frequency support on legacy action migration
e40pud Jan 9, 2023
6a27c94
Merge pull request #11 from e40pud/poc/onboard-detection-rules-frequency
ymao1 Jan 9, 2023
650d275
Merging in main
ymao1 Feb 9, 2023
dea0cdd
Merge branch 'poc/onboard-detection-rules' of github.com:ymao1/kibana…
ymao1 Feb 9, 2023
9406c5b
Merging in main
ymao1 Feb 15, 2023
cf9be1e
Cleanup
ymao1 Feb 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions x-pack/plugins/alerting/common/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,25 @@ export interface AlertsHealth {
};
}

export interface SummarizedAlertsWithAll {
new: {
count: number;
data: unknown[];
};
ongoing: {
count: number;
data: unknown[];
};
recovered: {
count: number;
data: unknown[];
};
all: {
count: number;
data: unknown[];
};
}

export interface ActionVariable {
name: string;
description: string;
Expand Down
61 changes: 55 additions & 6 deletions x-pack/plugins/alerting/server/task_runner/execution_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ExecuteOptions as EnqueueExecutionOptions } from '@kbn/actions-plugin/s
import { ActionsClient } from '@kbn/actions-plugin/server/actions_client';
import { chunk } from 'lodash';
import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger';
import { parseDuration, RawRule, ThrottledActions } from '../types';
import { GetRuleUrlFnOpts, parseDuration, RawRule, ThrottledActions } from '../types';
import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store';
import { injectActionParams } from './inject_action_params';
import { ExecutionHandlerOptions, RuleTaskInstance } from './types';
Expand Down Expand Up @@ -201,7 +201,7 @@ export class ExecutionHandler<
if (isSummaryActionPerRuleRun(action) && !this.hasAlerts(alerts)) {
continue;
}
const summarizedAlerts = await this.getSummarizedAlerts({
const { startMs, endMs, summarizedAlerts } = await this.getSummarizedAlerts({
action,
spaceId,
ruleId,
Expand All @@ -222,7 +222,13 @@ export class ExecutionHandler<
actionsPlugin,
actionTypeId,
kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl,
ruleUrl: this.buildRuleUrl(spaceId),
ruleUrl: this.buildRuleUrl({
id: ruleId,
spaceId,
params: this.rule.params,
startMs,
endMs,
}),
}),
}),
};
Expand Down Expand Up @@ -270,7 +276,11 @@ export class ExecutionHandler<
kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl,
alertParams: this.rule.params,
actionParams: action.params,
ruleUrl: this.buildRuleUrl(spaceId),
ruleUrl: this.buildRuleUrl({
id: ruleId,
spaceId,
params: this.rule.params,
}),
flapping: executableAlert.getFlapping(),
}),
}),
Expand Down Expand Up @@ -404,7 +414,24 @@ export class ExecutionHandler<
return alert.getScheduledActionOptions()?.actionGroup || this.ruleType.recoveryActionGroup.id;
}

private buildRuleUrl(spaceId: string): string | undefined {
private buildRuleUrl({
id,
params,
spaceId,
startMs,
endMs,
}: GetRuleUrlFnOpts<Params>): string | undefined {
// Use the rule type's getRuleUrl callback if defined
// This does not necessarily require `kibanaBaseUrl` to be defined
const ruleTypeUrl = this.ruleType.getRuleUrl
? this.ruleType?.getRuleUrl({ id, params, spaceId, startMs, endMs })
: null;

if (ruleTypeUrl) {
return ruleTypeUrl;
}

// Fallback to generic rule urle
if (!this.taskRunnerContext.kibanaBaseUrl) {
return;
}
Expand Down Expand Up @@ -546,13 +573,35 @@ export class ExecutionHandler<
const alerts = await this.ruleType.getSummarizedAlerts!(options);

const total = alerts.new.count + alerts.ongoing.count + alerts.recovered.count;
return {
const summarizedAlerts = {
...alerts,
all: {
count: total,
data: [...alerts.new.data, ...alerts.ongoing.data, ...alerts.recovered.data],
},
};

if (summarizedAlerts.all.count > 0) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns the time bounds for summarized alerts that can be used for building rule URLs. This ensures that the time bounds used to load alerts in the UI matches the time bounds for the alert summary so the alert counts match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating it from the summarized alerts instead of using existing time bounds because when we query for alerts per rule execution UUID, we don't have existing time bounds.

// get the time bounds for this alert array
const timestampMillis: number[] = summarizedAlerts.all.data
.map((alert: unknown) => {
const timestamp = (alert as { '@timestamp': string })['@timestamp'];
if (timestamp) {
return new Date(timestamp).valueOf();
}
return null;
})
.filter((timeInMillis: number | null) => null != timeInMillis)
.sort() as number[];

return {
startMs: timestampMillis[0],
endMs: timestampMillis[timestampMillis.length - 1],
summarizedAlerts,
};
}

return { summarizedAlerts };
}

private async actionRunOrAddToBulk({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
AlertInstanceContext,
RuleTypeParams,
SanitizedRule,
SummarizedAlertsWithAll,
} from '../types';

interface TransformActionParamsOptions {
Expand All @@ -35,25 +36,6 @@ interface TransformActionParamsOptions {
flapping: boolean;
}

interface SummarizedAlertsWithAll {
new: {
count: number;
data: unknown[];
};
ongoing: {
count: number;
data: unknown[];
};
recovered: {
count: number;
data: unknown[];
};
all: {
count: number;
data: unknown[];
};
}

export function transformActionParams({
actionsPlugin,
alertId,
Expand Down Expand Up @@ -140,6 +122,15 @@ export function transformSummaryActionParams({
const variables = {
kibanaBaseUrl,
date: new Date().toISOString(),
// For backwards compatibility with security solutions rules
context: {
alerts: alerts.new.data,
results_link: ruleUrl,
rule: rule.params,
},
state: {
signals_count: alerts.new.count,
},
rule: {
params: rule.params,
id: rule.id,
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/alerting/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,16 @@ export interface IRuleTypeAlerts {
fieldMap: FieldMap;
}

export interface GetRuleUrlFnOpts<Params extends RuleTypeParams> {
id: string;
params: Params;
spaceId: string;
startMs?: number;
endMs?: number;
}
export type GetRuleUrlFn<Params extends RuleTypeParams> = (
opts: GetRuleUrlFnOpts<Params>
) => string | null;
export interface RuleType<
Params extends RuleTypeParams = never,
ExtractedParams extends RuleTypeParams = never,
Expand Down Expand Up @@ -212,6 +222,8 @@ export interface RuleType<
cancelAlertsOnRuleTimeout?: boolean;
doesSetRecoveryContext?: boolean;
getSummarizedAlerts?: GetSummarizedAlertsFn;
getRuleUrl?: GetRuleUrlFn<Params>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allows rule types to specify custom function for building rule URLs


alerts?: IRuleTypeAlerts;
/**
* Determines whether framework should
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const mapAlertsToBulkCreate = <T>(alerts: Array<{ _id: string; _source: T }>) =>
};

export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper =
({ logger, ruleDataClient }) =>
({ logger, ruleDataClient, formatAlert }) =>
(type) => {
return {
...type,
Expand Down Expand Up @@ -160,17 +160,23 @@ export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper
return { createdAlerts: [], errors: {}, alertsWereTruncated };
}

const createdAlerts = augmentedAlerts
.map((alert, idx) => {
const responseItem = response.body.items[idx].create;
return {
_id: responseItem?._id ?? '',
_index: responseItem?._index ?? '',
...alert._source,
};
})
.filter((_, idx) => response.body.items[idx].create?.status === 201);

createdAlerts.forEach((alert) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reporting alerts 1-1 back to the framework.

options.services.alertFactory.create(alert._id).scheduleActions('default', {})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
options.services.alertFactory.create(alert._id).scheduleActions('default', {})
options.services.alertFactory.create(alert._id).scheduleActions('default', {
alerts: [alert],
results_link: <ruleUrl>
}).replaceState({ signals_count: 1});

);

return {
createdAlerts: augmentedAlerts
.map((alert, idx) => {
const responseItem = response.body.items[idx].create;
return {
_id: responseItem?._id ?? '',
_index: responseItem?._index ?? '',
...alert._source,
};
})
.filter((_, idx) => response.body.items[idx].create?.status === 201),
createdAlerts,
errors: errorAggregator(response.body, [409]),
alertsWereTruncated,
};
Expand Down Expand Up @@ -356,6 +362,7 @@ export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper
ruleDataClient,
useNamespace: true,
isLifecycleAlert: false,
formatAlert,
})(),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export type PersistenceAlertType<
export type CreatePersistenceRuleTypeWrapper = (options: {
ruleDataClient: IRuleDataClient;
logger: Logger;
formatAlert?: (alert: unknown) => unknown;
}) => <
TParams extends RuleTypeParams,
TState extends RuleTypeState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,18 @@ export const normalizeAlertForNotificationActions = (alert: DetectionAlert) => {
* the equivalent "legacy" alert context so that pre-8.0 actions will continue to work.
*/
export const formatAlertsForNotificationActions = (alerts: unknown[]): unknown[] => {
return alerts.map((alert) => {
if (isDetectionAlert(alert)) {
const normalizedAlert = normalizeAlertForNotificationActions(alert);
return {
...expandDottedObject(convertToLegacyAlert(normalizedAlert)),
...expandDottedObject(normalizedAlert),
};
}
return alert;
});
return alerts.map((alert) => formatAlertForNotificationActions(alert));
};

export const formatAlertForNotificationActions = (alert: unknown): unknown => {
if (isDetectionAlert(alert)) {
const normalizedAlert = normalizeAlertForNotificationActions(alert);
return {
...expandDottedObject(convertToLegacyAlert(normalizedAlert)),
...expandDottedObject(normalizedAlert),
};
}
return alert;
};

interface ScheduleNotificationActions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,27 @@ export const updateRules = async ({
...typeSpecificParams,
},
schedule: { interval: ruleUpdate.interval ?? '5m' },
actions: ruleUpdate.actions != null ? ruleUpdate.actions.map(transformRuleToAlertAction) : [],
throttle: transformToAlertThrottle(ruleUpdate.throttle),
notifyWhen: transformToNotifyWhen(ruleUpdate.throttle),
actions:
ruleUpdate.actions != null
? ruleUpdate.actions?.map((action) => {
const alertAction = transformRuleToAlertAction(action);
const notifyWhen = transformToNotifyWhen(ruleUpdate.throttle);
let throttle = transformToAlertThrottle(ruleUpdate.throttle);

// Uses the schedule interval as throttle when the throttle interval is null
if (!throttle && notifyWhen === 'onActiveAlert') {
throttle = ruleUpdate.interval ?? '5m';
}
return {
...alertAction,
frequency: {
summary: true,
notifyWhen: 'onThrottleInterval',
throttle: throttle === '1h' ? '5m' : throttle,
},
};
})
: [],
};

const update = await rulesClient.update({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ export const getUpdatedActionsParams = ({
...resOfAction,
id: actionReference[actionRef].id,
actionTypeId,
frequency: {
summary: true,
notifyWhen: transformToNotifyWhen(ruleThrottle) ?? 'onThrottleInterval',
throttle: transformToAlertThrottle(ruleThrottle),
},
},
];
}, []),
Expand Down
Loading