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

[Alerting] Alerting now uses Task Manager intervals internally #51182

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1082f23
feat(update-scheduled-task): Adds getTask api to Task Store
gmmorris Nov 14, 2019
201748f
feat(update-scheduled-task): Adds reschedule api to Task Manager
gmmorris Nov 18, 2019
1d78a86
Merge branch 'master' into task-manager/update-scheduled-task
gmmorris Nov 18, 2019
ea0b47a
refactor(update-scheduled-task): clean up reschedule code
gmmorris Nov 18, 2019
be1d700
refactor(update-scheduled-task): separate default value from serialis…
gmmorris Nov 18, 2019
2a289cd
Merge remote-tracking branch 'upstream/master' into task-manager/upda…
gmmorris Nov 19, 2019
9220454
feat(update-scheduled-task): simplified reschedule api to reuse exist…
gmmorris Nov 19, 2019
dbdc3f8
Merge branch 'master' into task-manager/update-scheduled-task
gmmorris Nov 19, 2019
fce851d
refactor(update-scheduled-task): removed unneeded interval func
gmmorris Nov 19, 2019
baec6e7
refactor(update-scheduled-task): reuse type
gmmorris Nov 20, 2019
e04cc60
refactor(update-scheduled-task): clean up task serialisation
gmmorris Nov 20, 2019
6fe4f78
doc(update-scheduled-task): documented `reschedule` api in README
gmmorris Nov 20, 2019
9e4cc56
Merge branch 'master' into task-manager/update-scheduled-task
gmmorris Nov 20, 2019
bf24701
doc(update-scheduled-task): corrected description of type
gmmorris Nov 20, 2019
bae03a4
feat(alerting-interval): use TaskManager intervals in Alerting
gmmorris Nov 20, 2019
cd64151
renamed rescheduling type
gmmorris Nov 20, 2019
67c651c
renamed methjods in store to reflect subject
gmmorris Nov 20, 2019
cbfc42c
explicitly pick out properties in serialisation
gmmorris Nov 20, 2019
b691b1e
introduce retry into Task rescheduling
gmmorris Nov 21, 2019
ed78933
Merge branch 'master' into task-manager/update-scheduled-task
gmmorris Nov 21, 2019
0abcf92
Merge branch 'master' into task-manager/update-scheduled-task
gmmorris Nov 21, 2019
0170202
Merge branch 'task-manager/update-scheduled-task' into alerting/interval
gmmorris Nov 21, 2019
e0690c4
Merge branch 'master' into alerting/interval
elasticmachine Nov 21, 2019
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
187 changes: 169 additions & 18 deletions x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,25 @@ describe('create()', () => {
`);
expect(taskManager.schedule).toHaveBeenCalledTimes(1);
expect(taskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"params": Object {
"alertId": "1",
"spaceId": "default",
},
"scope": Array [
"alerting",
],
"state": Object {
"alertInstances": Object {},
"alertTypeState": Object {},
"previousStartedAt": null,
},
"taskType": "alerting:123",
},
]
`);
Array [
Object {
"interval": "10s",
"params": Object {
"alertId": "1",
"spaceId": "default",
},
"scope": Array [
"alerting",
],
"state": Object {
"alertInstances": Object {},
"alertTypeState": Object {},
"previousStartedAt": null,
},
"taskType": "alerting:123",
},
]
`);
expect(savedObjectsClient.update).toHaveBeenCalledTimes(1);
expect(savedObjectsClient.update.mock.calls[0]).toHaveLength(3);
expect(savedObjectsClient.update.mock.calls[0][0]).toEqual('alert');
Expand Down Expand Up @@ -583,6 +584,7 @@ describe('enable()', () => {
);
expect(taskManager.schedule).toHaveBeenCalledWith({
taskType: `alerting:2`,
interval: '10s',
params: {
alertId: '1',
spaceId: 'default',
Expand Down Expand Up @@ -664,6 +666,7 @@ describe('enable()', () => {
);
expect(taskManager.schedule).toHaveBeenCalledWith({
taskType: `alerting:2`,
interval: '10s',
params: {
alertId: '1',
spaceId: 'default',
Expand Down Expand Up @@ -1262,6 +1265,154 @@ describe('update()', () => {
`);
});

