Skip to content

Commit

Permalink
[Response Ops][Action] Using error source from connector type executo…
Browse files Browse the repository at this point in the history
…rs if defined. (elastic#184015)

Resolves elastic#180419

## Summary

Updates action executor to use error source from errors thrown within
the connector type executor, defaulting to framework error if no error
source is specified.

## To Verify

Modify the server log connector to throw a user error:

```
--- a/x-pack/plugins/stack_connectors/server/connector_types/server_log/index.ts
+++ b/x-pack/plugins/stack_connectors/server/connector_types/server_log/index.ts
@@ -14,6 +14,7 @@ import type {
   ActionTypeExecutorOptions as ConnectorTypeExecutorOptions,
   ActionTypeExecutorResult as ConnectorTypeExecutorResult,
 } from '@kbn/actions-plugin/server/types';
+import { createTaskRunError, TaskErrorSource } from '@kbn/task-manager-plugin/server';
 import {
   AlertingConnectorFeatureId,
   UptimeConnectorFeatureId,
@@ -78,6 +79,7 @@ async function executor(
   execOptions: ServerLogConnectorTypeExecutorOptions
 ): Promise<ConnectorTypeExecutorResult<void>> {
   const { actionId, params, logger } = execOptions;
+  throw createTaskRunError(new Error('fail'), TaskErrorSource.USER);

   const sanitizedMessage = withoutControlCharacters(params.message);
```

Create a rule that will trigger a server log action. Let the action run
and then check the metrics endpoint at
https://localhost:5601/api/task_manager/metrics?reset=false to see that
this error was captured as a user error.

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
ymao1 and kibanamachine authored May 24, 2024
1 parent 9e32b69 commit 1e9a2ed
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 2 deletions.
31 changes: 30 additions & 1 deletion x-pack/plugins/actions/server/lib/action_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { finished } from 'stream/promises';
import { PassThrough } from 'stream';
import { SecurityConnectorFeatureId } from '../../common';
import { TaskErrorSource } from '@kbn/task-manager-plugin/common';
import { getErrorSource } from '@kbn/task-manager-plugin/server/task_running';
import { createTaskRunError, getErrorSource } from '@kbn/task-manager-plugin/server/task_running';

const actionExecutor = new ActionExecutor({ isESOCanEncrypt: true });
const services = actionsMock.createServices();
Expand Down Expand Up @@ -1199,6 +1199,35 @@ describe('Action Executor', () => {
});
});

test(`${label} logs warning captures source when executor throws error with error source`, async () => {
const err = createTaskRunError(
new Error('this action execution is intended to fail'),
TaskErrorSource.USER
);
err.stack = 'foo error\n stack 1\n stack 2\n stack 3';
connectorType.executor.mockRejectedValueOnce(err);
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(
connectorSavedObject
);
connectorTypeRegistry.get.mockReturnValueOnce(connectorType);

let executorResult;
if (executeUnsecure) {
executorResult = await actionExecutor.executeUnsecured(executeUnsecuredParams);
} else {
executorResult = await actionExecutor.execute(executeParams);
}

expect(executorResult?.errorSource).toBe(TaskErrorSource.USER);
expect(loggerMock.warn).toBeCalledWith(
'action execution failure: test:1: 1: an error occurred while running the action: this action execution is intended to fail; retry: true'
);
expect(loggerMock.error).toBeCalledWith(err, {
error: { stack_trace: 'foo error\n stack 1\n stack 2\n stack 3' },
tags: ['test', '1', 'action-run-failed'],
});
});

test(`${label} logs warning when executor returns invalid status`, async () => {
connectorType.executor.mockResolvedValueOnce({
actionId: '1',
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/actions/server/lib/action_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SpacesServiceStart } from '@kbn/spaces-plugin/server';
import { IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server';
import { AuthenticatedUser, SecurityPluginStart } from '@kbn/security-plugin/server';
import { createTaskRunError, TaskErrorSource } from '@kbn/task-manager-plugin/server';
import { getErrorSource } from '@kbn/task-manager-plugin/server/task_running';
import { getGenAiTokenTracking, shouldTrackGenAiToken } from './gen_ai_token_tracking';
import {
validateConfig,
Expand Down Expand Up @@ -507,6 +508,7 @@ export class ActionExecutor {
rawResult.errorSource = TaskErrorSource.USER;
}
} catch (err) {
const errorSource = getErrorSource(err) || TaskErrorSource.FRAMEWORK;
if (err.reason === ActionExecutionErrorReason.Authorization) {
rawResult = err.result;
} else {
Expand All @@ -517,7 +519,7 @@ export class ActionExecutor {
serviceMessage: err.message,
error: err,
retry: true,
errorSource: TaskErrorSource.FRAMEWORK,
errorSource,
};
}
}
Expand Down

0 comments on commit 1e9a2ed

Please sign in to comment.