Skip to content

Commit c2466b8

Browse files
Async indexAbstractionResolver.resolveIndexAbstractions
1 parent 9c3bc7d commit c2466b8

File tree

3 files changed

+143
-75
lines changed

3 files changed

+143
-75
lines changed

server/src/main/java/org/elasticsearch/cluster/metadata/IndexAbstractionResolver.java

Lines changed: 112 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,26 @@
88

99
package org.elasticsearch.cluster.metadata;
1010

11+
import org.elasticsearch.action.ActionListener;
12+
import org.elasticsearch.action.ActionRunnable;
13+
import org.elasticsearch.action.StepListener;
1114
import org.elasticsearch.action.support.IndicesOptions;
15+
import org.elasticsearch.action.support.PlainActionFuture;
1216
import org.elasticsearch.common.regex.Regex;
17+
import org.elasticsearch.common.util.AsyncSupplier;
18+
import org.elasticsearch.common.util.concurrent.FutureUtils;
1319
import org.elasticsearch.index.IndexNotFoundException;
1420

1521
import java.util.ArrayList;
1622
import java.util.Arrays;
1723
import java.util.Collection;
1824
import java.util.HashSet;
25+
import java.util.Iterator;
1926
import java.util.List;
2027
import java.util.Set;
28+
import java.util.concurrent.TimeUnit;
29+
import java.util.concurrent.atomic.AtomicBoolean;
30+
import java.util.concurrent.atomic.AtomicReference;
2131

