Skip to content

Commit 4313930

Browse files
authored
Alert doesn't fire action if it's muted or throttled (#124775) (#125562)
* Alert doesn't fire action if it's muted or throttled (cherry picked from commit e4dd6e7) # Conflicts: # x-pack/plugins/alerting/server/task_runner/task_runner.test.ts # x-pack/plugins/alerting/server/task_runner/task_runner.ts
1 parent bbefaf0 commit 4313930

File tree

2 files changed

+163
-35
lines changed

2 files changed

+163
-35
lines changed

x-pack/plugins/alerting/server/task_runner/task_runner.test.ts

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { omit } from 'lodash';
4040
import { UntypedNormalizedAlertType } from '../rule_type_registry';
4141
import { ruleTypeRegistryMock } from '../rule_type_registry.mock';
4242
import { ExecuteOptions } from '../../../actions/server/create_execute_function';
43+
import moment from 'moment';
4344

4445
const alertType: jest.Mocked<UntypedNormalizedAlertType> = {
4546
id: 'test',
@@ -55,6 +56,10 @@ const alertType: jest.Mocked<UntypedNormalizedAlertType> = {
5556

5657
let fakeTimer: sinon.SinonFakeTimers;
5758

59+
export const mockRunNowResponse = {
60+
id: 1,
61+
} as jest.ResolvedValue<unknown>;
62+
5863
describe('Task Runner', () => {
5964
let mockedTaskInstance: ConcreteTaskInstance;
6065

@@ -865,7 +870,136 @@ describe('Task Runner', () => {
865870
}
866871
);
867872

868-
test('actionsPlugin.execute is not called when notifyWhen=onActionGroupChange and alert instance state does not change', async () => {
873+
testAgainstEphemeralSupport(
874+
'skips firing actions for active alert if alert is throttled %s',
875+
(
876+
customTaskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType,
877+
enqueueFunction: (options: ExecuteOptions) => Promise<void | RunNowResult>
878+
) =>
879+
async () => {
880+
(
881+
customTaskRunnerFactoryInitializerParams as TaskRunnerFactoryInitializerParamsType
882+
).actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
883+
customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(
884+
true
885+
);
886+
actionsClient.ephemeralEnqueuedExecution.mockResolvedValue(mockRunNowResponse);
887+
alertType.executor.mockImplementation(
888+
async ({
889+
services: executorServices,
890+
}: AlertExecutorOptions<
891+
AlertTypeParams,
892+
AlertTypeState,
893+
AlertInstanceState,
894+
AlertInstanceContext,
895+
string
896+
>) => {
897+
executorServices.alertInstanceFactory('1').scheduleActions('default');
898+
executorServices.alertInstanceFactory('2').scheduleActions('default');
899+
}
900+
);
901+
const taskRunner = new TaskRunner(
902+
alertType,
903+
{
904+
...mockedTaskInstance,
905+
state: {
906+
...mockedTaskInstance.state,
907+
alertInstances: {
908+
'2': {
909+
meta: {
910+
lastScheduledActions: { date: moment().toISOString(), group: 'default' },
911+
},
912+
state: {
913+
bar: false,
914+
start: '1969-12-31T00:00:00.000Z',
915+
duration: 86400000000000,
916+
},
917+
},
918+
},
919+
},
920+
},
921+
taskRunnerFactoryInitializerParams
922+
);
923+
rulesClient.get.mockResolvedValue({
924+
...mockedAlertTypeSavedObject,
925+
throttle: '1d',
926+
});
927+
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({
928+
id: '1',
929+
type: 'alert',
930+
attributes: {
931+
apiKey: Buffer.from('123:abc').toString('base64'),
932+
enabled: true,
933+
},
934+
references: [],
935+
});
936+
await taskRunner.run();
937+
// expect(enqueueFunction).toHaveBeenCalledTimes(1);
938+
939+
const logger = customTaskRunnerFactoryInitializerParams.logger;
940+
expect(logger.debug).toHaveBeenCalledTimes(4);
941+
expect(logger.debug).nthCalledWith(
942+
3,
943+
`skipping scheduling of actions for '2' in alert test:1: 'alert-name': instance is throttled`
944+
);
945+
}
946+
);
947+
948+
testAgainstEphemeralSupport(
949+
'skips firing actions for active alert when alert is muted even if notifyWhen === onActionGroupChange %s',
950+
(
951+
customTaskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType,
952+
enqueueFunction: (options: ExecuteOptions) => Promise<void | RunNowResult>
953+
) =>
954+
async () => {
955+
customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(
956+
true
957+
);
958+
alertType.executor.mockImplementation(
959+
async ({
960+
services: executorServices,
961+
}: AlertExecutorOptions<
962+
AlertTypeParams,
963+
AlertTypeState,
964+
AlertInstanceState,
965+
AlertInstanceContext,
966+
string
967+
>) => {
968+
executorServices.alertInstanceFactory('1').scheduleActions('default');
969+
executorServices.alertInstanceFactory('2').scheduleActions('default');
970+
}
971+
);
972+
const taskRunner = new TaskRunner(
973+
alertType,
974+
mockedTaskInstance,
975+
customTaskRunnerFactoryInitializerParams
976+
);
977+
rulesClient.get.mockResolvedValue({
978+
...mockedAlertTypeSavedObject,
979+
mutedInstanceIds: ['2'],
980+
notifyWhen: 'onActionGroupChange',
981+
});
982+
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({
983+
id: '1',
984+
type: 'alert',
985+
attributes: {
986+
apiKey: Buffer.from('123:abc').toString('base64'),
987+
enabled: true,
988+
},
989+
references: [],
990+
});
991+
await taskRunner.run();
992+
expect(enqueueFunction).toHaveBeenCalledTimes(1);
993+
const logger = customTaskRunnerFactoryInitializerParams.logger;
994+
expect(logger.debug).toHaveBeenCalledTimes(4);
995+
expect(logger.debug).nthCalledWith(
996+
3,
997+
`skipping scheduling of actions for '2' in alert test:1: 'alert-name': instance is muted`
998+
);
999+
}
1000+
);
1001+
1002+
test('actionsPlugin.execute is not called when notifyWhen=onActionGroupChange and alert state does not change', async () => {
8691003
taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
8701004
taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true);
8711005
alertType.executor.mockImplementation(

x-pack/plugins/alerting/server/task_runner/task_runner.ts

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -379,41 +379,35 @@ export class TaskRunner<
379379
alertLabel,
380380
});
381381

382-
const instancesToExecute =
383-
notifyWhen === 'onActionGroupChange'
384-
? Object.entries(instancesWithScheduledActions).filter(
385-
([alertInstanceName, alertInstance]: [
386-
string,
387-
AlertInstance<InstanceState, InstanceContext>
388-
]) => {
389-
const shouldExecuteAction =
390-
alertInstance.scheduledActionGroupOrSubgroupHasChanged();
391-
if (!shouldExecuteAction) {
392-
this.logger.debug(
393-
`skipping scheduling of actions for '${alertInstanceName}' in alert ${alertLabel}: instance is active but action group has not changed`
394-
);
395-
}
396-
return shouldExecuteAction;
397-
}
398-
)
399-
: Object.entries(instancesWithScheduledActions).filter(
400-
([alertInstanceName, alertInstance]: [
401-
string,
402-
AlertInstance<InstanceState, InstanceContext>
403-
]) => {
404-
const throttled = alertInstance.isThrottled(throttle);
405-
const muted = mutedInstanceIdsSet.has(alertInstanceName);
406-
const shouldExecuteAction = !throttled && !muted;
407-
if (!shouldExecuteAction) {
408-
this.logger.debug(
409-
`skipping scheduling of actions for '${alertInstanceName}' in alert ${alertLabel}: instance is ${
410-
muted ? 'muted' : 'throttled'
411-
}`
412-
);
413-
}
414-
return shouldExecuteAction;
415-
}
382+
const instancesToExecute = Object.entries(instancesWithScheduledActions).filter(
383+
([alertInstanceName, alertInstance]: [
384+
string,
385+
AlertInstance<InstanceState, InstanceContext>
386+
]) => {
387+
const throttled = alertInstance.isThrottled(throttle);
388+
const muted = mutedInstanceIdsSet.has(alertInstanceName);
389+
let shouldExecuteAction = true;
390+
391+
if (throttled || muted) {
392+
shouldExecuteAction = false;
393+
this.logger.debug(
394+
`skipping scheduling of actions for '${alertInstanceName}' in alert ${alertLabel}: instance is ${
395+
muted ? 'muted' : 'throttled'
396+
}`
416397
);
398+
} else if (
399+
notifyWhen === 'onActionGroupChange' &&
400+
!alertInstance.scheduledActionGroupOrSubgroupHasChanged()
401+
) {
402+
shouldExecuteAction = false;
403+
this.logger.debug(
404+
`skipping scheduling of actions for '${alertInstanceName}' in alert ${alertLabel}: instance is active but action group has not changed`
405+
);
406+
}
407+
408+
return shouldExecuteAction;
409+
}
410+
);
417411

418412
await Promise.all(
419413
instancesToExecute.map(

0 commit comments

Comments
 (0)