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] add pre-create, pre-update, and post-delete hooks for connectors #194081

Merged
merged 10 commits into from
Oct 9, 2024

Conversation

pmuellr
Copy link
Member

@pmuellr pmuellr commented Sep 25, 2024

Extracted from #189027, commit c97afeb

Summary

Allows connector types to add functions to be called when connectors are created, updated, and deleted.

Checklist

@pmuellr
Copy link
Member Author

pmuellr commented Sep 26, 2024

Here are some things to consider looking at the original commit:

  • naming: "event" implies to me all kinds of semantics that don't fit: truly async (no one is waiting for the return), that it won't affect the flow of the event producer, etc. We previously referenced "hook" before, and I still think that fits. Maybe "callback"? We've also used "middleware" in task manager, though I believe it's unused, and don't think that term fits either.
    So maybe preSaveHook and postDeleteHook?
  • if an error occurs in the hook, should it fail the create/update/delete? At first I was a little worried about this, but I think it's probably the best - it easily allows a hook to fail the operation, and if a hook is really part of the connector so I'm not sure we want a lot of more "sophisticated" ways of indicating success/failure.
  • what to pass to the hooks. Currently scopedClusterClient is passed, but this doesn't seem right. Currently we send a "Request" object in, this feels like it's good enough. (note: see correction in comment below). Authors can also (I think) curry in their own objects when the connector is registered, as we do for other things
  • we should add doc for this, wherever we are keeping doc these days (at least the README I guess)

@pmuellr
Copy link
Member Author

pmuellr commented Sep 26, 2024

I was mistaken about the executor only getting passed a request. Here's the full signature for the executor:

export interface ActionTypeExecutorOptions<
Config extends Record<string, unknown>,
Secrets extends Record<string, unknown>,
Params
> {
actionId: string;
services: Services | UnsecuredServices;
config: Config;
secrets: Secrets;
params: Params;
logger: Logger;
isEphemeral?: boolean;
taskInfo?: TaskInfo;
configurationUtilities: ActionsConfigurationUtilities;
source?: ActionExecutionSource<unknown>;
request?: KibanaRequest;
connectorUsageCollector: ConnectorUsageCollector;
}

I think we want most of these, but we could add them later as needed. Critically, we want services (or a subset).

Here's Services and UnsecuredServices:

export interface Services {
savedObjectsClient: SavedObjectsClientContract;
scopedClusterClient: ElasticsearchClient;
connectorTokenClient: ConnectorTokenClient;
}
export interface UnsecuredServices {
savedObjectsClient: ISavedObjectsRepository;
scopedClusterClient: ElasticsearchClient;
connectorTokenClient: ConnectorTokenClient;
}

@pmuellr
Copy link
Member Author

pmuellr commented Sep 26, 2024

Thought it would be interesting to think about extending this in the future. Not in this PR :-)

Currently, the hooks really just allow the hook to allow or deny the operation - they don't return anything useful. What else could they do?

  • modify the config/secrets before the create/update
  • set some "connector state" object values; a concept we don't currently support

Modifying config/secrets seems dicey - in fact, we may want to provide deep copies of these to prevent modification. But could be useful to supply additional computed values in those objects. There are noconnector type provided functions invoked iduring create/update/delete, so this may make it easier to deal with non-trivial or expensive data before it hits the executor.

The concept of "connector state" feels like something we talked about a long time ago, but there are no issues open. The idea would be to add the capability for a state object ({}) to be provided to the executor and hooks, to store some data across invocations. The use case I keep coming to (but may not be completely relevant) is the Slack API channels bit. Because of the APIs involved, it's non-trivial to search for Slack channels to provide to the user. Could we do this once, to at least cache some of the relevant bits?

