"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskRunnerFactory = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _uuid = require("uuid"); var _lodash = require("lodash"); var _server = require("@kbn/spaces-plugin/server"); var _server2 = require("@kbn/core/server"); var _server3 = require("@kbn/task-manager-plugin/server"); var _types = require("../types"); var _saved_objects = require("../constants/saved_objects"); var _action_execution_source = require("./action_execution_source"); var _related_saved_objects = require("./related_saved_objects"); var _action_task_params_utils = require("./action_task_params_utils"); var _monitoring = require("../monitoring"); var _errors = require("./errors"); /* * 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. */ class TaskRunnerFactory { constructor(actionExecutor, inMemoryMetrics) { (0, _defineProperty2.default)(this, "isInitialized", false); (0, _defineProperty2.default)(this, "taskRunnerContext", void 0); (0, _defineProperty2.default)(this, "actionExecutor", void 0); (0, _defineProperty2.default)(this, "inMemoryMetrics", void 0); this.actionExecutor = actionExecutor; this.inMemoryMetrics = inMemoryMetrics; } initialize(taskRunnerContext) { if (this.isInitialized) { throw new Error('TaskRunnerFactory already initialized'); } this.isInitialized = true; this.taskRunnerContext = taskRunnerContext; } create({ taskInstance }) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); } const { actionExecutor, inMemoryMetrics } = this; const { logger, encryptedSavedObjectsClient, spaceIdToNamespace, basePathService, savedObjectsRepository } = this.taskRunnerContext; const taskInfo = { scheduled: taskInstance.runAt, attempts: taskInstance.attempts, numSkippedRuns: taskInstance.numSkippedRuns }; const actionExecutionId = (0, _uuid.v4)(); const actionTaskExecutorParams = taskInstance.params; let actionData; return { async loadIndirectParams() { try { const taskParams = await getActionTaskParams(actionTaskExecutorParams, encryptedSavedObjectsClient, spaceIdToNamespace); const { spaceId } = actionTaskExecutorParams; const request = getFakeRequest(taskParams.attributes.apiKey); const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; const actionInfo = await actionExecutor.getActionInfoInternal(taskParams.attributes.actionId, request, namespace.namespace); actionData = { data: { indirectParams: actionInfo.rawAction, taskParams, actionInfo } }; return actionData; } catch (error) { actionData = { error }; return { error }; } }, async run() { if (!actionData) { actionData = await this.loadIndirectParams(); } if (actionData.error) { return (0, _server3.throwRetryableError)(actionData.error, true); } const { spaceId } = actionTaskExecutorParams; const { taskParams, actionInfo } = actionData.data; const { attributes: { actionId, params, apiKey, executionId, consumer, source, relatedSavedObjects }, references } = taskParams; const path = (0, _server.addSpaceIdToPath)('/', spaceId); const request = getFakeRequest(apiKey); basePathService.set(request, path); let executorResult; try { executorResult = await actionExecutor.execute({ params, actionId: actionId, isEphemeral: !(0, _types.isPersistedActionTask)(actionTaskExecutorParams), request, taskInfo, actionInfo, executionId, consumer, relatedSavedObjects: (0, _related_saved_objects.validatedRelatedSavedObjects)(logger, relatedSavedObjects), actionExecutionId, ...getSource(references, source) }); } catch (e) { logger.error(`Action '${actionId}' failed: ${e.message}`); if (e instanceof _errors.ActionTypeDisabledError) { // We'll stop re-trying due to action being forbidden (0, _server3.throwUnrecoverableError)(e); } throw e; } inMemoryMetrics.increment(_monitoring.IN_MEMORY_METRICS.ACTION_EXECUTIONS); if (executorResult.status === 'error') { inMemoryMetrics.increment(_monitoring.IN_MEMORY_METRICS.ACTION_FAILURES); logger.error(`Action '${actionId}' failed: ${executorResult.message}`); // Task manager error handler only kicks in when an error thrown (at this time) // So what we have to do is throw when the return status is `error`. throw (0, _server3.throwRetryableError)(new Error(executorResult.message), executorResult.retry); } }, cancel: async () => { // Write event log entry const { spaceId } = actionTaskExecutorParams; const { attributes: { actionId, apiKey, executionId, consumer, source, relatedSavedObjects }, references } = await getActionTaskParams(actionTaskExecutorParams, encryptedSavedObjectsClient, spaceIdToNamespace); const request = getFakeRequest(apiKey); const path = (0, _server.addSpaceIdToPath)('/', spaceId); basePathService.set(request, path); await actionExecutor.logCancellation({ actionId, request, consumer, executionId, relatedSavedObjects: relatedSavedObjects || [], actionExecutionId, ...getSource(references, source) }); inMemoryMetrics.increment(_monitoring.IN_MEMORY_METRICS.ACTION_TIMEOUTS); logger.debug(`Cancelling action task for action with id ${actionId} - execution error due to timeout.`); return { state: {} }; }, cleanup: async () => { // Cleanup action_task_params object now that we're done with it if ((0, _types.isPersistedActionTask)(actionTaskExecutorParams)) { try { await savedObjectsRepository.delete(_saved_objects.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 ${_saved_objects.ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE} object [id="${actionTaskExecutorParams.actionTaskParamsId}"]: ${e.message}`); } } } }; } } exports.TaskRunnerFactory = TaskRunnerFactory; function getFakeRequest(apiKey) { const requestHeaders = {}; if (apiKey) { requestHeaders.authorization = `ApiKey ${apiKey}`; } const fakeRawRequest = { headers: requestHeaders, path: '/' }; // Since we're using API keys and accessing elasticsearch can only be done // via a request, we're faking one with the proper authorization headers. return _server2.CoreKibanaRequest.from(fakeRawRequest); } async function getActionTaskParams(executorParams, encryptedSavedObjectsClient, spaceIdToNamespace) { const { spaceId } = executorParams; const namespace = spaceIdToNamespace(spaceId); if ((0, _types.isPersistedActionTask)(executorParams)) { const actionTask = await encryptedSavedObjectsClient.getDecryptedAsInternalUser(_saved_objects.ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, executorParams.actionTaskParamsId, { namespace }); const { attributes: { relatedSavedObjects }, references } = actionTask; const { actionId, relatedSavedObjects: injectedRelatedSavedObjects } = (0, _action_task_params_utils.injectSavedObjectReferences)(references, relatedSavedObjects); return { ...actionTask, attributes: { ...actionTask.attributes, ...(actionId ? { actionId } : {}), ...(relatedSavedObjects ? { relatedSavedObjects: injectedRelatedSavedObjects } : {}) } }; } else { var _executorParams$refer; return { attributes: executorParams.taskParams, references: (_executorParams$refer = executorParams.references) !== null && _executorParams$refer !== void 0 ? _executorParams$refer : [] }; } } function getSource(references, sourceType) { const sourceInReferences = references.find(ref => ref.name === 'source'); if (sourceInReferences) { return { source: (0, _action_execution_source.asSavedObjectExecutionSource)((0, _lodash.pick)(sourceInReferences, 'id', 'type')) }; } return sourceType ? { source: (0, _action_execution_source.asEmptySource)(sourceType) } : {}; }