* added ability to run ephemeral tasks * fixed typing * added typing on plugin * WIP * Fix type issues * Hook up the ephemeral task into the task runner for actions * Tasks can now run independently of one another * Use deferred language * Refactor taskParams slightly * Use Promise.all * Remove deferred logic * Add config options to limit the amount of tasks executing at once * Add ephemeral task monitoring * WIP * Add single test so far * Ensure we log after actions have executed * Remove confusing * 1 * Add logic to ensure we fallback to default enqueueing if the total actions is above the config * Add additional test * Fix tests a bit, ensure we log the alerting:actions-execute right away and the tests should listen for alerts:execute * Better tests * If the queue is at capacity, attempt to execute the ephemeral task as a regular action * Ensure we run ephemeral tasks before to avoid them getting stuck in the queue * Do not handle the promise anymore * Remove unnecessary code * Properly handle errors from ephemeral task lifecycle * moved acitons domain out of alerting and into actions plugin * Remove some tests * Fix TS and test issues * Fix type issues * Fix more type issues * Fix more type issues * Fix jest tests * Fix more jest tests * Off by default * Fix jest tests * Update config for this suite too * Start of telemetry code * Fix types and add missing files * Fix telemetry schema * Fix types * Fix more types * moved load event emission to pollingcycle and added health stats on Ephemeral tasks * Add more telemetry data based on new health metrics for the ephemeral queue * Fix tests and types * Add separate request capacity for ephemeral queue * Fix telemetry schema and add tests for usage collection * track polled tasks by persistence and use in capacity estimation instead of executions * fixed typing * Bump default capacity * added delay metric to ephemeral stats * Fix bad merge * Fix tests * Fix tests * Fix types * Skip failing tests * Exclude ephemeral stats from capacity estimation tests * PR feedback * More PR feedback * PR feedback * Fix merge conflict * Try fixing CI * Fix broken lock file from merge * Match master * Add this back * PR feedback * Change to queue and add test * Disable ephemeral queue in tests * Updated desc * Comment out ephemeral-specific tests tha require the entire test suite to support ephemeral tasks * Add clarifying comment Co-authored-by: Gidi Meir Morris <github@gidi.io> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
160 lines
5.1 KiB
TypeScript
160 lines
5.1 KiB
TypeScript
/*
|
|
* 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 { SavedObjectsClientContract } from '../../../../src/core/server';
|
|
import { RunNowResult, TaskManagerStartContract } from '../../task_manager/server';
|
|
import {
|
|
RawAction,
|
|
ActionTypeRegistryContract,
|
|
PreConfiguredAction,
|
|
ActionTaskExecutorParams,
|
|
} from './types';
|
|
import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './constants/saved_objects';
|
|
import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor';
|
|
import { isSavedObjectExecutionSource } from './lib';
|
|
import { RelatedSavedObjects } from './lib/related_saved_objects';
|
|
|
|
interface CreateExecuteFunctionOptions {
|
|
taskManager: TaskManagerStartContract;
|
|
isESOCanEncrypt: boolean;
|
|
actionTypeRegistry: ActionTypeRegistryContract;
|
|
preconfiguredActions: PreConfiguredAction[];
|
|
}
|
|
|
|
export interface ExecuteOptions extends Pick<ActionExecutorOptions, 'params' | 'source'> {
|
|
id: string;
|
|
spaceId: string;
|
|
apiKey: string | null;
|
|
relatedSavedObjects?: RelatedSavedObjects;
|
|
}
|
|
|
|
export type ExecutionEnqueuer<T> = (
|
|
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
|
options: ExecuteOptions
|
|
) => Promise<T>;
|
|
|
|
export function createExecutionEnqueuerFunction({
|
|
taskManager,
|
|
actionTypeRegistry,
|
|
isESOCanEncrypt,
|
|
preconfiguredActions,
|
|
}: CreateExecuteFunctionOptions): ExecutionEnqueuer<void> {
|
|
return async function execute(
|
|
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
|
{ id, params, spaceId, source, apiKey, relatedSavedObjects }: ExecuteOptions
|
|
) {
|
|
if (!isESOCanEncrypt) {
|
|
throw new Error(
|
|
`Unable to execute action because the Encrypted Saved Objects plugin is missing encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
|
);
|
|
}
|
|
|
|
const action = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id);
|
|
validateCanActionBeUsed(action);
|
|
|
|
const { actionTypeId } = action;
|
|
if (!actionTypeRegistry.isActionExecutable(id, actionTypeId, { notifyUsage: true })) {
|
|
actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);
|
|
}
|
|
|
|
const actionTaskParamsRecord = await unsecuredSavedObjectsClient.create(
|
|
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
|
|
{
|
|
actionId: id,
|
|
params,
|
|
apiKey,
|
|
relatedSavedObjects,
|
|
},
|
|
executionSourceAsSavedObjectReferences(source)
|
|
);
|
|
|
|
await taskManager.schedule({
|
|
taskType: `actions:${action.actionTypeId}`,
|
|
params: {
|
|
spaceId,
|
|
actionTaskParamsId: actionTaskParamsRecord.id,
|
|
},
|
|
state: {},
|
|
scope: ['actions'],
|
|
});
|
|
};
|
|
}
|
|
|
|
export function createEphemeralExecutionEnqueuerFunction({
|
|
taskManager,
|
|
actionTypeRegistry,
|
|
preconfiguredActions,
|
|
}: CreateExecuteFunctionOptions): ExecutionEnqueuer<RunNowResult> {
|
|
return async function execute(
|
|
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
|
{ id, params, spaceId, source, apiKey }: ExecuteOptions
|
|
): Promise<RunNowResult> {
|
|
const action = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id);
|
|
validateCanActionBeUsed(action);
|
|
|
|
const { actionTypeId } = action;
|
|
if (!actionTypeRegistry.isActionExecutable(id, actionTypeId, { notifyUsage: true })) {
|
|
actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);
|
|
}
|
|
|
|
const taskParams: ActionTaskExecutorParams = {
|
|
spaceId,
|
|
taskParams: {
|
|
actionId: id,
|
|
// 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 } : {}),
|
|
},
|
|
...executionSourceAsSavedObjectReferences(source),
|
|
};
|
|
|
|
return taskManager.ephemeralRunNow({
|
|
taskType: `actions:${action.actionTypeId}`,
|
|
params: taskParams,
|
|
state: {},
|
|
scope: ['actions'],
|
|
});
|
|
};
|
|
}
|
|
|
|
function validateCanActionBeUsed(action: PreConfiguredAction | RawAction) {
|
|
const { name, isMissingSecrets } = action;
|
|
if (isMissingSecrets) {
|
|
throw new Error(
|
|
`Unable to execute action because no secrets are defined for the "${name}" connector.`
|
|
);
|
|
}
|
|
}
|
|
|
|
function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorOptions['source']) {
|
|
return isSavedObjectExecutionSource(executionSource)
|
|
? {
|
|
references: [
|
|
{
|
|
name: 'source',
|
|
...executionSource.source,
|
|
},
|
|
],
|
|
}
|
|
: {};
|
|
}
|
|
|
|
async function getAction(
|
|
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
|
preconfiguredActions: PreConfiguredAction[],
|
|
actionId: string
|
|
): Promise<PreConfiguredAction | RawAction> {
|
|
const pcAction = preconfiguredActions.find((action) => action.id === actionId);
|
|
if (pcAction) {
|
|
return pcAction;
|
|
}
|
|
|
|
const { attributes } = await unsecuredSavedObjectsClient.get<RawAction>('action', actionId);
|
|
return attributes;
|
|
}
|