The state object would be persisted (pretend with the connector, and that's probably ok, but could be a new SO), and provided to the executor and hooks. We'd need to provide a way for those functions to "return" the new state to persist, not clear the best way to do that. We likely would NOT want to return this via HTTP, but probably via the ActionsClient is fine. Seems very likely some code outside of executor/hooks would want access to this data as well.

I don't think there's anything in the current "shape" of these hooks that would make it harder to implement these, if we want to go there.

@pmuellr
Copy link
Member Author

pmuellr commented Sep 26, 2024

In b0ec428c5be6717eb2076d6ff00086870506a372 I added a minimal services object to the hook parameters, similar to the executor options. But only provided the scopedClusterClient for now.

@@ -255,6 +256,33 @@ export class ActionsClient {
}
this.context.actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);

const hookServices: HookServices = {
scopedClusterClient: this.context.scopedClusterClient.asCurrentUser,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my specific use case I need scopedClusterClient.asInternalUser, so can we just return scopedClusterClient to have a choice or expose two client?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I plan on sending this.context.scopedClusterClient, so both will be available. Easy to imagine both might be needed.

@YulNaumenko
Copy link
Contributor

Using post/preSave hooks in the commit 6f9504f

@pmuellr pmuellr force-pushed the 189027-just-actions-servers branch from 6ab5755 to 4599d00 Compare October 7, 2024 16:09
…onnectors

Extracted from elastic#189027,
commit c97afeb

Allows connector types to add functions to be called when connectors are
created, updated, and deleted.
@pmuellr pmuellr force-pushed the 189027-just-actions-servers branch from 4599d00 to c182b1b Compare October 7, 2024 16:20
@kibana-ci
Copy link
Collaborator

kibana-ci commented Oct 7, 2024

⏳ Build in-progress, with failures

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #84 / alerting api integration security and spaces enabled - Group 2 Connectors create no_kibana_privileges at space1 should call the pre-save hook appropriately
  • [job] [logs] FTR Configs #86 / alerting api integration security and spaces enabled - Group 2 Connectors create no_kibana_privileges at space1 should call the pre-save hook appropriately
  • [job] [logs] FTR Configs #86 / alerting api integration security and spaces enabled - Group 2 Connectors create no_kibana_privileges at space1 should call the pre-save hook appropriately
  • [job] [logs] FTR Configs #84 / alerting api integration security and spaces enabled - Group 2 Connectors create no_kibana_privileges at space1 should call the pre-save hook appropriately
  • [job] [logs] FTR Configs #82 / Reporting Functional Tests with Security enabled Access to Management > Reporting Download report user can access download link for export type that is no longer supported
  • [job] [logs] Jest Tests #3 / update() updates an action with all given properties
  • [job] [logs] Jest Tests #3 / update() updates an action with all given properties

History

  • 💔 Build #238598 failed 447fa3d6284628f6be92dddae39dde139b40c25c
  • 💚 Build #238208 succeeded f037dba9cab5cdfd1935c39a9f39409af0af80e0
  • 💚 Build #237461 succeeded b0ec428c5be6717eb2076d6ff00086870506a372
  • 💛 Build #237393 was flaky 74f8e98d2c848623690b4f4abb621c5df8078356

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

- re-added the update code to the new update module (update body removed from actionsClient)
- propertly set `scopedClusterClient` instead of the `asCurrentUser` flavor
- moved `tryCatch()` to `lib`
@pmuellr pmuellr added release_note:skip Skip the PR/issue when compiling release notes Feature:Actions Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) labels Oct 8, 2024
@pmuellr pmuellr marked this pull request as ready for review October 8, 2024 22:07
@pmuellr pmuellr requested a review from a team as a code owner October 8, 2024 22:07
@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

// functional version of try/catch, allows you to not have to use
// `let` vars initialied to `undefined` to capture the result value

export async function tryCatch<T>(fn: () => Promise<T>): Promise<T | Error> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handy little function to avoid having to let define a variable that would need to be set within a try / catch.

Happy to use some alternative, didn't find anything simple.

const label = `connectorId: "${id}"; type: ${actionTypeId}`;
const tags = ['post-save-hook', id];

