Skip to content

Commit 07f1aa5

Browse files
authored
Make remote cluster resolution stricter (#40419)
Remote cluster resolution is currently lenient, to support local indices that may contain `:` in their name. From 8.0 on, there can no longer be indices in the cluster that contain `:` in their name, hence we can make remote cluster resolution stricter. Instead of treating any index expression containing a `:` whenever there is no corresponding matching remote cluster registered, we now throw a `NoSuchRemoteClusterException`. Closes #37863
1 parent 8c7b606 commit 07f1aa5

File tree

9 files changed

+33
-55
lines changed

9 files changed

+33
-55
lines changed

server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public TransportFieldCapabilitiesAction(TransportService transportService,
6666
protected void doExecute(Task task, FieldCapabilitiesRequest request, final ActionListener<FieldCapabilitiesResponse> listener) {
6767
final ClusterState clusterState = clusterService.state();
6868
final Map<String, OriginalIndices> remoteClusterIndices = remoteClusterService.groupIndices(request.indicesOptions(),
69-
request.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState));
69+
request.indices());
7070
final OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY);
7171
final String[] concreteIndices;
7272
if (localIndices == null) {

server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener<
205205
}
206206
final ClusterState clusterState = clusterService.state();
207207
final Map<String, OriginalIndices> remoteClusterIndices = remoteClusterService.groupIndices(searchRequest.indicesOptions(),
208-
searchRequest.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState));
208+
searchRequest.indices());
209209
OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY);
210210
if (remoteClusterIndices.isEmpty()) {
211211
executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener);

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.cluster.metadata;
2121

2222
import org.elasticsearch.common.regex.Regex;
23+
import org.elasticsearch.transport.NoSuchRemoteClusterException;
2324