test('reschedules the Alert Task if the interval is changed', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
alertTypeRegistry.get.mockReturnValueOnce({
id: '123',
name: 'Test',
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.get.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
alertTypeId: '123',
scheduledTaskId: 'task-123',
},
references: [],
version: '123',
});
savedObjectsClient.update.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
interval: '10s',
alertTypeParams: {
bar: true,
},
actions: [
{
group: 'default',
actionRef: 'action_0',
params: {
foo: true,
},
},
],
scheduledTaskId: 'task-123',
},
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
});
await alertsClient.update({
id: '1',
data: {
interval: '100s',
name: 'abc',
tags: ['foo'],
alertTypeParams: {
bar: true,
},
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
],
},
});

expect(taskManager.reschedule.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
"id": "task-123",
"interval": "100s",
}
`);
});

test('does not reschedule the Alert Task if the interval is unchanged', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
alertTypeRegistry.get.mockReturnValueOnce({
id: '123',
name: 'Test',
actionGroups: ['default'],
async executor() {},
});
savedObjectsClient.get.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
interval: '10s',
alertTypeId: '123',
scheduledTaskId: 'task-123',
},
references: [],
version: '123',
});
savedObjectsClient.update.mockResolvedValueOnce({
id: '1',
type: 'alert',
attributes: {
enabled: true,
interval: '10s',
alertTypeParams: {
bar: true,
},
actions: [
{
group: 'default',
actionRef: 'action_0',
params: {
foo: true,
},
},
],
scheduledTaskId: 'task-123',
},
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
});
await alertsClient.update({
id: '1',
data: {
interval: '10s',
name: 'abc',
tags: ['foo'],
alertTypeParams: {
bar: true,
},
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
],
},
});

expect(taskManager.reschedule).not.toHaveBeenCalled();
});

it('calls the createApiKey function', async () => {
const alertsClient = new AlertsClient(alertsClientParams);
alertTypeRegistry.get.mockReturnValueOnce({
Expand Down
14 changes: 13 additions & 1 deletion x-pack/legacy/plugins/alerting/server/alerts_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export class AlertsClient {
scheduledTask = await this.scheduleAlert(
createdAlert.id,
rawAlert.alertTypeId,
rawAlert.interval
data.interval
);
} catch (e) {
// Cleanup data, something went wrong scheduling the task
Expand Down Expand Up @@ -223,6 +223,10 @@ export class AlertsClient {
references,
}
);
if (data.interval !== attributes.interval) {
// if interval has changed, we should reschedule the task
await this.rescheduleAlert(attributes.scheduledTaskId, data.interval);
}
return this.getAlertFromRaw(id, updatedObject.attributes, updatedObject.references);
}

Expand Down Expand Up @@ -358,6 +362,7 @@ export class AlertsClient {
private async scheduleAlert(id: string, alertTypeId: string, interval: string) {
return await this.taskManager.schedule({
taskType: `alerting:${alertTypeId}`,
interval,
params: {
alertId: id,
spaceId: this.spaceId,
Expand All @@ -371,6 +376,13 @@ export class AlertsClient {
});
}

private async rescheduleAlert(scheduledTaskId: string, interval: string) {
return await this.taskManager.reschedule({
id: scheduledTaskId,
interval,
});
}

private extractReferences(actions: Alert['actions']) {
const references: SavedObjectReference[] = [];
const rawActions = actions.map((action, i) => {
Expand Down
26 changes: 0 additions & 26 deletions x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts

This file was deleted.

16 changes: 0 additions & 16 deletions x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ test('successfully executes the task', async () => {
const runnerResult = await taskRunner.run();
expect(runnerResult).toMatchInlineSnapshot(`
Object {
"runAt": 1970-01-01T00:00:10.000Z,
"state": Object {
"alertInstances": Object {},
"alertTypeState": undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { RunContext } from '../../../task_manager';
import { createExecutionHandler } from './create_execution_handler';
import { createAlertInstanceFactory } from './create_alert_instance_factory';
import { AlertInstance } from './alert_instance';
import { getNextRunAt } from './get_next_run_at';
import { validateAlertTypeParams } from './validate_alert_type_params';
import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server';
import { PluginStartContract as ActionsPluginStartContract } from '../../../actions';
Expand Down Expand Up @@ -94,7 +93,7 @@ export class TaskRunnerFactory {
const services = getServices(fakeRequest);
// Ensure API key is still valid and user has access
const {
attributes: { alertTypeParams, actions, interval, throttle, muteAll, mutedInstanceIds },
attributes: { alertTypeParams, actions, throttle, muteAll, mutedInstanceIds },
references,
} = await services.savedObjectsClient.get<RawAlert>('alert', alertId);

Expand Down Expand Up @@ -167,15 +166,12 @@ export class TaskRunnerFactory {
})
);

const nextRunAt = getNextRunAt(new Date(taskInstance.startedAt!), interval);

return {
state: {
alertTypeState,
alertInstances,
previousStartedAt: taskInstance.startedAt!,
},
runAt: nextRunAt,
};
},
};
Expand Down
5 changes: 4 additions & 1 deletion x-pack/legacy/plugins/alerting/server/shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export interface Server extends Legacy.Server {
/**
* Shim what we're thinking setup and start contracts will look like
*/
export type TaskManagerStartContract = Pick<TaskManager, 'schedule' | 'fetch' | 'remove'>;
export type TaskManagerStartContract = Pick<
TaskManager,
'schedule' | 'reschedule' | 'fetch' | 'remove'
>;
export type SecurityPluginSetupContract = Pick<SecurityPlugin, '__legacyCompat'>;
export type SecurityPluginStartContract = Pick<SecurityPlugin, 'authc'>;
export type XPackMainPluginSetupContract = Pick<XPackMainPlugin, 'registerFeature'>;
Expand Down
13 changes: 13 additions & 0 deletions x-pack/legacy/plugins/task_manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,19 @@ The danger is that in such a situation, a Task with that same `id` might already

To achieve this you should use the `ensureScheduling` api which has the exact same behavior as `schedule`, except it allows the scheduling of a Task with an `id` that's already in assigned to another Task and it will assume that the existing Task is the one you wished to `schedule`, treating this as a successful operation.

### reschedule
The `reschedule` api allows a developer to reconfigure an existing Task's `runAt` and `interval` fields.
Given the `id` of an existing Task, you may specify either, or both, of these fields and the Task's fields will be updated accordingly, returning the updated Task.

One thing to note is that this api behaves mildly differently depending on the state of the Task.

If a request is made to `reschedule` the `interval` field of an `idle` task, the task's field will be updated and the task will run immediately and the new `interval` will be applied to scheduling the _next_ run.
If a request is made to `reschedule` the `runAt` field of an `idle` task, irrespective of whether the `interval` field is also specified, the task's field(s) will be updated and the task will only run when the newly specified `runAt` expires (after which, any new `interval` that may also have been specified, will be applied to the _next_ scheduled run).
These behaviors mirrors how `schedule` treats these two fields.

Where this behavior diverges is if a request is made to `reschedule` a non `idle` task, such as a `running` task or a `failed` task.
In such a case, `reschedule` will avoid changing the `runAt` field, as it is used internally to manage Task Manager lifecycles and changing that field in such a case could be considered dangerous. `interval` on the other hand, will be updated in such a case, and we recommend using the Task returned by the `reschedule` api to assess whether the fields have been updated as expected.

### more options

More custom access to the tasks can be done directly via Elasticsearch, though that won't be officially supported, as we can change the document structure at any time.
Expand Down
Loading