if (actionType.postSaveHook) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the idea here that by passing in wasSuccessful the postSave hook could potentially undo whatever it did in the preSave hook if the create failed? Might be useful to be explicit about this in the README

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the exact reason, and you're right, I'll add it to the doc. Yuliia will need to make use of it, as she is calling the ES /inference endpoint in the pre hook, but if the connector create/update fails, she'll need to "undo" it ...

Copy link
Contributor

@ymao1 ymao1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

async executor({ config, secrets, params, services, actionId }) {
return { status: 'ok', actionId };
},
async preSaveHook({ connectorId, config, secrets, services, isUpdate, logger }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could add a flag to the config to force the hook to throw an error to test the error condition

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, thanks for reminding me. I do think we should have some FT that test the error conditions, but didn't think they were absolutely required, since we do test in the jest tests. And given the time frame. Also not sure how to test for the SO create/update failing in FT, but could likely do it in a jest integration test.

So was planning on opening an issue to add those later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@YulNaumenko YulNaumenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thank you @pmuellr for shaping it and making through the line! Docs are awesome 💅

@pmuellr
Copy link
Member Author

pmuellr commented Oct 9, 2024

I thought it wouldn't do a full re-build if just an .md file changed, but maybe I'm thinking .asciidoc :-)

I'll plan on merging this after THAT build completes ...

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
actions 308 314 +6

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
actions 33 36 +3
Unknown metric groups

API count

id before after diff
actions 314 320 +6

History

@pmuellr pmuellr merged commit 57096d1 into elastic:main Oct 9, 2024
26 checks passed
@kibanamachine
Copy link
Contributor

Starting backport for target branches: 8.x

https://github.com/elastic/kibana/actions/runs/11263635266

kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Oct 9, 2024
…onnectors (elastic#194081)

Allows connector types to add functions to be called when connectors are created, updated, and deleted.

Extracted from elastic#189027, commit c97afeb

Co-authored-by: Yuliia Naumenko <[email protected]>
(cherry picked from commit 57096d1)
@kibanamachine
Copy link
Contributor

💚 All backports created successfully

Status Branch Result
8.x

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Oct 9, 2024
… for connectors (#194081) (#195686)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[ResponseOps] add pre-create, pre-update, and post-delete hooks for
connectors (#194081)](#194081)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Patrick
Mueller","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-09T21:52:09Z","message":"[ResponseOps]
add pre-create, pre-update, and post-delete hooks for connectors
(#194081)\n\nAllows connector types to add functions to be called when
connectors are created, updated, and deleted.\r\n\r\nExtracted from
#189027, commit
c97afeb\r\n\r\nCo-authored-by: Yuliia
Naumenko
<[email protected]>","sha":"57096d1f4fbcb4fc0135505b6c6100566ff08cc9","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Feature:Actions","Team:ResponseOps","v9.0.0","backport:prev-minor","v8.16.0"],"title":"[ResponseOps]
add pre-create, pre-update, and post-delete hooks for
connectors","number":194081,"url":"https://github.com/elastic/kibana/pull/194081","mergeCommit":{"message":"[ResponseOps]
add pre-create, pre-update, and post-delete hooks for connectors
(#194081)\n\nAllows connector types to add functions to be called when
connectors are created, updated, and deleted.\r\n\r\nExtracted from
#189027, commit
c97afeb\r\n\r\nCo-authored-by: Yuliia
Naumenko
<[email protected]>","sha":"57096d1f4fbcb4fc0135505b6c6100566ff08cc9"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194081","number":194081,"mergeCommit":{"message":"[ResponseOps]
add pre-create, pre-update, and post-delete hooks for connectors
(#194081)\n\nAllows connector types to add functions to be called when
connectors are created, updated, and deleted.\r\n\r\nExtracted from
#189027, commit
c97afeb\r\n\r\nCo-authored-by: Yuliia
Naumenko
<[email protected]>","sha":"57096d1f4fbcb4fc0135505b6c6100566ff08cc9"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Patrick Mueller <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) Feature:Actions release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v8.16.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants