-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Explicit index resolution API #18523
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
base: main
Are you sure you want to change the base?
Explicit index resolution API #18523
Conversation
❌ Gradle check result for 09a308b: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #18523 +/- ##
============================================
+ Coverage 73.01% 73.10% +0.09%
- Complexity 70585 70762 +177
============================================
Files 5723 5726 +3
Lines 323509 323905 +396
Branches 46852 46889 +37
============================================
+ Hits 236222 236803 +581
+ Misses 68213 68017 -196
- Partials 19074 19085 +11 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Task task, | ||
String action, | ||
Request request, | ||
ActionRequestMetadata<Request, Response> actionRequestMetadata, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on slack, can this be done in a backward compatible way? There are many existing ActionFilters across the plugins that would need to be updated to account for this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have been thinking about this, but I did not find really good options.
- We could keep the existing method and add a new one as a
default
implementation:
public interface ActionFilter {
<Request extends ActionRequest, Response extends ActionResponse> void apply(
Task task,
String action,
Request request,
ActionListener<Response> listener,
ActionFilterChain<Request, Response> chain
);
default <Request extends ActionRequest, Response extends ActionResponse> void apply(
Task task,
String action,
Request request,
ActionRequestMetadata<Request, Response> actionRequestMetadata,
ActionListener<Response> listener,
ActionFilterChain<Request, Response> chain
) {
this.apply(task, action, request, listener, chain);
}
This has the advantage that existing code does not need to be touched. However, any code which wants to use the new interface actually has to implement two methods: The first apply()
without the new parameter, which would be actually dead code. And the second apply()
with the new parameter.
- To fix this, one could make both methods default methods:
public interface ActionFilter {
@Deprecated
default <Request extends ActionRequest, Response extends ActionResponse> void apply(
Task task,
String action,
Request request,
ActionListener<Response> listener,
ActionFilterChain<Request, Response> chain
) {
throw new UnsupportedOperationException("Not implemented");
}
default <Request extends ActionRequest, Response extends ActionResponse> void apply(
Task task,
String action,
Request request,
ActionRequestMetadata<Request, Response> actionRequestMetadata,
ActionListener<Response> listener,
ActionFilterChain<Request, Response> chain
) {
this.apply(task, action, request, listener, chain);
}
This would probably work, but it seems to be messy and potentially confusing to developers.
- That's the reason why I chose the present approach for now. It creates a bit of work, which is however straight-forward. And the result is clean. Actually, back in the ES days, it was a regular occurence that Java APIs changed in breaking ways across minor release versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, the interface is marked as @opensearch.internal
which created the impression that there are no compatiblity guarantees here.
private final Request request; | ||
|
||
private ResolvedIndices resolvedIndices; | ||
private boolean resolvedIndicesInitialized; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we rename this variable as it indicates that resolution has completed. Maybe isIndexResolutionCompleted()
or something to that effect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure!
* the action will operate on. The best way to achieve this, is to move the index extraction code from the execute | ||
* methods into reusable methods and to depend on these both for execution and reporting. | ||
*/ | ||
public interface TransportIndicesResolvingAction<Request extends ActionRequest> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly support offloading this to the existing transport actions and obviating the need for custom logic in the security plugin to handle all of the different action types: https://github.com/opensearch-project/security/blob/main/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java#L645-L840
nit: I see that Request
is used as a generic here. Can we shorten that to a single char like R
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the transport action class hierarchy, it seems to be common to use Request
and (if needed) Response
as the names for the generic parameters:
OpenSearch/server/src/main/java/org/opensearch/action/support/TransportAction.java
Lines 58 to 60 in 63921b8
@PublicApi(since = "1.0.0") | |
public abstract class TransportAction<Request extends ActionRequest, Response extends ActionResponse> { | |
I have followed this pattern.
@saratvemulapalli @sohami @owaiskazi19 @dbwiddis @jainankitk wdyt of the proposed changes in this PR? The main goal is to offload concrete index resolution to the core instead of security knowing how to extract from all different types of requests as it does in https://github.com/opensearch-project/security/blob/main/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java#L661-L837 |
* the action will operate on. The best way to achieve this, is to move the index extraction code from the execute | ||
* methods into reusable methods and to depend on these both for execution and reporting. | ||
*/ | ||
public interface TransportIndicesResolvingAction<Request extends ActionRequest> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using an interface is a good approach for exposing index resolution logic, rather than requiring each plugin to implement it independently. I like the idea of having the core provide this functionality.
@nibix @cwperks The interface can also be extended with other helpful methods for plugins, such as:
getOperationType
, requiresClusterState
or a method to see if the action involves system indices.
Additionally, we could consider renaming the interface to TransportIndicesAwareAction
, making it more flexible and future-proof for supporting additional capabilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the feedback!
I also believe that there is potential for more meta data retrieval methods.
However, I am wondering whether it might make sense to have separate interfaces for such methods. The reasons:
- Adding methods to interfaces retroactively is difficult, because it requires the immediate adaption of all implementing classes. Alternatively, one can resort to default methods - which is however in my perception mostly a kludge.
- I can imagine that not all transport actions will support the same meta data methods. There are certainly more transport actions that operate on the cluster state than actions that resolve index names.
Thus, maybe, it would be better to define an own interface for each purpose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we could start with TransportIndicesAwareAction
interface first and then if there's a need for more retrieval methods in the future, we can decide on if creating a new interface or adding in TransportIndicesAwareAction
would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me! 👍
@nibix Can |
The current version has separate attributes for remote indices and local indices, aliases and data streams. Currently, local indices, alises and datastreams are however in one bucket.
Yes, wildcards will be resolved in the ResolvedIndices object (that's one of the main objectives of the class :) |
This PR is stalled because it has been open for 30 days with no activity. |
Additionally, I need an API extension to let IndexNameExpressionResolver to optionally not fail on non-existant indices. This is also mostly done, I will try to push this ASAP. Another thing I still have to think about: How to test this. |
I would be interested to learn about the details of this case, just to make sure that we are aligned |
@kumargu to comment further, but my understanding is they need to rolve the concrete indices from the request to then determine the correct set of keys for decryption (and whether the user has access). |
Hi @nibix , for index-level encryption we need to know -
|
FYI @nibix I found another area of the core trying to do extraction of indices from an ActionRequest in an ActionFilter. See: https://github.com/opensearch-project/OpenSearch/blob/main/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/rule/attribute_extractor/IndicesExtractor.java @kaushalmahi12 @ruai0511 FYI that IndicesExtractor is missing a lot of cases for resolving to concrete indices from a generic ActionRequest. See the IndexResolverReplacer from the security plugin which currently has the most exhaustive logic. The change @nibix is introducing in this PR is for resolving to concrete indices from any type of transport action associated with indices so WLM would benefit from these changes as well. @kkhatua tagging you as well. |
Hm, so is that only about |
Yes. Index Level Encryption can be only updated on family of |
e721867
to
4266dbc
Compare
❌ Gradle check result for 4266dbc: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
❌ Gradle check result for ea276fb: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
❌ Gradle check result for 004f937: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
d8b5c69
to
4c83f2b
Compare
Apologies @nibix , since this PR changed the ActionFilter extension point I did not want to move forward until the 3.4 version bump. The core is now bumped to 3.4. Could you please sync with the latest from main and I will take another pass first thing tomorrow morning? |
Collection<String> indices = noAliasesSpecified ? Arrays.asList(concreteIndices) : indexToAliasesMap.keySet(); | ||
|
||
return ResolvedIndices.of(indices) | ||
.withLocalSubActions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to wrap my head around when withLocalSubActions
should be used. I understand that some transport actions call others under the hood, but wouldn't that then delegate to the sub-transport action to figure out how to resolve the indices? Why must this always be done from the parent action?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a high level: Some transport actions perform several kinds of actions without using further underlying transport actions. A very notorious example is https://docs.opensearch.org/latest/im-plugin/index-alias/ ...
For the get aliases action we also have the challenge that the user can request aliases and indices indepentently, this is modeled here.
4c83f2b
to
c3e9de4
Compare
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
8fce793
to
7b2f1b0
Compare
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
Signed-off-by: Nils Bandener <[email protected]>
7b2f1b0
to
9e8cffa
Compare
Description
This PR introduces a mechanism that allows
ActionFilter
implementations to retrieve information about the actual indices a transport action will operate on - based on the current request. So far, theActionFilter
had access to the index information that is directly available in theActionRequest
objects. However, transport actions have quite varying opinions on how to interpret this information. Some evaluate index patterns, some only date math, some allow remote indices, and some only allow a single concrete index. The new mechanism creates interfaces that give transport actions the ability to communicate their interpretation explicitly.This PR does only add the interface, but not any implementation that uses the new information. The first implementation that consumes the information will be in the security plugin; a draft PR that shows how the information will be processed is located at opensearch-project/security#5399.
Intended use
The primary intended client will be the security plugin. In order to implement index-based access controls, the security plugin needs a reliable way to retrieve information about the indices a request refers to.
At the moment, this needs to be hard-coded for each known action request in the security plugin. This results in a very large class containing many special cases: https://github.com/opensearch-project/security/blob/c51ce46dc0d64326fe9f719e83f2cbb53147117a/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java#L652
This brings several issues:
See opensearch-project/security#5367 for more details.
Reading this PR
This is is a large PR, as it adds both the interfaces and infrastructure and also the individual method implementations to
TransportAction
classes. When reviewing, you should probably start with the inferfaces and infrastructure; if you got an overview of this, you can go ahead to the individual implementations.Main components
TransportIndicesResolvingAction
This PR introduces a new Java interface that can be implemented by
TransportActions
:OpenSearch/server/src/main/java/org/opensearch/action/support/TransportIndicesResolvingAction.java
Lines 14 to 30 in 09a308b
The method
resolveIndices
expects a request object and is supposed to return the indices, aliases and/or data streams, the respective transport action would operate on, if theexecute()
method is called.ActionRequestMetadata
A new class
ActionRequestMetadata
is introduced which is passed as additional parameter to allActionFilter.apply()
implementations:OpenSearch/server/src/main/java/org/opensearch/action/support/ActionFilter.java
Lines 56 to 63 in b008fd2
OpenSearch/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java
Lines 21 to 76 in b008fd2
OptionallyResolvedIndices, ResolvedIndices
The
ActionRequestMetadata
class givesActionFilter
implementations access to instances ofResolvedIndices
andOptionallyResolvedIndices
. The reason why the classOptionallyResolvedIndices
exists is the following: There can be situations when the resolved indices are not known. For example, that's the case when a transport action does not implement theTransportIndicesResolvingAction
interface. The existance of theOptionallyResolvedIndices
class provides a type safe and null safe way of checking whether the information is known or not. Implementations that use the classes should implement this pattern:Still, also the
OptionallyResolvedIndices
class implements a couple of methods which allow certain checks to be done without theinstanceof
checks.OpenSearch/server/src/main/java/org/opensearch/cluster/metadata/OptionallyResolvedIndices.java
Lines 18 to 117 in b008fd2
OpenSearch/server/src/main/java/org/opensearch/cluster/metadata/ResolvedIndices.java
Lines 31 to 573 in b008fd2
IndexNameExpressionResolver
The class
IndexNameExpressionResolver
has been extended by methods that returnResolvedIndices.Local.Concrete
objects. This allows transport actions to use the resolution logic fromIndexNameExpressionResolver
both for its internal purposes and also for implementing theTransportIndicesResolvingAction
interface.There is one fundamental different in the use of the resolution logic between the actual action execution and the implementation of
TransportIndicesResolvingAction
: When the actual action is executed,IndexNameExpressionResolver
performs certain validation steps: Do indices exist, does the request index fit other specified parameters, etc. For the metadata reporting, however, we want the metadata even when the validation fails. Thus, the returnedResolvedIndices.Local.Concrete
provide methods to access validated indices and method to access unvalidated names.Related Issues
Partially resolves opensearch-project/security#5367
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.