2232
public class IndexAbstractionResolver {
2333

@@ -43,69 +53,116 @@ public List<String> resolveIndexAbstractions(Iterable<String> indices, IndicesOp
4353
public List<String> resolveIndexAbstractions(Iterable<String> indices, IndicesOptions indicesOptions, Metadata metadata,
4454
Collection<String> availableIndexAbstractions, boolean replaceWildcards,
4555
boolean includeDataStreams) {
46-
List<String> finalIndices = new ArrayList<>();
47-
boolean wildcardSeen = false;
48-
for (String index : indices) {
49-
String indexAbstraction;
50-
boolean minus = false;
51-
if (index.charAt(0) == '-' && wildcardSeen) {
52-
indexAbstraction = index.substring(1);
53-
minus = true;
54-
} else {
55-
indexAbstraction = index;
56-
}
56+
PlainActionFuture<List<String>> future = PlainActionFuture.newFuture();
57+
// if the supplier is not async, the method is not async, hence get is non-blocking
58+
resolveIndexAbstractions(indices, indicesOptions, metadata, listener -> listener.onResponse(availableIndexAbstractions),
59+
replaceWildcards,
60+
includeDataStreams,
61+
future);
62+
return FutureUtils.get(future, 0, TimeUnit.MILLISECONDS);
63+
}
5764

58-
// we always need to check for date math expressions
59-
final String dateMathName = indexNameExpressionResolver.resolveDateMathExpression(indexAbstraction);
60-
if (dateMathName != indexAbstraction) {
61-
assert dateMathName.equals(indexAbstraction) == false;
62-
if (replaceWildcards && Regex.isSimpleMatchPattern(dateMathName)) {
63-
// continue
64-
indexAbstraction = dateMathName;
65-
} else if (availableIndexAbstractions.contains(dateMathName) &&
66-
isIndexVisible(indexAbstraction, dateMathName, indicesOptions, metadata, includeDataStreams, true)) {
67-
if (minus) {
68-
finalIndices.remove(dateMathName);
69-
} else {
70-
finalIndices.add(dateMathName);
71-
}
65+
public void resolveIndexAbstractions(Iterable<String> indices, IndicesOptions indicesOptions, Metadata metadata,
66+
AsyncSupplier<Collection<String>> availableIndexAbstractionsSupplier, boolean replaceWildcards,
67+
boolean includeDataStreams, ActionListener<List<String>> listener) {
68+
final Iterator<String> indicesIterator = indices.iterator();
69+
final Runnable evalItems = new ActionRunnable(listener) {
70+
71+
final List<String> finalIndices = new ArrayList<>();
72+
final AtomicBoolean wildcardSeen = new AtomicBoolean(false);
73+
74+
@Override
75+
public void doRun() {
76+
if (false == indicesIterator.hasNext()) {
77+
listener.onResponse(finalIndices);
7278
} else {
73-
if (indicesOptions.ignoreUnavailable() == false) {
74-
throw new IndexNotFoundException(dateMathName);
79+
final String index = indicesIterator.next();
80+
final AtomicReference<String> indexAbstraction;
81+
final boolean minus;
82+
if (index.charAt(0) == '-' && wildcardSeen.get()) {
83+
indexAbstraction = new AtomicReference<>(index.substring(1));
84+
minus = true;
85+
} else {
86+
indexAbstraction = new AtomicReference<>(index);
87+
minus = false;
7588
}
76-
}
77-
}
7889

79-
if (replaceWildcards && Regex.isSimpleMatchPattern(indexAbstraction)) {
80-
wildcardSeen = true;
81-
Set<String> resolvedIndices = new HashSet<>();
82-
for (String authorizedIndex : availableIndexAbstractions) {
83-
if (Regex.simpleMatch(indexAbstraction, authorizedIndex) &&
84-
isIndexVisible(indexAbstraction, authorizedIndex, indicesOptions, metadata, includeDataStreams)) {
85-
resolvedIndices.add(authorizedIndex);
86-
}
87-
}
88-
if (resolvedIndices.isEmpty()) {
89-
//es core honours allow_no_indices for each wildcard expression, we do the same here by throwing index not found.
90-
if (indicesOptions.allowNoIndices() == false) {
91-
throw new IndexNotFoundException(indexAbstraction);
92-
}
93-
} else {
94-
if (minus) {
95-
finalIndices.removeAll(resolvedIndices);
90+
// we always need to check for date math expressions
91+
final String dateMathName = indexNameExpressionResolver.resolveDateMathExpression(indexAbstraction.get());
92+
final StepListener<Void> evaluateDateMathStep = new StepListener<>();
93+
if (dateMathName != indexAbstraction.get()) {
94+
assert dateMathName.equals(indexAbstraction.get()) == false;
95+
if (replaceWildcards && Regex.isSimpleMatchPattern(dateMathName)) {
96+
// this is a date math and a wildcard
97+
indexAbstraction.set(dateMathName);
98+
// continue
99+
evaluateDateMathStep.onResponse(null);
100+
} else {
101+
availableIndexAbstractionsSupplier.getAsync(ActionListener.wrap(availableIndexAbstractions -> {
102+
if (availableIndexAbstractions.contains(dateMathName) &&
103+
isIndexVisible(indexAbstraction.get(), dateMathName, indicesOptions, metadata, includeDataStreams, true)) {
104+
if (minus) {
105+
finalIndices.remove(dateMathName);
106+
} else {
107+
finalIndices.add(dateMathName);
108+
}
109+
} else {
110+
if (indicesOptions.ignoreUnavailable() == false) {
111+
listener.onFailure(new IndexNotFoundException(dateMathName));
112+
return;
113+
}
114+
}
115+
evaluateDateMathStep.onResponse(null);
116+
}, listener::onFailure));
117+
}
96118
} else {
97-
finalIndices.addAll(resolvedIndices);
119+
evaluateDateMathStep.onResponse(null);
98120
}
99-
}
100-
} else if (dateMathName.equals(indexAbstraction)) {
101-
if (minus) {
102-
finalIndices.remove(indexAbstraction);
103-
} else {
104-
finalIndices.add(indexAbstraction);
121+
122+
evaluateDateMathStep.whenComplete(anotherVoid -> {
123+
if (replaceWildcards && Regex.isSimpleMatchPattern(indexAbstraction.get())) {
124+
wildcardSeen.set(true);
125+
availableIndexAbstractionsSupplier.getAsync(ActionListener.wrap(availableIndexAbstractions -> {
126+
Set<String> resolvedIndices = new HashSet<>();
127+
for (String authorizedIndex : availableIndexAbstractions) {
128+
if (Regex.simpleMatch(indexAbstraction.get(), authorizedIndex) &&
129+
isIndexVisible(indexAbstraction.get(), authorizedIndex, indicesOptions, metadata, includeDataStreams)) {
130+
resolvedIndices.add(authorizedIndex);
131+
}
132+
}
133+
if (resolvedIndices.isEmpty()) {
134+
//es core honours allow_no_indices for each wildcard expression, we do the same here by throwing index not found.
135+
if (indicesOptions.allowNoIndices() == false) {
136+
listener.onFailure(new IndexNotFoundException(indexAbstraction.get()));
137+
return;
138+
}
139+
} else {
140+
if (minus) {
141+
finalIndices.removeAll(resolvedIndices);
142+
} else {
143+
finalIndices.addAll(resolvedIndices);
144+
}
145+
}
146+
// next expression item
147+
this.doRun();
148+
}, listener::onFailure));
149+
} else if (dateMathName.equals(indexAbstraction.get())) {
150+
if (minus) {
151+
finalIndices.remove(indexAbstraction.get());
152+
} else {
153+
finalIndices.add(indexAbstraction.get());
154+
}
155+
// next expression item
156+
this.doRun();
157+
} else {
158+
// next expression item
159+
this.doRun();
160+
}
161+
}, listener::onFailure);
105162
}
106163
}
107-
}
108-
return finalIndices;
164+
};
165+
evalItems.run();
109166
}
110167

111168
public static boolean isIndexVisible(String expression, String index, IndicesOptions indicesOptions, Metadata metadata,

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ private void authorizeAction(final RequestInfo requestInfo, final String request
283283
authzEngine.authorizeClusterAction(requestInfo, authzInfo, clusterAuthzListener);
284284
} else if (isIndexAction(action)) {
285285
final Metadata metadata = clusterService.state().metadata();
286-
final AsyncSupplier<List<String>> authorizedIndicesSupplier = new CachingAsyncSupplier<>(authzIndicesListener ->
286+
final AsyncSupplier<Collection<String>> authorizedIndicesSupplier = new CachingAsyncSupplier<>(authzIndicesListener ->
287287
authzEngine.loadAuthorizedIndices(requestInfo, authzInfo, metadata.getIndicesLookup(),
288-
authzIndicesListener));
288+
authzIndicesListener.map(list -> list)));
289289
final AsyncSupplier<ResolvedIndices> resolvedIndicesAsyncSupplier =
290290
new CachingAsyncSupplier<>((resolvedIndicesListener) -> indicesAndAliasesResolver.resolve(request, metadata,
291291
authorizedIndicesSupplier, resolvedIndicesListener.delegateResponse((l, e) -> {

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class IndicesAndAliasesResolver {
102102
* Otherwise, <em>N</em> will be added to the <em>local</em> index list.
103103
*/
104104

105-
void resolve(TransportRequest request, Metadata metadata, AsyncSupplier<List<String>> authorizedIndicesSupplier,
105+
void resolve(TransportRequest request, Metadata metadata, AsyncSupplier<Collection<String>> authorizedIndicesSupplier,
106106
ActionListener<ResolvedIndices> listener) {
107107
if (request instanceof IndicesAliasesRequest) {
108108
IndicesAliasesRequest indicesAliasesRequest = (IndicesAliasesRequest) request;
@@ -133,7 +133,8 @@ ResolvedIndices resolve(TransportRequest request, Metadata metadata, List<String
133133
return FutureUtils.get(future, 0, TimeUnit.MILLISECONDS);
134134
}
135135

136-
void resolveIndicesAndAliases(IndicesRequest indicesRequest, Metadata metadata, AsyncSupplier<List<String>> authorizedIndicesSupplier
136+
void resolveIndicesAndAliases(IndicesRequest indicesRequest, Metadata metadata,
137+
AsyncSupplier<Collection<String>> authorizedIndicesSupplier
137138
, ActionListener<ResolvedIndices> listener) {
138139
if (indicesRequest instanceof PutMappingRequest && ((PutMappingRequest) indicesRequest).getConcreteIndex() != null) {
139140
/*
@@ -175,26 +176,36 @@ void resolveIndicesAndAliases(IndicesRequest indicesRequest, Metadata metadata,
175176
resolvedIndicesStepListener.onResponse(new ResolvedIndices.Builder());
176177
}
177178
} else {
178-
authorizedIndicesSupplier.getAsync(ActionListener.wrap(authorizedIndices -> {
179-
final ResolvedIndices split;
179+
final ResolvedIndices split;
180+
try {
180181
if (allowsRemoteIndices(indicesRequest)) {
181182
split = remoteClusterResolver.splitLocalAndRemoteIndexNames(indicesRequest.indices());
182183
} else {
183184
split = new ResolvedIndices(Arrays.asList(indicesRequest.indices()), Collections.emptyList());
184185
}
185-
ResolvedIndices.Builder resolvedIndicesBuilder = new ResolvedIndices.Builder();
186-
// cannot pass the async supplier
187-
List<String> replaced = indexAbstractionResolver.resolveIndexAbstractions(split.getLocal(), indicesOptions, metadata,
188-
authorizedIndices, replaceWildcards, indicesRequest.includeDataStreams());
189-
if (indicesOptions.ignoreUnavailable()) {
190-
//out of all the explicit names (expanded from wildcards and original ones that were left untouched)
191-
//remove all the ones that the current user is not authorized for and ignore them
192-
replaced = replaced.stream().filter(authorizedIndices::contains).collect(Collectors.toList());
193-
}
194-
resolvedIndicesBuilder.addLocal(replaced);
195-
resolvedIndicesBuilder.addRemote(split.getRemote());
196-
resolvedIndicesStepListener.onResponse(resolvedIndicesBuilder);
197-
}, listener::onFailure));
186+
} catch (Exception e) {
187+
listener.onFailure(e);
188+
return;
189+
}
190+
indexAbstractionResolver.resolveIndexAbstractions(split.getLocal(), indicesOptions, metadata,
191+
authorizedIndicesSupplier, replaceWildcards, indicesRequest.includeDataStreams(),
192+
ActionListener.wrap(replaced -> {
193+
ResolvedIndices.Builder resolvedIndicesBuilder = new ResolvedIndices.Builder();
194+
if (indicesOptions.ignoreUnavailable()) {
195+
authorizedIndicesSupplier.getAsync(ActionListener.wrap(authorizedIndices -> {
196+
//out of all the explicit names (expanded from wildcards and original ones that were left untouched)
197+
//remove all the ones that the current user is not authorized for and ignore them
198+
resolvedIndicesBuilder.addLocal(replaced.stream().filter(authorizedIndices::contains)
199+
.collect(Collectors.toList()));
200+
resolvedIndicesBuilder.addRemote(split.getRemote());
201+
resolvedIndicesStepListener.onResponse(resolvedIndicesBuilder);
202+
}, listener::onFailure));
203+
} else {
204+
resolvedIndicesBuilder.addLocal(replaced);
205+
resolvedIndicesBuilder.addRemote(split.getRemote());
206+
resolvedIndicesStepListener.onResponse(resolvedIndicesBuilder);
207+
}
208+
}, listener::onFailure));
198209
}
199210

200211
final StepListener<Void> indicesReplacedStepListener = new StepListener<>();
@@ -301,7 +312,7 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, Metadata
301312
* request's concrete index is not in the list of authorized indices, then we need to look to
302313
* see if this can be authorized against an alias
303314
*/
304-
static void getPutMappingIndexOrAlias(PutMappingRequest request, AsyncSupplier<List<String>> authorizedIndicesSupplier,
315+
static void getPutMappingIndexOrAlias(PutMappingRequest request, AsyncSupplier<Collection<String>> authorizedIndicesSupplier,
305316
Metadata metadata, ActionListener<String> listener) {
306317
final String concreteIndexName = request.getConcreteIndex().getName();
307318
// validate that the concrete index exists, otherwise there is no remapping that we could do

0 commit comments

Comments
 (0)