Skip to content

Commit c9c4740

Browse files
authored
Support recording index resolution results on IndicesRequest (#134783)
This PR introduces `ResolvedIndexExpression` and `ResolvedIndexExpressions` classes to capture context about index resolution (local results, exceptions, and unresolved remote expressions). `IndicesRequest.Replaceable` now provides optional methods to set and retrieve resolved index expressions, enabling requests to carry richer resolution metadata alongside the existing `indices(...)` API. Follow up work will plug this class into `IndicesAndAliasesResolver`. This change is motivated by the requirements around error handling for cross-project search where error handling is performed post local index resolution in some cases. Closes: ES-12688
1 parent fda6992 commit c9c4740

File tree

3 files changed

+126
-1
lines changed

3 files changed

+126
-1
lines changed

server/src/main/java/org/elasticsearch/action/IndicesRequest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.elasticsearch.action;
1111

1212
import org.elasticsearch.action.support.IndicesOptions;
13+
import org.elasticsearch.core.Nullable;
1314
import org.elasticsearch.index.shard.ShardId;
1415

1516
import java.util.Collection;
@@ -48,9 +49,25 @@ interface Replaceable extends IndicesRequest {
4849
*/
4950
IndicesRequest indices(String... indices);
5051

52+
/**
53+
* Record the results of index resolution. See {@link ResolvedIndexExpressions} for details.
54+
* Note: this method does not replace {@link #indices(String...)}. {@link #indices(String...)} must still be called to update
55+
* the actual list of indices the request relates to.
56+
*/
57+
default void setResolvedIndexExpressions(ResolvedIndexExpressions expressions) {}
58+
59+
/**
60+
* Returns the results of index resolution, if recorded via
61+
* {@link #setResolvedIndexExpressions(ResolvedIndexExpressions)}. Null if not recorded.
62+
*/
63+
@Nullable
64+
default ResolvedIndexExpressions getResolvedIndexExpressions() {
65+
return null;
66+
}
67+
5168
/**
5269
* Determines whether the request can contain indices on a remote cluster.
53-
*
70+
* <p>
5471
* NOTE in theory this method can belong to the {@link IndicesRequest} interface because whether a request
5572
* allowing remote indices has no inherent relationship to whether it is {@link Replaceable} or not.
5673
* However, we don't have an existing request that is non-replaceable but allows remote indices.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.action;
11+
12+
import org.elasticsearch.ElasticsearchException;
13+
import org.elasticsearch.core.Nullable;
14+
15+
import java.util.List;
16+
17+
/**
18+
* This class allows capturing context about index expression replacements performed on an {@link IndicesRequest.Replaceable} during
19+
* index resolution, in particular the results of local resolution, and the remote (unresolved) expressions if any.
20+
* <p>
21+
* The replacements are separated into local and remote expressions.
22+
* For local expressions, the class allows recording local index resolution results along with failure info.
23+
* For remote expressions, only the expressions are recorded.
24+
*
25+
* <p>An example structure is:</p>
26+
*
27+
* <pre>{@code
28+
* {
29+
* "original": "my-index-*",
30+
* "localExpressions": {
31+
* "expressions": ["my-index-000001", "my-index-000002"],
32+
* "localIndexResolutionResult": "SUCCESS"
33+
* },
34+
* "remoteExpressions": ["remote1:my-index-*", "remote2:my-index-*"]
35+
* }
36+
* }</pre>
37+
*
38+
* @param original the original index expression, as provided by the user
39+
* @param localExpressions the local expressions that replace the original along with their resolution result
40+
* and failure info
41+
* @param remoteExpressions the remote expressions that replace the original
42+
*/
43+
public record ResolvedIndexExpression(String original, LocalExpressions localExpressions, List<String> remoteExpressions) {
44+
/**
45+
* Indicates if a local index resolution attempt was successful or failed.
46+
* Failures can be due to missing concrete resources or unauthorized concrete resources.
47+
* A wildcard expression resolving to nothing is still considered a successful resolution.
48+
*/
49+
enum LocalIndexResolutionResult {
50+
SUCCESS,
51+
CONCRETE_RESOURCE_MISSING,
52+
CONCRETE_RESOURCE_UNAUTHORIZED,
53+
}
54+
55+
/**
56+
* Represents local (non-remote) resolution results, including expanded indices, and the resolution result.
57+
*/
58+
public record LocalExpressions(
59+
List<String> expressions,
60+
LocalIndexResolutionResult localIndexResolutionResult,
61+
@Nullable ElasticsearchException exception
62+
) {
63+
public LocalExpressions {
64+
assert localIndexResolutionResult != LocalIndexResolutionResult.SUCCESS || exception == null
65+
: "If the local resolution result is SUCCESS, exception must be null";
66+
}
67+
}
68+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.action;
11+
12+
import java.util.Map;
13+
14+
/**
15+
* A collection of {@link ResolvedIndexExpression}, keyed by the original expression.
16+
*
17+
* <p>An example structure is:</p>
18+
*
19+
* <pre>{@code
20+
* {
21+
* "my-index-*": {
22+
* "original": "my-index-*",
23+
* "localExpressions": {
24+
* "expressions": ["my-index-000001", "my-index-000002"],
25+
* "localIndexResolutionResult": "SUCCESS"
26+
* },
27+
* "remoteExpressions": ["remote1:my-index-*", "remote2:my-index-*"]
28+
* },
29+
* "my-index-000001": {
30+
* "original": "my-index-000001",
31+
* "localExpressions": {
32+
* "expressions": ["my-index-000001"],
33+
* "localIndexResolutionResult": "SUCCESS"
34+
* },
35+
* "remoteExpressions": ["remote1:my-index-000001", "remote2:my-index-000001"]
36+
* }
37+
* }
38+
* }</pre>
39+
*/
40+
public record ResolvedIndexExpressions(Map<String, ResolvedIndexExpression> expressions) {}

0 commit comments

Comments
 (0)