2425
import java.util.ArrayList;
2526
import java.util.Collections;
@@ -36,20 +37,21 @@ public final class ClusterNameExpressionResolver {
3637
private final WildcardExpressionResolver wildcardResolver = new WildcardExpressionResolver();
3738

3839
/**
39-
* Resolves the provided cluster expression to matching cluster names. This method only
40-
* supports exact or wildcard matches.
40+
* Resolves the provided cluster expression to matching cluster names. Supports exact or wildcard matches.
41+
* Throws {@link NoSuchRemoteClusterException} in case there are no registered remote clusters matching the provided expression.
4142
*
4243
* @param remoteClusters the aliases for remote clusters
4344
* @param clusterExpression the expressions that can be resolved to cluster names.
4445
* @return the resolved cluster aliases.
46+
* @throws NoSuchRemoteClusterException if there are no remote clusters matching the provided expression
4547
*/
4648
public List<String> resolveClusterNames(Set<String> remoteClusters, String clusterExpression) {
4749
if (remoteClusters.contains(clusterExpression)) {
4850
return Collections.singletonList(clusterExpression);
4951
} else if (Regex.isSimpleMatchPattern(clusterExpression)) {
5052
return wildcardResolver.resolve(remoteClusters, clusterExpression);
5153
} else {
52-
return Collections.emptyList();
54+
throw new NoSuchRemoteClusterException(clusterExpression);
5355
}
5456
}
5557

server/src/main/java/org/elasticsearch/transport/NoSuchRemoteClusterException.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
*/
3131
public final class NoSuchRemoteClusterException extends ResourceNotFoundException {
3232

33-
NoSuchRemoteClusterException(String clusterName) {
33+
public NoSuchRemoteClusterException(String clusterName) {
3434
//No node available for cluster
3535
super("no such remote cluster: [" + clusterName + "]");
3636
}

server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java

+4-22
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import java.util.NavigableSet;
4747
import java.util.Set;
4848
import java.util.TreeSet;
49-
import java.util.function.Predicate;
5049
import java.util.function.Supplier;
5150
import java.util.stream.Collectors;
5251
import java.util.stream.Stream;
@@ -244,36 +243,19 @@ static DiscoveryNode buildSeedNode(String clusterName, String address, boolean p
244243
*
245244
* @param remoteClusterNames the remote cluster names
246245
* @param requestIndices the indices in the search request to filter
247-
* @param indexExists a predicate that can test if a certain index or alias exists in the local cluster
248246
*
249247
* @return a map of grouped remote and local indices
250248
*/
251-
protected Map<String, List<String>> groupClusterIndices(Set<String> remoteClusterNames, String[] requestIndices,
252-
Predicate<String> indexExists) {
249+
protected Map<String, List<String>> groupClusterIndices(Set<String> remoteClusterNames, String[] requestIndices) {
253250
Map<String, List<String>> perClusterIndices = new HashMap<>();
254251
for (String index : requestIndices) {
255252
int i = index.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR);
256253
if (i >= 0) {
257254
String remoteClusterName = index.substring(0, i);
258255
List<String> clusters = clusterNameResolver.resolveClusterNames(remoteClusterNames, remoteClusterName);
259-
if (clusters.isEmpty() == false) {
260-
if (indexExists.test(index)) {
261-
//We use ":" as a separator for remote clusters. There may be a conflict if there is an index that is named
262-
//remote_cluster_alias:index_name - for this case we fail the request. The user can easily change the cluster alias
263-
//if that happens. Note that indices and aliases can be created with ":" in their names names up to 6.last, which
264-
//means such names need to be supported until 7.last. It will be possible to remove this check from 8.0 on.
265-
throw new IllegalArgumentException("Can not filter indices; index " + index +
266-
" exists but there is also a remote cluster named: " + remoteClusterName);
267-
}
268-
String indexName = index.substring(i + 1);
269-
for (String clusterName : clusters) {
270-
perClusterIndices.computeIfAbsent(clusterName, k -> new ArrayList<>()).add(indexName);
271-
}
272-
} else {
273-
//Indices and aliases can be created with ":" in their names up to 6.last (although deprecated), and still be
274-
//around in 7.x. That's why we need to be lenient here and treat the index as local although it contains ":".
275-
//It will be possible to remove such leniency and assume that no local indices contain ":" only from 8.0 on.
276-
perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index);
256+
String indexName = index.substring(i + 1);
257+
for (String clusterName : clusters) {
258+
perClusterIndices.computeIfAbsent(clusterName, k -> new ArrayList<>()).add(indexName);
277259
}
278260
} else {
279261
perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index);

server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ boolean isRemoteNodeConnected(final String remoteCluster, final DiscoveryNode no
288288
return remoteClusters.get(remoteCluster).isNodeConnected(node);
289289
}
290290

291-
public Map<String, OriginalIndices> groupIndices(IndicesOptions indicesOptions, String[] indices, Predicate<String> indexExists) {
291+
public Map<String, OriginalIndices> groupIndices(IndicesOptions indicesOptions, String[] indices) {
292292
Map<String, OriginalIndices> originalIndicesMap = new HashMap<>();
293293
if (isCrossClusterSearchEnabled()) {
294-
final Map<String, List<String>> groupedIndices = groupClusterIndices(getRemoteClusterNames(), indices, indexExists);
294+
final Map<String, List<String>> groupedIndices = groupClusterIndices(getRemoteClusterNames(), indices);
295295
if (groupedIndices.isEmpty()) {
296296
//search on _all in the local cluster if neither local indices nor remote indices were specified
297297
originalIndicesMap.put(LOCAL_CLUSTER_GROUP_KEY, new OriginalIndices(Strings.EMPTY_ARRAY, indicesOptions));

server/src/test/java/org/elasticsearch/cluster/metadata/ClusterNameExpressionResolverTests.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.cluster.metadata;
2121

2222
import org.elasticsearch.test.ESTestCase;
23+
import org.elasticsearch.transport.NoSuchRemoteClusterException;
2324

2425
import java.util.Arrays;
2526
import java.util.HashSet;
@@ -43,8 +44,8 @@ public void testExactMatch() {
4344
}
4445

4546
public void testNoWildCardNoMatch() {
46-
List<String> clusters = clusterNameResolver.resolveClusterNames(remoteClusters, "totallyDifferent2");
47-
assertTrue(clusters.isEmpty());
47+
expectThrows(NoSuchRemoteClusterException.class,
48+
() -> clusterNameResolver.resolveClusterNames(remoteClusters, "totallyDifferent2"));
4849
}
4950

5051
public void testWildCardNoMatch() {

server/src/test/java/org/elasticsearch/transport/RemoteClusterServiceTests.java

+15-22
Original file line numberDiff line numberDiff line change
@@ -217,22 +217,21 @@ public void testGroupClusterIndices() throws IOException {
217217
assertTrue(service.isRemoteClusterRegistered("cluster_2"));
218218
assertFalse(service.isRemoteClusterRegistered("foo"));
219219
Map<String, List<String>> perClusterIndices = service.groupClusterIndices(service.getRemoteClusterNames(),
220-
new String[]{"foo:bar", "cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo",
221-
"cluster*:baz", "*:boo", "no*match:boo"},
222-
i -> false);
220+
new String[]{"cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo", "cluster*:baz",
221+
"*:boo"});
223222
List<String> localIndices = perClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY);
224223
assertNotNull(localIndices);
225-
assertEquals(Arrays.asList("foo:bar", "foo", "no*match:boo"), localIndices);
224+
assertEquals("foo", localIndices.get(0));
226225
assertEquals(2, perClusterIndices.size());
227226
assertEquals(Arrays.asList("bar", "test", "baz", "boo"), perClusterIndices.get("cluster_1"));
228227
assertEquals(Arrays.asList("foo:bar", "foo*", "baz", "boo"), perClusterIndices.get("cluster_2"));
229228

230-
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () ->
231-
service.groupClusterIndices(service.getRemoteClusterNames(), new String[]{"foo:bar", "cluster_1:bar",
232-
"cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo"}, "cluster_1:bar"::equals));
229+
expectThrows(NoSuchRemoteClusterException.class, () -> service.groupClusterIndices(service.getRemoteClusterNames(),
230+
new String[]{"foo:bar", "cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo"}));
233231

234-
assertEquals("Can not filter indices; index cluster_1:bar exists but there is also a remote cluster named:" +
235-
" cluster_1", iae.getMessage());
232+
expectThrows(NoSuchRemoteClusterException.class, () ->
233+
service.groupClusterIndices(service.getRemoteClusterNames(), new String[]{"cluster_1:bar",
234+
"cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "does_not_exist:*"}));
236235
}
237236
}
238237
}
@@ -264,34 +263,28 @@ public void testGroupIndices() throws IOException {
264263
assertFalse(service.isRemoteClusterRegistered("foo"));
265264
{
266265
Map<String, OriginalIndices> perClusterIndices = service.groupIndices(IndicesOptions.LENIENT_EXPAND_OPEN,
267-
new String[]{"foo:bar", "cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo",
268-
"cluster*:baz", "*:boo", "no*match:boo"},
269-
i -> false);
266+
new String[]{"cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo", "cluster*:baz",
267+
"*:boo"});
270268
assertEquals(3, perClusterIndices.size());
271-
assertArrayEquals(new String[]{"foo:bar", "foo", "no*match:boo"},
269+
assertArrayEquals(new String[]{"foo"},
272270
perClusterIndices.get(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).indices());
273271
assertArrayEquals(new String[]{"bar", "test", "baz", "boo"}, perClusterIndices.get("cluster_1").indices());
274272
assertArrayEquals(new String[]{"foo:bar", "foo*", "baz", "boo"}, perClusterIndices.get("cluster_2").indices());
275273
}
276274
{
277-
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () ->
278-
service.groupClusterIndices(service.getRemoteClusterNames(), new String[]{"foo:bar", "cluster_1:bar",
279-
"cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo"}, "cluster_1:bar"::equals));
280-
assertEquals("Can not filter indices; index cluster_1:bar exists but there is also a remote cluster named:" +
281-
" cluster_1", iae.getMessage());
275+
expectThrows(NoSuchRemoteClusterException.class, () -> service.groupClusterIndices(service.getRemoteClusterNames(),
276+
new String[]{"foo:bar", "cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo"}));
282277
}
283278
{
284279
Map<String, OriginalIndices> perClusterIndices = service.groupIndices(IndicesOptions.LENIENT_EXPAND_OPEN,
285-
new String[]{"cluster_1:bar", "cluster_2:foo*"},
286-
i -> false);
280+
new String[]{"cluster_1:bar", "cluster_2:foo*"});
287281
assertEquals(2, perClusterIndices.size());
288282
assertArrayEquals(new String[]{"bar"}, perClusterIndices.get("cluster_1").indices());
289283
assertArrayEquals(new String[]{"foo*"}, perClusterIndices.get("cluster_2").indices());
290284
}
291285
{
292286
Map<String, OriginalIndices> perClusterIndices = service.groupIndices(IndicesOptions.LENIENT_EXPAND_OPEN,
293-
Strings.EMPTY_ARRAY,
294-
i -> false);
287+
Strings.EMPTY_ARRAY);
295288
assertEquals(1, perClusterIndices.size());
296289
assertArrayEquals(Strings.EMPTY_ARRAY, perClusterIndices.get(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).indices());
297290
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ protected void updateRemoteCluster(String clusterAlias, List<String> addresses,
449449
}
450450

451451
ResolvedIndices splitLocalAndRemoteIndexNames(String... indices) {
452-
final Map<String, List<String>> map = super.groupClusterIndices(clusters, indices, exists -> false);
452+
final Map<String, List<String>> map = super.groupClusterIndices(clusters, indices);
453453
final List<String> local = map.remove(LOCAL_CLUSTER_GROUP_KEY);
454454
final List<String> remote = map.entrySet().stream()
455455
.flatMap(e -> e.getValue().stream().map(v -> e.getKey() + REMOTE_CLUSTER_INDEX_SEPARATOR + v))

0 commit comments

Comments
 (0)