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

[ResponseOps][Alerting] Optimize the scheduling of rule actions so it can happen in bulk #137781

Merged
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f61423a
Adding bulk scheduler
doakalexi Aug 1, 2022
6bfa428
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 1, 2022
cca5a96
Updating bulk schedule
doakalexi Aug 2, 2022
e0a9591
Merge branch 'alerting/schedule-rule-actions-in-bulk' of github.com:d…
doakalexi Aug 2, 2022
01153f8
Fixing failing tests
doakalexi Aug 2, 2022
c060392
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 2, 2022
2492cd6
Fixing test
doakalexi Aug 2, 2022
09ea93a
Merge branch 'alerting/schedule-rule-actions-in-bulk' of github.com:d…
doakalexi Aug 2, 2022
e09a8c2
Adding bulk schedule tests
doakalexi Aug 2, 2022
7771d1c
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
doakalexi Aug 2, 2022
3ddd86b
Cleaning up enqueue function
doakalexi Aug 3, 2022
a5566d1
Merge branch 'alerting/schedule-rule-actions-in-bulk' of github.com:d…
doakalexi Aug 3, 2022
d40d000
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
doakalexi Aug 3, 2022
886d4e1
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 8, 2022
b15ed37
Using bulk getConnectors
doakalexi Aug 9, 2022
9871251
Removing empty line
doakalexi Aug 9, 2022
9cbd921
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 10, 2022
79702ff
Update x-pack/plugins/actions/server/create_execute_function.ts
doakalexi Aug 10, 2022
3b0be30
Update x-pack/plugins/actions/server/create_execute_function.ts
doakalexi Aug 10, 2022
d405d58
Cleaning up auth
doakalexi Aug 11, 2022
434482b
Merge branch 'alerting/schedule-rule-actions-in-bulk' of github.com:d…
doakalexi Aug 11, 2022
e4cf6d9
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 11, 2022
9be7f30
Fixing test failure
doakalexi Aug 11, 2022
943d96d
Merge branch 'alerting/schedule-rule-actions-in-bulk' of github.com:d…
doakalexi Aug 11, 2022
0ee4624
Updating bulk auth changes
doakalexi Aug 11, 2022
7cefa1a
Fixed track change
doakalexi Aug 11, 2022
4c3407a
Fixing test failures
doakalexi Aug 11, 2022
5547408
Addressing pr comments
doakalexi Aug 12, 2022
1d3a90c
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 12, 2022
f98680d
Merge branch 'main' into alerting/schedule-rule-actions-in-bulk
kibanamachine Aug 12, 2022
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
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/actions_client.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const createActionsClientMock = () => {
execute: jest.fn(),
enqueueExecution: jest.fn(),
ephemeralEnqueuedExecution: jest.fn(),
bulkEnqueueExecution: jest.fn(),
listTypes: jest.fn(),
isActionTypeEnabled: jest.fn(),
isPreconfigured: jest.fn(),
Expand Down
130 changes: 130 additions & 0 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ActionsAuthorization } from './authorization/actions_authorization';
import {
getAuthorizationModeBySource,
AuthorizationMode,
getBulkAuthorizationModeBySource,
} from './authorization/get_authorization_mode_by_source';
import { actionsAuthorizationMock } from './authorization/actions_authorization.mock';
import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption';
Expand Down Expand Up @@ -59,6 +60,9 @@ jest.mock('./authorization/get_authorization_mode_by_source', () => {
getAuthorizationModeBySource: jest.fn(() => {
return 1;
}),
getBulkAuthorizationModeBySource: jest.fn(() => {
return 1;
}),
AuthorizationMode: {
Legacy: 0,
RBAC: 1,
Expand All @@ -80,6 +84,7 @@ const actionExecutor = actionExecutorMock.create();
const authorization = actionsAuthorizationMock.create();
const executionEnqueuer = jest.fn();
const ephemeralExecutionEnqueuer = jest.fn();
const bulkExecutionEnqueuer = jest.fn();
const request = httpServerMock.createKibanaRequest();
const auditLogger = auditLoggerMock.create();
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
Expand Down Expand Up @@ -124,6 +129,7 @@ beforeEach(() => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
auditLogger,
Expand Down Expand Up @@ -550,6 +556,7 @@ describe('create()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
connectorTokenClient: connectorTokenClientMock.create(),
Expand Down Expand Up @@ -655,6 +662,7 @@ describe('get()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -714,6 +722,7 @@ describe('get()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -835,6 +844,7 @@ describe('get()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -909,6 +919,7 @@ describe('getAll()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -1050,6 +1061,7 @@ describe('getAll()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -1131,6 +1143,7 @@ describe('getBulk()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -1266,6 +1279,7 @@ describe('getBulk()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -1324,6 +1338,7 @@ describe('getOAuthAccessToken()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -2326,6 +2341,119 @@ describe('enqueueExecution()', () => {
});
});

describe('bulkEnqueueExecution()', () => {
describe('authorization', () => {
test('ensures user is authorised to excecute actions', async () => {
(getBulkAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
return { [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 };
});
await actionsClient.bulkEnqueueExecution([
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '123abc',
apiKey: null,
},
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '456def',
apiKey: null,
},
]);
expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute');
});

test('throws when user is not authorised to create the type of action', async () => {
(getBulkAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
return { [AuthorizationMode.RBAC]: 1, [AuthorizationMode.Legacy]: 0 };
});
authorization.ensureAuthorized.mockRejectedValue(
new Error(`Unauthorized to execute all actions`)
);

await expect(
actionsClient.bulkEnqueueExecution([
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '123abc',
apiKey: null,
},
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '456def',
apiKey: null,
},
])
).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`);

expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute');
});

test('tracks legacy RBAC', async () => {
(getBulkAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
return { [AuthorizationMode.RBAC]: 0, [AuthorizationMode.Legacy]: 2 };
});

await actionsClient.bulkEnqueueExecution([
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '123abc',
apiKey: null,
},
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '456def',
apiKey: null,
},
]);

expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith(
'bulkEnqueueExecution',
mockUsageCounter,
2
);
});
});

test('calls the bulkExecutionEnqueuer with the appropriate parameters', async () => {
(getBulkAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
return { [AuthorizationMode.RBAC]: 0, [AuthorizationMode.Legacy]: 0 };
});
const opts = [
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '123abc',
apiKey: null,
},
{
id: uuid.v4(),
params: {},
spaceId: 'default',
executionId: '456def',
apiKey: null,
},
];
await expect(actionsClient.bulkEnqueueExecution(opts)).resolves.toMatchInlineSnapshot(
`undefined`
);

expect(bulkExecutionEnqueuer).toHaveBeenCalledWith(unsecuredSavedObjectsClient, opts);
});
});

describe('isActionTypeEnabled()', () => {
const fooActionType: ActionType = {
id: 'foo',
Expand Down Expand Up @@ -2366,6 +2494,7 @@ describe('isPreconfigured()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down Expand Up @@ -2403,6 +2532,7 @@ describe('isPreconfigured()', () => {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
preconfiguredActions: [
Expand Down
24 changes: 24 additions & 0 deletions x-pack/plugins/actions/server/actions_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ import { ExecuteOptions } from './lib/action_executor';
import {
ExecutionEnqueuer,
ExecuteOptions as EnqueueExecutionOptions,
BulkExecutionEnqueuer,
} from './create_execute_function';
import { ActionsAuthorization } from './authorization/actions_authorization';
import {
getAuthorizationModeBySource,
getBulkAuthorizationModeBySource,
AuthorizationMode,
} from './authorization/get_authorization_mode_by_source';
import { connectorAuditEvent, ConnectorAuditAction } from './lib/audit_events';
Expand Down Expand Up @@ -94,6 +96,7 @@ interface ConstructorOptions {
actionExecutor: ActionExecutorContract;
executionEnqueuer: ExecutionEnqueuer<void>;
ephemeralExecutionEnqueuer: ExecutionEnqueuer<RunNowResult>;
bulkExecutionEnqueuer: BulkExecutionEnqueuer<void>;
request: KibanaRequest;
authorization: ActionsAuthorization;
auditLogger?: AuditLogger;
Expand All @@ -118,6 +121,7 @@ export class ActionsClient {
private readonly authorization: ActionsAuthorization;
private readonly executionEnqueuer: ExecutionEnqueuer<void>;
private readonly ephemeralExecutionEnqueuer: ExecutionEnqueuer<RunNowResult>;
private readonly bulkExecutionEnqueuer: BulkExecutionEnqueuer<void>;
private readonly auditLogger?: AuditLogger;
private readonly usageCounter?: UsageCounter;
private readonly connectorTokenClient: ConnectorTokenClientContract;
Expand All @@ -132,6 +136,7 @@ export class ActionsClient {
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization,
auditLogger,
Expand All @@ -147,6 +152,7 @@ export class ActionsClient {
this.actionExecutor = actionExecutor;
this.executionEnqueuer = executionEnqueuer;
this.ephemeralExecutionEnqueuer = ephemeralExecutionEnqueuer;
this.bulkExecutionEnqueuer = bulkExecutionEnqueuer;
this.request = request;
this.authorization = authorization;
this.auditLogger = auditLogger;
Expand Down Expand Up @@ -656,6 +662,24 @@ export class ActionsClient {
return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options);
}

public async bulkEnqueueExecution(options: EnqueueExecutionOptions[]): Promise<void> {
const authCounts = await getBulkAuthorizationModeBySource(
this.unsecuredSavedObjectsClient,
options
);
if (authCounts[AuthorizationMode.RBAC] > 0) {
await this.authorization.ensureAuthorized('execute');
}
if (authCounts[AuthorizationMode.Legacy] > 0) {
trackLegacyRBACExemption(
'bulkEnqueueExecution',
this.usageCounter,
authCounts[AuthorizationMode.Legacy]
);
}
return this.bulkExecutionEnqueuer(this.unsecuredSavedObjectsClient, options);
}

public async ephemeralEnqueuedExecution(options: EnqueueExecutionOptions): Promise<RunNowResult> {
const { source } = options;
if (
Expand Down
Loading