Alert doesn't fire action if it's muted or throttled (#124775)

* Alert doesn't fire action if it's muted or throttled
This commit is contained in:
Ersin Erdal 2022-02-09 01:39:46 +01:00 committed by GitHub
parent f85e59dd08
commit e4dd6e769c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 148 additions and 28 deletions

View file

@ -41,6 +41,7 @@ import { omit } from 'lodash';
import { UntypedNormalizedRuleType } from '../rule_type_registry';
import { ruleTypeRegistryMock } from '../rule_type_registry.mock';
import { ExecuteOptions } from '../../../actions/server/create_execute_function';
import moment from 'moment';
jest.mock('uuid', () => ({
v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28',
@ -978,7 +979,128 @@ describe('Task Runner', () => {
}
);
test('actionsPlugin.execute is not called when notifyWhen=onActionGroupChange and alert alert state does not change', async () => {
test.each(ephemeralTestParams)(
'skips firing actions for active alert if alert is throttled %s',
async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => {
(
customTaskRunnerFactoryInitializerParams as TaskRunnerFactoryInitializerParamsType
).actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(
true
);
actionsClient.ephemeralEnqueuedExecution.mockResolvedValue(mockRunNowResponse);
ruleType.executor.mockImplementation(
async ({
services: executorServices,
}: AlertExecutorOptions<
AlertTypeParams,
AlertTypeState,
AlertInstanceState,
AlertInstanceContext,
string
>) => {
executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertFactory.create('2').scheduleActions('default');
}
);
const taskRunner = new TaskRunner(
ruleType,
{
...mockedTaskInstance,
state: {
...mockedTaskInstance.state,
alertInstances: {
'2': {
meta: {
lastScheduledActions: { date: moment().toISOString(), group: 'default' },
},
state: {
bar: false,
start: '1969-12-31T00:00:00.000Z',
duration: 86400000000000,
},
},
},
},
},
taskRunnerFactoryInitializerParams
);
rulesClient.get.mockResolvedValue({
...mockedRuleTypeSavedObject,
throttle: '1d',
});
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({
id: '1',
type: 'alert',
attributes: {
apiKey: Buffer.from('123:abc').toString('base64'),
enabled: true,
},
references: [],
});
await taskRunner.run();
// expect(enqueueFunction).toHaveBeenCalledTimes(1);
const logger = customTaskRunnerFactoryInitializerParams.logger;
// expect(logger.debug).toHaveBeenCalledTimes(5);
expect(logger.debug).nthCalledWith(
3,
`skipping scheduling of actions for '2' in rule test:1: 'rule-name': rule is throttled`
);
}
);
test.each(ephemeralTestParams)(
'skips firing actions for active alert when alert is muted even if notifyWhen === onActionGroupChange %s',
async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => {
customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(
true
);
ruleType.executor.mockImplementation(
async ({
services: executorServices,
}: AlertExecutorOptions<
AlertTypeParams,
AlertTypeState,
AlertInstanceState,
AlertInstanceContext,
string
>) => {
executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertFactory.create('2').scheduleActions('default');
}
);
const taskRunner = new TaskRunner(
ruleType,
mockedTaskInstance,
customTaskRunnerFactoryInitializerParams
);
rulesClient.get.mockResolvedValue({
...mockedRuleTypeSavedObject,
mutedInstanceIds: ['2'],
notifyWhen: 'onActionGroupChange',
});
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({
id: '1',
type: 'alert',
attributes: {
apiKey: Buffer.from('123:abc').toString('base64'),
enabled: true,
},
references: [],
});
await taskRunner.run();
expect(enqueueFunction).toHaveBeenCalledTimes(1);
const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(5);
expect(logger.debug).nthCalledWith(
3,
`skipping scheduling of actions for '2' in rule test:1: 'rule-name': rule is muted`
);
}
);
test('actionsPlugin.execute is not called when notifyWhen=onActionGroupChange and alert state does not change', async () => {
taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true);
ruleType.executor.mockImplementation(

View file

@ -477,34 +477,32 @@ export class TaskRunner<
triggeredActions = concat(triggeredActions, scheduledActionsForRecoveredAlerts);
const alertsToExecute =
notifyWhen === 'onActionGroupChange'
? Object.entries(alertsWithScheduledActions).filter(
([alertName, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) => {
const shouldExecuteAction = alert.scheduledActionGroupOrSubgroupHasChanged();
if (!shouldExecuteAction) {
this.logger.debug(
`skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: alert is active but action group has not changed`
);
}
return shouldExecuteAction;
}
)
: Object.entries(alertsWithScheduledActions).filter(
([alertName, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) => {
const throttled = alert.isThrottled(throttle);
const muted = mutedAlertIdsSet.has(alertName);
const shouldExecuteAction = !throttled && !muted;
if (!shouldExecuteAction) {
this.logger.debug(
`skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: rule is ${
muted ? 'muted' : 'throttled'
}`
);
}
return shouldExecuteAction;
}
const alertsToExecute = Object.entries(alertsWithScheduledActions).filter(
([alertName, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) => {
const throttled = alert.isThrottled(throttle);
const muted = mutedAlertIdsSet.has(alertName);
let shouldExecuteAction = true;
if (throttled || muted) {
shouldExecuteAction = false;
this.logger.debug(
`skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: rule is ${
muted ? 'muted' : 'throttled'
}`
);
} else if (
notifyWhen === 'onActionGroupChange' &&
!alert.scheduledActionGroupOrSubgroupHasChanged()
) {
shouldExecuteAction = false;
this.logger.debug(
`skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: alert is active but action group has not changed`
);
}
return shouldExecuteAction;
}
);
const allTriggeredActions = await Promise.all(
alertsToExecute.map(