[Response Ops] Remove ephemeral tasks from action and alerting plugins (#197421)

## Summary
Issue: https://github.com/elastic/kibana/issues/151461

Removes all reference to ephemeral tasks in the alerting and actions
plugin. As well as unit and E2E tests while maintaining backwards
compatibility for `xpack.alerting.maxEphemeralActionsPerAlert` flag.


### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Jiawei Wu 2024-12-02 14:06:01 -08:00 committed by GitHub
parent 40905c14ad
commit 7e71e5a1eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 912 additions and 1407 deletions

View file

@ -577,12 +577,6 @@ For a <<servicenow-action-type,{sn-itsm}>>, <<servicenow-sir-action-type,{sn-sir
[[alert-settings]]
=== Alerting settings
`xpack.alerting.maxEphemeralActionsPerAlert` {ess-icon}::
deprecated:[8.8.0]
Sets the number of actions that will run ephemerally. To use this, enable
ephemeral tasks in task manager first with
<<task-manager-settings,`xpack.task_manager.ephemeral_tasks.enabled`>>
`xpack.alerting.cancelAlertsOnRuleTimeout` {ess-icon}::
Specifies whether to skip writing alerts and scheduling actions if rule
processing was cancelled due to a timeout. Default: `true`. This setting can be

View file

@ -22,7 +22,6 @@ const createActionsClientMock = () => {
getBulk: jest.fn(),
getOAuthAccessToken: jest.fn(),
execute: jest.fn(),
ephemeralEnqueuedExecution: jest.fn(),
bulkEnqueueExecution: jest.fn(),
listTypes: jest.fn(),
isActionTypeEnabled: jest.fn(),

View file

@ -79,7 +79,6 @@ const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
const actionExecutor = actionExecutorMock.create();
const authorization = actionsAuthorizationMock.create();
const ephemeralExecutionEnqueuer = jest.fn();
const bulkExecutionEnqueuer = jest.fn();
const request = httpServerMock.createKibanaRequest();
const auditLogger = auditLoggerMock.create();
@ -138,7 +137,6 @@ beforeEach(() => {
kibanaIndices,
inMemoryConnectors: [],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -613,7 +611,6 @@ describe('create()', () => {
kibanaIndices,
inMemoryConnectors: [],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -739,7 +736,6 @@ describe('create()', () => {
],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -801,7 +797,6 @@ describe('create()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -862,7 +857,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -899,7 +893,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -956,7 +949,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -999,7 +991,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1121,7 +1112,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1165,7 +1155,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1199,7 +1188,6 @@ describe('get()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1270,7 +1258,6 @@ describe('getBulk()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1408,7 +1395,6 @@ describe('getBulk()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1503,7 +1489,6 @@ describe('getBulk()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1577,7 +1562,6 @@ describe('getBulk()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -1662,7 +1646,6 @@ describe('getOAuthAccessToken()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2097,7 +2080,6 @@ describe('delete()', () => {
},
],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2133,7 +2115,6 @@ describe('delete()', () => {
},
],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2653,7 +2634,6 @@ describe('update()', () => {
},
],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2696,7 +2676,6 @@ describe('update()', () => {
},
],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2782,7 +2761,6 @@ describe('execute()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2845,7 +2823,6 @@ describe('execute()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -2907,7 +2884,6 @@ describe('execute()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -3180,7 +3156,6 @@ describe('isPreconfigured()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -3230,7 +3205,6 @@ describe('isPreconfigured()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -3282,7 +3256,6 @@ describe('isSystemAction()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -3332,7 +3305,6 @@ describe('isSystemAction()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,

View file

@ -18,7 +18,6 @@ import {
Logger,
} from '@kbn/core/server';
import { AuditLogger } from '@kbn/security-plugin/server';
import { RunNowResult } from '@kbn/task-manager-plugin/server';
import { IEventLogClient } from '@kbn/event-log-plugin/server';
import { KueryNode } from '@kbn/es-query';
import { Connector, ConnectorWithExtraFindData } from '../application/connector/types';
@ -46,7 +45,6 @@ import {
} from '../types';
import { PreconfiguredActionDisabledModificationError } from '../lib/errors/preconfigured_action_disabled_modification';
import {
ExecutionEnqueuer,
ExecuteOptions as EnqueueExecutionOptions,
BulkExecutionEnqueuer,
ExecutionResponse,
@ -92,7 +90,6 @@ export interface ConstructorOptions {
unsecuredSavedObjectsClient: SavedObjectsClientContract;
inMemoryConnectors: InMemoryConnector[];
actionExecutor: ActionExecutorContract;
ephemeralExecutionEnqueuer: ExecutionEnqueuer<RunNowResult>;
bulkExecutionEnqueuer: BulkExecutionEnqueuer<ExecutionResponse>;
request: KibanaRequest;
authorization: ActionsAuthorization;
@ -112,7 +109,6 @@ export interface ActionsClientContext {
actionExecutor: ActionExecutorContract;
request: KibanaRequest;
authorization: ActionsAuthorization;
ephemeralExecutionEnqueuer: ExecutionEnqueuer<RunNowResult>;
bulkExecutionEnqueuer: BulkExecutionEnqueuer<ExecutionResponse>;
auditLogger?: AuditLogger;
usageCounter?: UsageCounter;
@ -131,7 +127,6 @@ export class ActionsClient {
unsecuredSavedObjectsClient,
inMemoryConnectors,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization,
@ -148,7 +143,6 @@ export class ActionsClient {
kibanaIndices,
inMemoryConnectors,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization,
@ -504,18 +498,6 @@ export class ActionsClient {
return this.context.bulkExecutionEnqueuer(this.context.unsecuredSavedObjectsClient, options);
}
public async ephemeralEnqueuedExecution(options: EnqueueExecutionOptions): Promise<RunNowResult> {
await this.context.authorization.ensureAuthorized({
operation: 'execute',
actionTypeId: options.actionTypeId,
});
return this.context.ephemeralExecutionEnqueuer(
this.context.unsecuredSavedObjectsClient,
options
);
}
public async listTypes({
featureId,
includeSystemActionTypes = false,

View file

@ -38,7 +38,6 @@ const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
const actionExecutor = actionExecutorMock.create();
const authorization = actionsAuthorizationMock.create();
const ephemeralExecutionEnqueuer = jest.fn();
const bulkExecutionEnqueuer = jest.fn();
const request = httpServerMock.createKibanaRequest();
const auditLogger = auditLoggerMock.create();
@ -131,7 +130,6 @@ beforeEach(() => {
kibanaIndices,
inMemoryConnectors: [],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,

View file

@ -52,7 +52,6 @@ const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
const actionExecutor = actionExecutorMock.create();
const authorization = actionsAuthorizationMock.create();
const ephemeralExecutionEnqueuer = jest.fn();
const bulkExecutionEnqueuer = jest.fn();
const request = httpServerMock.createKibanaRequest();
const auditLogger = auditLoggerMock.create();
@ -78,7 +77,6 @@ describe('getAll()', () => {
kibanaIndices,
inMemoryConnectors: [],
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -134,7 +132,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -279,7 +276,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -382,7 +378,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -487,7 +482,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -579,7 +573,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -613,7 +606,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
@ -690,7 +682,6 @@ describe('getAll()', () => {
scopedClusterClient,
kibanaIndices,
actionExecutor,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,

View file

@ -60,7 +60,6 @@ describe('listTypes()', () => {
unsecuredSavedObjectsClient: savedObjectsClientMock.create(),
inMemoryConnectors: [],
actionExecutor: actionExecutorMock.create(),
ephemeralExecutionEnqueuer: jest.fn(),
bulkExecutionEnqueuer: jest.fn(),
request: httpServerMock.createKibanaRequest(),
authorization: actionsAuthorizationMock.create() as unknown as ActionsAuthorization,

View file

@ -6,13 +6,8 @@
*/
import { SavedObjectsBulkResponse, SavedObjectsClientContract, Logger } from '@kbn/core/server';
import { RunNowResult, TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
import {
RawAction,
ActionTypeRegistryContract,
InMemoryConnector,
ActionTaskExecutorParams,
} from './types';
import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
import { RawAction, ActionTypeRegistryContract, InMemoryConnector } from './types';
import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './constants/saved_objects';
import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor';
import { extractSavedObjectReferences, isSavedObjectExecutionSource } from './lib';
@ -215,43 +210,6 @@ export function createBulkExecutionEnqueuerFunction({
};
}
export function createEphemeralExecutionEnqueuerFunction({
taskManager,
actionTypeRegistry,
inMemoryConnectors,
logger,
}: CreateExecuteFunctionOptions): ExecutionEnqueuer<RunNowResult> {
return async function execute(
unsecuredSavedObjectsClient: SavedObjectsClientContract,
{ id, params, spaceId, source, consumer, apiKey, executionId }: ExecuteOptions
): Promise<RunNowResult> {
const { connector } = await getConnector(unsecuredSavedObjectsClient, inMemoryConnectors, id);
validateConnector({ id, connector, actionTypeRegistry });
const taskParams: ActionTaskExecutorParams = {
spaceId,
taskParams: {
actionId: id,
consumer,
// Saved Objects won't allow us to enforce unknown rather than any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: params as Record<string, any>,
...(apiKey ? { apiKey } : {}),
...(executionId ? { executionId } : {}),
},
...executionSourceAsSavedObjectReferences(source),
};
return taskManager.ephemeralRunNow({
taskType: `actions:${connector.actionTypeId}`,
params: taskParams,
state: {},
scope: ['actions'],
});
};
}
function validateConnector({
id,
connector,
@ -287,21 +245,6 @@ function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorO
: {};
}
async function getConnector(
unsecuredSavedObjectsClient: SavedObjectsClientContract,
inMemoryConnectors: InMemoryConnector[],
actionId: string
): Promise<{ connector: InMemoryConnector | RawAction; isInMemory: boolean }> {
const inMemoryAction = inMemoryConnectors.find((action) => action.id === actionId);
if (inMemoryAction) {
return { connector: inMemoryAction, isInMemory: true };
}
const { attributes } = await unsecuredSavedObjectsClient.get<RawAction>('action', actionId);
return { connector: attributes, isInMemory: false };
}
async function getConnectors(
unsecuredSavedObjectsClient: SavedObjectsClientContract,
inMemoryConnectors: InMemoryConnector[],

View file

@ -82,7 +82,6 @@ export interface ExecuteOptions<Source = unknown> {
actionId: string;
consumer?: string;
executionId?: string;
isEphemeral?: boolean;
params: Record<string, unknown>;
relatedSavedObjects?: RelatedSavedObjects;
request: KibanaRequest;
@ -133,7 +132,6 @@ export class ActionExecutor {
actionId,
consumer,
executionId,
isEphemeral,
request,
params,
relatedSavedObjects,
@ -175,7 +173,6 @@ export class ActionExecutor {
},
executeLabel: `execute_action`,
executionId,
isEphemeral,
namespace,
params,
relatedSavedObjects,
@ -367,7 +364,6 @@ export class ActionExecutor {
checkCanExecuteFn,
executeLabel,
executionId,
isEphemeral,
namespace,
params,
relatedSavedObjects,
@ -512,7 +508,6 @@ export class ActionExecutor {
params: validatedParams,
config: validatedConfig,
secrets: validatedSecrets,
isEphemeral,
taskInfo,
configurationUtilities,
logger,

View file

@ -35,7 +35,6 @@ import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
const executeParamsFields = [
'actionId',
'isEphemeral',
'params',
'relatedSavedObjects',
'executionId',
@ -173,7 +172,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
relatedSavedObjects: [],
executionId: '123abc',
@ -233,7 +231,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '9',
isEphemeral: false,
params: { baz: true },
executionId: '123abc',
relatedSavedObjects: [],
@ -289,7 +286,6 @@ describe('Task Runner Factory', () => {
expect(pick(executeParams, [...executeParamsFields, 'consumer'])).toEqual({
actionId: '2',
consumer: 'test-consumer',
isEphemeral: false,
params: { baz: true },
relatedSavedObjects: [],
executionId: '123abc',
@ -346,7 +342,6 @@ describe('Task Runner Factory', () => {
expect(pick(executeParams, [...executeParamsFields, 'consumer'])).toEqual({
actionId: '2',
consumer: 'test-consumer',
isEphemeral: false,
params: { baz: true },
relatedSavedObjects: [],
executionId: '123abc',
@ -407,7 +402,6 @@ describe('Task Runner Factory', () => {
expect(pick(executeParams, [...executeParamsFields, 'consumer'])).toEqual({
actionId: '2',
consumer: 'test-consumer',
isEphemeral: false,
params: { baz: true },
relatedSavedObjects: [],
executionId: '123abc',
@ -569,7 +563,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
executionId: '123abc',
relatedSavedObjects: [],
@ -627,7 +620,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
executionId: '123abc',
relatedSavedObjects: [
@ -680,7 +672,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
executionId: '123abc',
relatedSavedObjects: [
@ -739,7 +730,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
request: {
headers: {
@ -785,7 +775,6 @@ describe('Task Runner Factory', () => {
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
expect(pick(executeParams, executeParamsFields)).toEqual({
actionId: '2',
isEphemeral: false,
params: { baz: true },
executionId: '123abc',
relatedSavedObjects: [],

View file

@ -32,7 +32,6 @@ import {
ActionTaskParams,
ActionTypeExecutorResult,
ActionTypeRegistryContract,
isPersistedActionTask,
SpaceIdToNamespaceFunction,
} from '../types';
import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../constants/saved_objects';
@ -128,7 +127,6 @@ export class TaskRunnerFactory {
executorResult = await actionExecutor.execute({
params,
actionId: actionId as string,
isEphemeral: !isPersistedActionTask(actionTaskExecutorParams),
request,
taskInfo,
executionId,
@ -209,19 +207,17 @@ export class TaskRunnerFactory {
},
cleanup: async () => {
// Cleanup action_task_params object now that we're done with it
if (isPersistedActionTask(actionTaskExecutorParams)) {
try {
await savedObjectsRepository.delete(
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
actionTaskExecutorParams.actionTaskParamsId,
{ refresh: false, namespace: spaceIdToNamespace(actionTaskExecutorParams.spaceId) }
);
} catch (e) {
// Log error only, we shouldn't fail the task because of an error here (if ever there's retry logic)
logger.error(
`Failed to cleanup ${ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE} object [id="${actionTaskExecutorParams.actionTaskParamsId}"]: ${e.message}`
);
}
try {
await savedObjectsRepository.delete(
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
actionTaskExecutorParams.actionTaskParamsId,
{ refresh: false, namespace: spaceIdToNamespace(actionTaskExecutorParams.spaceId) }
);
} catch (e) {
// Log error only, we shouldn't fail the task because of an error here (if ever there's retry logic)
logger.error(
`Failed to cleanup ${ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE} object [id="${actionTaskExecutorParams.actionTaskParamsId}"]: ${e.message}`
);
}
},
};
@ -252,45 +248,41 @@ async function getActionTaskParams(
): Promise<TaskParams> {
const { spaceId } = executorParams;
const namespace = spaceIdToNamespace(spaceId);
if (isPersistedActionTask(executorParams)) {
try {
const actionTask =
await encryptedSavedObjectsClient.getDecryptedAsInternalUser<ActionTaskParams>(
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
executorParams.actionTaskParamsId,
{ namespace }
);
const {
attributes: { relatedSavedObjects },
references,
} = actionTask;
const { actionId, relatedSavedObjects: injectedRelatedSavedObjects } =
injectSavedObjectReferences(references, relatedSavedObjects as RelatedSavedObjects);
return {
...actionTask,
attributes: {
...actionTask.attributes,
...(actionId ? { actionId } : {}),
...(relatedSavedObjects ? { relatedSavedObjects: injectedRelatedSavedObjects } : {}),
},
};
} catch (e) {
const errorSource = SavedObjectsErrorHelpers.isNotFoundError(e)
? TaskErrorSource.USER
: TaskErrorSource.FRAMEWORK;
logger.error(
`Failed to load action task params ${executorParams.actionTaskParamsId}: ${e.message}`,
{ tags: ['connector-run-failed', `${errorSource}-error`] }
try {
const actionTask =
await encryptedSavedObjectsClient.getDecryptedAsInternalUser<ActionTaskParams>(
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
executorParams.actionTaskParamsId,
{ namespace }
);
if (SavedObjectsErrorHelpers.isNotFoundError(e)) {
throw createRetryableError(createTaskRunError(e, errorSource), true);
}
const {
attributes: { relatedSavedObjects },
references,
} = actionTask;
const { actionId, relatedSavedObjects: injectedRelatedSavedObjects } =
injectSavedObjectReferences(references, relatedSavedObjects as RelatedSavedObjects);
return {
...actionTask,
attributes: {
...actionTask.attributes,
...(actionId ? { actionId } : {}),
...(relatedSavedObjects ? { relatedSavedObjects: injectedRelatedSavedObjects } : {}),
},
};
} catch (e) {
const errorSource = SavedObjectsErrorHelpers.isNotFoundError(e)
? TaskErrorSource.USER
: TaskErrorSource.FRAMEWORK;
logger.error(
`Failed to load action task params ${executorParams.actionTaskParamsId}: ${e.message}`,
{ tags: ['connector-run-failed', `${errorSource}-error`] }
);
if (SavedObjectsErrorHelpers.isNotFoundError(e)) {
throw createRetryableError(createTaskRunError(e, errorSource), true);
}
} else {
return { attributes: executorParams.taskParams, references: executorParams.references ?? [] };
throw createRetryableError(createTaskRunError(e, errorSource), true);
}
}

View file

@ -48,10 +48,7 @@ import { resolveCustomHosts } from './lib/custom_host_settings';
import { events } from './lib/event_based_telemetry';
import { ActionsClient } from './actions_client/actions_client';
import { ActionTypeRegistry } from './action_type_registry';
import {
createEphemeralExecutionEnqueuerFunction,
createBulkExecutionEnqueuerFunction,
} from './create_execute_function';
import { createBulkExecutionEnqueuerFunction } from './create_execute_function';
import { registerActionsUsageCollector } from './usage';
import {
ActionExecutor,
@ -480,14 +477,6 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
request,
authorization: instantiateAuthorization(request),
actionExecutor: actionExecutor!,
ephemeralExecutionEnqueuer: createEphemeralExecutionEnqueuerFunction({
taskManager: plugins.taskManager,
actionTypeRegistry: actionTypeRegistry!,
isESOCanEncrypt: isESOCanEncrypt!,
inMemoryConnectors: this.inMemoryConnectors,
configurationUtilities: actionsConfigUtils,
logger,
}),
bulkExecutionEnqueuer: createBulkExecutionEnqueuerFunction({
taskManager: plugins.taskManager,
actionTypeRegistry: actionTypeRegistry!,
@ -753,14 +742,6 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
request,
authorization: instantiateAuthorization(request),
actionExecutor: actionExecutor!,
ephemeralExecutionEnqueuer: createEphemeralExecutionEnqueuerFunction({
taskManager,
actionTypeRegistry: actionTypeRegistry!,
isESOCanEncrypt: isESOCanEncrypt!,
inMemoryConnectors,
configurationUtilities: actionsConfigUtils,
logger,
}),
bulkExecutionEnqueuer: createBulkExecutionEnqueuerFunction({
taskManager,
actionTypeRegistry: actionTypeRegistry!,

View file

@ -13,7 +13,6 @@ import {
SavedObjectAttributes,
ElasticsearchClient,
CustomRequestHandlerContext,
SavedObjectReference,
Logger,
ISavedObjectsRepository,
IScopedClusterClient,
@ -88,7 +87,6 @@ export interface ActionTypeExecutorOptions<
secrets: Secrets;
params: Params;
logger: Logger;
isEphemeral?: boolean;
taskInfo?: TaskInfo;
configurationUtilities: ActionsConfigurationUtilities;
source?: ActionExecutionSource<unknown>;
@ -238,27 +236,11 @@ export interface ActionTaskParams extends SavedObjectAttributes {
source?: string;
}
interface PersistedActionTaskExecutorParams {
export interface ActionTaskExecutorParams {
spaceId: string;
actionTaskParamsId: string;
}
interface EphemeralActionTaskExecutorParams {
spaceId: string;
taskParams: ActionTaskParams;
references?: SavedObjectReference[];
}
export type ActionTaskExecutorParams =
| PersistedActionTaskExecutorParams
| EphemeralActionTaskExecutorParams;
export function isPersistedActionTask(
actionTask: ActionTaskExecutorParams
): actionTask is PersistedActionTaskExecutorParams {
return typeof (actionTask as PersistedActionTaskExecutorParams).actionTaskParamsId === 'string';
}
export interface ProxySettings {
proxyUrl: string;
proxyBypassHosts: Set<string> | undefined;

View file

@ -21,7 +21,6 @@ describe('config validation', () => {
"interval": "5m",
"removalDelay": "1h",
},
"maxEphemeralActionsPerAlert": 10,
"rules": Object {
"maxScheduledPerMinute": 32000,
"minimumScheduleInterval": Object {

View file

@ -60,7 +60,6 @@ const rulesSchema = schema.object({
}),
});
export const DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT = 10;
export const configSchema = schema.object({
healthCheck: schema.object({
interval: schema.string({ validate: validateDurationSchema, defaultValue: '60m' }),
@ -69,9 +68,7 @@ export const configSchema = schema.object({
interval: schema.string({ validate: validateDurationSchema, defaultValue: '5m' }),
removalDelay: schema.string({ validate: validateDurationSchema, defaultValue: '1h' }),
}),
maxEphemeralActionsPerAlert: schema.number({
defaultValue: DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT,
}),
maxEphemeralActionsPerAlert: schema.maybe(schema.number()),
enableFrameworkAlerts: schema.boolean({ defaultValue: true }),
cancelAlertsOnRuleTimeout: schema.boolean({ defaultValue: true }),
rules: rulesSchema,

View file

@ -36,7 +36,6 @@ export type {
export { DEFAULT_AAD_CONFIG } from './types';
export { RULE_SAVED_OBJECT_TYPE, API_KEY_PENDING_INVALIDATION_TYPE } from './saved_objects';
export { RuleNotifyWhen } from '../common';
export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config';
export type { PluginSetupContract, PluginStartContract } from './plugin';
export type { FindResult, BulkEditOperation, BulkOperationError } from './rules_client';
export type { Rule } from './application/rule/types';
@ -84,9 +83,9 @@ export const config: PluginConfigDescriptor<AlertingConfig> = {
rules: { run: { alerts: { max: true } } },
},
deprecations: ({ renameFromRoot, deprecate }) => [
deprecate('maxEphemeralActionsPerAlert', 'a future version', {
deprecate('maxEphemeralActionsPerAlert', '9.0.0', {
level: 'warning',
message: `Configuring "xpack.alerting.maxEphemeralActionsPerAlert" is deprecated and will be removed in a future version. Remove this setting to increase action execution resiliency.`,
message: `The setting "xpack.alerting.maxEphemeralActionsPerAlert" is deprecated and currently ignored by the system. Please remove this setting.`,
}),
],
};

View file

@ -615,7 +615,6 @@ export class AlertingPlugin {
logger,
}),
maxAlerts: this.config.rules.run.alerts.max,
maxEphemeralActionsPerRule: this.config.maxEphemeralActionsPerAlert,
ruleTypeRegistry: this.ruleTypeRegistry!,
rulesSettingsService: new RulesSettingsService({
cacheInterval: this.config.rulesSettings.cacheInterval,
@ -626,7 +625,6 @@ export class AlertingPlugin {
savedObjects: core.savedObjects,
share: plugins.share,
spaceIdToNamespace,
supportsEphemeralTasks: plugins.taskManager.supportsEphemeralTasks(),
uiSettings: core.uiSettings,
usageCounter: this.usageCounter,
isServerless: this.isServerless,

View file

@ -2617,7 +2617,6 @@ describe('Action Scheduler', () => {
});
expect(buildActionParams).not.toHaveBeenCalledWith();
expect(actionsClient.ephemeralEnqueuedExecution).not.toHaveBeenCalled();
expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled();
expect(alertingEventLogger.logAction).not.toHaveBeenCalled();
expect(executorParams.logger.warn).toHaveBeenCalledWith(
@ -2662,7 +2661,6 @@ describe('Action Scheduler', () => {
expect(res).toEqual({ throttledSummaryActions: {} });
expect(buildActionParams).not.toHaveBeenCalled();
expect(alertsClient.getSummarizedAlerts).not.toHaveBeenCalled();
expect(actionsClient.ephemeralEnqueuedExecution).not.toHaveBeenCalled();
expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled();
expect(alertingEventLogger.logAction).not.toHaveBeenCalled();
});

View file

@ -5,13 +5,8 @@
* 2.0.
*/
import { createTaskRunError, TaskErrorSource } from '@kbn/task-manager-plugin/server';
import {
createTaskRunError,
isEphemeralTaskRejectedDueToCapacityError,
TaskErrorSource,
} from '@kbn/task-manager-plugin/server';
import {
ExecuteOptions as EnqueueExecutionOptions,
ExecutionResponseItem,
ExecutionResponseType,
} from '@kbn/actions-plugin/server/create_execute_function';
@ -51,8 +46,6 @@ export class ActionScheduler<
IActionScheduler<State, Context, ActionGroupIds, RecoveryActionGroupId>
> = [];
private ephemeralActionsToSchedule: number;
constructor(
private readonly context: ActionSchedulerOptions<
Params,
@ -65,7 +58,6 @@ export class ActionScheduler<
AlertData
>
) {
this.ephemeralActionsToSchedule = context.taskRunnerContext.maxEphemeralActionsPerRule;
for (const [_, scheduler] of Object.entries(schedulers)) {
this.schedulers.push(new scheduler(context));
}
@ -101,37 +93,28 @@ export class ActionScheduler<
return { throttledSummaryActions };
}
const bulkScheduleRequest: EnqueueExecutionOptions[] = [];
for (const result of allActionsToScheduleResult) {
await this.runActionAsEphemeralOrAddToBulkScheduleRequest({
enqueueOptions: result.actionToEnqueue,
bulkScheduleRequest,
});
}
let bulkScheduleResponse: ExecutionResponseItem[] = [];
if (!!bulkScheduleRequest.length) {
for (const c of chunk(bulkScheduleRequest, BULK_SCHEDULE_CHUNK_SIZE)) {
let enqueueResponse;
try {
enqueueResponse = await withAlertingSpan('alerting:bulk-enqueue-actions', () =>
this.context.actionsClient!.bulkEnqueueExecution(c)
);
} catch (e) {
if (e.statusCode === 404) {
throw createTaskRunError(e, TaskErrorSource.USER);
}
throw createTaskRunError(e, TaskErrorSource.FRAMEWORK);
}
if (enqueueResponse.errors) {
bulkScheduleResponse = bulkScheduleResponse.concat(
enqueueResponse.items.filter(
(i) => i.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR
)
);
for (const c of chunk(allActionsToScheduleResult, BULK_SCHEDULE_CHUNK_SIZE)) {
let enqueueResponse;
try {
enqueueResponse = await withAlertingSpan('alerting:bulk-enqueue-actions', () =>
this.context.actionsClient!.bulkEnqueueExecution(
c.map((actions) => actions.actionToEnqueue)
)
);
} catch (e) {
if (e.statusCode === 404) {
throw createTaskRunError(e, TaskErrorSource.USER);
}
throw createTaskRunError(e, TaskErrorSource.FRAMEWORK);
}
if (enqueueResponse.errors) {
bulkScheduleResponse = bulkScheduleResponse.concat(
enqueueResponse.items.filter(
(i) => i.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR
)
);
}
}
@ -175,28 +158,4 @@ export class ActionScheduler<
return { throttledSummaryActions };
}
private async runActionAsEphemeralOrAddToBulkScheduleRequest({
enqueueOptions,
bulkScheduleRequest,
}: {
enqueueOptions: EnqueueExecutionOptions;
bulkScheduleRequest: EnqueueExecutionOptions[];
}) {
if (
this.context.taskRunnerContext.supportsEphemeralTasks &&
this.ephemeralActionsToSchedule > 0
) {
this.ephemeralActionsToSchedule--;
try {
await this.context.actionsClient!.ephemeralEnqueuedExecution(enqueueOptions);
} catch (err) {
if (isEphemeralTaskRejectedDueToCapacityError(err)) {
bulkScheduleRequest.push(enqueueOptions);
}
}
} else {
bulkScheduleRequest.push(enqueueOptions);
}
}
}

View file

@ -168,13 +168,11 @@ const taskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType
kibanaBaseUrl: 'https://localhost:5601',
logger,
maxAlerts: 1000,
maxEphemeralActionsPerRule: 10,
ruleTypeRegistry,
rulesSettingsService,
savedObjects: savedObjectsService,
share: {} as SharePluginStart,
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
supportsEphemeralTasks: false,
uiSettings: uiSettingsService,
usageCounter: mockUsageCounter,
isServerless: false,

File diff suppressed because it is too large Load diff

View file

@ -216,13 +216,11 @@ describe('Task Runner', () => {
logger,
maintenanceWindowsService,
maxAlerts: 1000,
maxEphemeralActionsPerRule: 10,
ruleTypeRegistry,
rulesSettingsService,
savedObjects: savedObjectsService,
share: {} as SharePluginStart,
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
supportsEphemeralTasks: false,
uiSettings: uiSettingsService,
usageCounter: mockUsageCounter,
isServerless: false,

View file

@ -149,13 +149,11 @@ describe('Task Runner Cancel', () => {
logger,
maintenanceWindowsService,
maxAlerts: 1000,
maxEphemeralActionsPerRule: 10,
ruleTypeRegistry,
rulesSettingsService,
savedObjects: savedObjectsService,
share: {} as SharePluginStart,
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
supportsEphemeralTasks: false,
uiSettings: uiSettingsService,
usageCounter: mockUsageCounter,
isServerless: false,

View file

@ -120,13 +120,11 @@ describe('Task Runner Factory', () => {
logger: loggingSystemMock.create().get(),
maintenanceWindowsService,
maxAlerts: 1000,
maxEphemeralActionsPerRule: 10,
ruleTypeRegistry: ruleTypeRegistryMock.create(),
rulesSettingsService,
savedObjects: savedObjectsService,
share: {} as SharePluginStart,
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
supportsEphemeralTasks: true,
uiSettings: uiSettingsService,
usageCounter: mockUsageCounter,
isServerless: false,

View file

@ -172,13 +172,11 @@ export interface TaskRunnerContext {
logger: Logger;
maintenanceWindowsService: MaintenanceWindowsService;
maxAlerts: number;
maxEphemeralActionsPerRule: number;
ruleTypeRegistry: RuleTypeRegistry;
rulesSettingsService: RulesSettingsService;
savedObjects: SavedObjectsServiceStart;
share: SharePluginStart;
spaceIdToNamespace: SpaceIdToNamespaceFunction;
supportsEphemeralTasks: boolean;
uiSettings: UiSettingsServiceStart;
usageCounter?: UsageCounter;
isServerless: boolean;

View file

@ -57,7 +57,6 @@ export function generateAlertingConfig(): AlertingConfig {
interval: '5m',
removalDelay: '1h',
},
maxEphemeralActionsPerAlert: 10,
cancelAlertsOnRuleTimeout: true,
rules: {
maxScheduledPerMinute: 10000,

View file

@ -217,7 +217,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
...emailSettings,
...maxScheduledPerMinuteSettings,
'--xpack.eventLog.logEntries=true',
'--xpack.task_manager.ephemeral_tasks.enabled=false',
`--xpack.task_manager.unsafe.exclude_task_types=${JSON.stringify([
'actions:test.excluded',
])}`,

View file

@ -1,124 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { flatten } from 'lodash';
import { IValidatedEvent } from '@kbn/event-log-plugin/server';
import { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from '@kbn/alerting-plugin/server/config';
import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers';
import { Spaces } from '../../../scenarios';
import { getUrlPrefix, ObjectRemover, getTestRuleData, getEventLog } from '../../../../common/lib';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default function createNotifyWhenTests({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const retry = getService('retry');
const es = getService('es');
const esTestIndexTool = new ESTestIndexTool(es, retry);
describe('ephemeral', () => {
const objectRemover = new ObjectRemover(supertest);
beforeEach(async () => {
await esTestIndexTool.destroy();
await esTestIndexTool.setup();
});
afterEach(async () => await esTestIndexTool.destroy());
after(async () => {
await objectRemover.removeAll();
});
it('should execute all requests, when some will be ephemeral and some not', async () => {
const nonEphemeralTasks = 3;
const actionPromises = [];
for (let i = 0; i < DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT + nonEphemeralTasks; i++) {
actionPromises.push(
supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: `My action${i}`,
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200)
);
}
const createdActions = await Promise.all(actionPromises);
createdActions.forEach((createdAction) =>
objectRemover.add(Spaces.space1.id, createdAction.body.id, 'connector', 'actions')
);
const pattern = {
instance: [true, true, true, false, true, true],
};
const alertData = getTestRuleData({
rule_type_id: 'test.patternFiring',
params: { pattern },
schedule: { interval: '1m' },
throttle: null,
notify_when: 'onActiveAlert',
actions: createdActions.map((createdAction) => {
return {
id: createdAction.body.id,
group: 'default',
params: {
index: ES_TEST_INDEX_NAME,
reference: '',
message: 'test message',
},
};
}),
});
const { body: createdAlert } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`)
.set('kbn-xsrf', 'foo')
.send(alertData)
.expect(200);
objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting');
const events = flatten(
await Promise.all(
createdActions.map(async (createdAction) => {
return await retry.try(async () => {
return await getEventLog({
getService,
spaceId: Spaces.space1.id,
type: 'action',
id: createdAction.body.id,
provider: 'actions',
actions: new Map([['execute', { gte: 1 }]]),
});
});
})
)
);
const executeActionsEvents = getEventsByAction(events, 'execute');
expect(executeActionsEvents.length).equal(
nonEphemeralTasks + DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT
);
const searchResult = await esTestIndexTool.search('action:test.index-record');
// @ts-expect-error doesnt handle total: number
expect(searchResult.body.hits.total.value).equal(
nonEphemeralTasks + DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT
);
});
});
}
function getEventsByAction(events: IValidatedEvent[], action: string) {
return events.filter((event) => event?.event?.action === action);
}

View file

@ -18,7 +18,6 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC
loadTestFile(require.resolve('./builtin_alert_types'));
loadTestFile(require.resolve('./mustache_templates.ts'));
loadTestFile(require.resolve('./notify_when'));
loadTestFile(require.resolve('./ephemeral'));
loadTestFile(require.resolve('./event_log_alerts'));
loadTestFile(require.resolve('./snooze'));
loadTestFile(require.resolve('./unsnooze'));