1
0
Эх сурвалжийг харах

Make all remote index name parsing go through RemoteClusterAware (#113501) (#113653)

* Make all remote index name parsing go through RemoteClusterAware
Stanislav Malyshev 1 жил өмнө
parent
commit
81891410f0
18 өөрчлөгдсөн 105 нэмэгдсэн , 112 устгасан
  1. 7 19
      modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
  2. 3 14
      modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexValidator.java
  3. 2 9
      server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java
  4. 3 6
      server/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java
  5. 3 9
      server/src/main/java/org/elasticsearch/action/search/TransportSearchHelper.java
  6. 3 2
      server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
  7. 4 6
      server/src/main/java/org/elasticsearch/index/query/SearchIndexNameMatcher.java
  8. 4 8
      server/src/main/java/org/elasticsearch/search/profile/SearchProfileResults.java
  9. 49 8
      server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java
  10. 1 1
      server/src/test/java/org/elasticsearch/transport/RemoteClusterAwareTests.java
  11. 3 5
      test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java
  12. 2 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/license/RemoteClusterLicenseChecker.java
  13. 4 6
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/util/StringUtils.java
  14. 4 2
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/TableIdentifier.java
  15. 4 2
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/TableIdentifier.java
  16. 4 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java
  17. 3 5
      x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java
  18. 2 2
      x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/interceptor/SearchRequestCacheDisablingInterceptor.java

+ 7 - 19
modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java

@@ -108,7 +108,6 @@ import org.elasticsearch.xcontent.XContentType;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -238,29 +237,21 @@ public class PainlessExecuteAction {
                     return new Tuple<>(null, null);
                 }
                 String trimmed = indexExpression.trim();
-                String sep = String.valueOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR);
-                if (trimmed.startsWith(sep) || trimmed.endsWith(sep)) {
-                    throw new IllegalArgumentException(
-                        "Unable to parse one single valid index name from the provided index: [" + indexExpression + "]"
-                    );
-                }
-
+                String[] parts = RemoteClusterAware.splitIndexName(trimmed);
                 // The parser here needs to ensure that the indexExpression is not of the form "remote1:blogs,remote2:blogs"
                 // because (1) only a single index is allowed for Painless Execute and
                 // (2) if this method returns Tuple("remote1", "blogs,remote2:blogs") that will not fail with "index not found".
                 // Instead, it will fail with the inaccurate and confusing error message:
                 // "Cross-cluster calls are not supported in this context but remote indices were requested: [blogs,remote1:blogs]"
                 // which comes later out of the IndexNameExpressionResolver pathway this code uses.
-                String[] parts = indexExpression.split(sep, 2);
-                if (parts.length == 1) {
-                    return new Tuple<>(null, parts[0]);
-                } else if (parts.length == 2 && parts[1].contains(sep) == false) {
-                    return new Tuple<>(parts[0], parts[1]);
-                } else {
+                if ((parts[0] != null && parts[1].isEmpty())
+                    || parts[1].contains(String.valueOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR))) {
                     throw new IllegalArgumentException(
                         "Unable to parse one single valid index name from the provided index: [" + indexExpression + "]"
                     );
                 }
+
+                return new Tuple<>(parts[0], parts[1]);
             }
 
             public String getClusterAlias() {
@@ -556,8 +547,8 @@ public class PainlessExecuteAction {
         // Visible for testing
         static void removeClusterAliasFromIndexExpression(Request request) {
             if (request.index() != null) {
-                String[] split = request.index().split(String.valueOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR));
-                if (split.length > 1) {
+                String[] split = RemoteClusterAware.splitIndexName(request.index());
+                if (split[0] != null) {
                     /*
                      * if the cluster alias is null and the index field has a clusterAlias (clusterAlias:index notation)
                      * that means this is executing on a remote cluster (it was forwarded by the querying cluster).
@@ -565,9 +556,6 @@ public class PainlessExecuteAction {
                      * We need to strip off the clusterAlias from the index before executing the script locally,
                      * so it will resolve to a local index
                      */
-                    assert split.length == 2
-                        : "If the index contains the REMOTE_CLUSTER_INDEX_SEPARATOR it should have only two parts but it has "
-                            + Arrays.toString(split);
                     request.index(split[1]);
                 }
             }

+ 3 - 14
modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexValidator.java

@@ -156,21 +156,10 @@ public class ReindexValidator {
     }
 
     private static SearchRequest skipRemoteIndexNames(SearchRequest source) {
-        return new SearchRequest(source).indices(
-            Arrays.stream(source.indices()).filter(name -> isRemoteExpression(name) == false).toArray(String[]::new)
-        );
-    }
-
-    private static boolean isRemoteExpression(String expression) {
         // An index expression that references a remote cluster uses ":" to separate the cluster-alias from the index portion of the
         // expression, e.g., cluster0:index-name
-        // in the same time date-math `expression` can also contain ':' symbol inside its name
-        // to distinguish between those two, given `expression` is pre-evaluated using date-math resolver
-        // after evaluation date-math `expression` should not contain ':' symbol
-        // otherwise if `expression` is legit remote name, ':' symbol remains
-        // NOTE: index expressions can be prefixed with "-", which will not be parsed by resolveDateMathExpression,
-        // but in this particular case it doesn't seem to be relevant.
-        return IndexNameExpressionResolver.resolveDateMathExpression(expression)
-            .contains(String.valueOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR));
+        return new SearchRequest(source).indices(
+            Arrays.stream(source.indices()).filter(name -> RemoteClusterAware.isRemoteIndexName(name) == false).toArray(String[]::new)
+        );
     }
 }

+ 2 - 9
server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java

@@ -15,13 +15,12 @@ import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.action.ValidateActions;
 import org.elasticsearch.action.support.IndicesOptions;
-import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.tasks.CancellableTask;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.tasks.TaskId;
-import org.elasticsearch.transport.RemoteClusterService;
+import org.elasticsearch.transport.RemoteClusterAware;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -166,13 +165,7 @@ public class ResolveClusterActionRequest extends ActionRequest implements Indice
 
     boolean localIndicesPresent(String[] indices) {
         for (String index : indices) {
-            // ensure that `index` is a remote name and not a date math expression which includes ':' symbol
-            // since date math expression after evaluation should not contain ':' symbol
-            // NOTE: index expressions can be prefixed with "-" for index exclusion, which will not be parsed by resolveDateMathExpression
-            String indexExpression = IndexNameExpressionResolver.resolveDateMathExpression(
-                index.charAt(0) == '-' ? index.substring(1) : index
-            );
-            if (indexExpression.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR) < 0) {
+            if (RemoteClusterAware.isRemoteIndexName(index) == false) {
                 return true;
             }
         }

+ 3 - 6
server/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java

@@ -141,12 +141,9 @@ public class ShardSearchFailure extends ShardOperationFailedException {
                 if (SHARD_FIELD.equals(currentFieldName)) {
                     shardId = parser.intValue();
                 } else if (INDEX_FIELD.equals(currentFieldName)) {
-                    indexName = parser.text();
-                    int indexOf = indexName.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR);
-                    if (indexOf > 0) {
-                        clusterAlias = indexName.substring(0, indexOf);
-                        indexName = indexName.substring(indexOf + 1);
-                    }
+                    String[] split = RemoteClusterAware.splitIndexName(parser.text());
+                    clusterAlias = split[0];
+                    indexName = split[1];
                 } else if (NODE_FIELD.equals(currentFieldName)) {
                     nodeId = parser.text();
                 } else {

+ 3 - 9
server/src/main/java/org/elasticsearch/action/search/TransportSearchHelper.java

@@ -110,15 +110,9 @@ public final class TransportSearchHelper {
 
     private static SearchContextIdForNode innerReadSearchContextIdForNode(String contextUUID, StreamInput in) throws IOException {
         long id = in.readLong();
-        String target = in.readString();
-        String clusterAlias;
-        final int index = target.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR);
-        if (index == -1) {
-            clusterAlias = null;
-        } else {
-            clusterAlias = target.substring(0, index);
-            target = target.substring(index + 1);
-        }
+        String[] split = RemoteClusterAware.splitIndexName(in.readString());
+        String clusterAlias = split[0];
+        String target = split[1];
         return new SearchContextIdForNode(clusterAlias, target, new ShardSearchContextId(contextUUID, id));
     }
 

+ 3 - 2
server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java

@@ -37,6 +37,7 @@ import org.elasticsearch.indices.IndexClosedException;
 import org.elasticsearch.indices.InvalidIndexNameException;
 import org.elasticsearch.indices.SystemIndices;
 import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel;
+import org.elasticsearch.transport.RemoteClusterAware;
 
 import java.time.Instant;
 import java.time.ZoneId;
@@ -1753,7 +1754,7 @@ public class IndexNameExpressionResolver {
                 return;
             }
             for (String index : indexExpressions) {
-                if (index.contains(":")) {
+                if (RemoteClusterAware.isRemoteIndexName(index)) {
                     failOnRemoteIndicesNotIgnoringUnavailable(indexExpressions);
                 }
             }
@@ -1762,7 +1763,7 @@ public class IndexNameExpressionResolver {
         private static void failOnRemoteIndicesNotIgnoringUnavailable(List<String> indexExpressions) {
             List<String> crossClusterIndices = new ArrayList<>();
             for (String index : indexExpressions) {
-                if (index.contains(":")) {
+                if (RemoteClusterAware.isRemoteIndexName(index)) {
                     crossClusterIndices.add(index);
                 }
             }

+ 4 - 6
server/src/main/java/org/elasticsearch/index/query/SearchIndexNameMatcher.java

@@ -53,14 +53,12 @@ public class SearchIndexNameMatcher implements Predicate<String> {
      *  the separator ':', and must match on both the cluster alias and index name.
      */
     public boolean test(String pattern) {
-        int separatorIndex = pattern.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR);
-        if (separatorIndex < 0) {
+        String[] splitIndex = RemoteClusterAware.splitIndexName(pattern);
+
+        if (splitIndex[0] == null) {
             return clusterAlias == null && matchesIndex(pattern);
         } else {
-            String clusterPattern = pattern.substring(0, separatorIndex);
-            String indexPattern = pattern.substring(separatorIndex + 1);
-
-            return Regex.simpleMatch(clusterPattern, clusterAlias) && matchesIndex(indexPattern);
+            return Regex.simpleMatch(splitIndex[0], clusterAlias) && matchesIndex(splitIndex[1]);
         }
     }
 

+ 4 - 8
server/src/main/java/org/elasticsearch/search/profile/SearchProfileResults.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.Writeable;
 import org.elasticsearch.core.Nullable;
+import org.elasticsearch.transport.RemoteClusterAware;
 import org.elasticsearch.xcontent.ToXContentFragment;
 import org.elasticsearch.xcontent.XContentBuilder;
 
@@ -143,15 +144,10 @@ public final class SearchProfileResults implements Writeable, ToXContentFragment
         Matcher m = SHARD_ID_DECOMPOSITION.matcher(compositeId);
         if (m.find()) {
             String nodeId = m.group(1);
-            String indexName = m.group(2);
+            String[] tokens = RemoteClusterAware.splitIndexName(m.group(2));
+            String cluster = tokens[0];
+            String indexName = tokens[1];
             int shardId = Integer.parseInt(m.group(3));
-            String cluster = null;
-            if (indexName.contains(":")) {
-                // index names and cluster names cannot contain a ':', so this split should be accurate
-                String[] tokens = indexName.split(":", 2);
-                cluster = tokens[0];
-                indexName = tokens[1];
-            }
             return new ShardProfileId(nodeId, indexName, shardId, cluster);
         } else {
             assert false : "Unable to match input against expected pattern of [nodeId][indexName][shardId]. Input: " + compositeId;

+ 49 - 8
server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java

@@ -53,6 +53,45 @@ public abstract class RemoteClusterAware {
         return RemoteConnectionStrategy.getRemoteClusters(settings);
     }
 
+    /**
+     * Check whether the index expression represents remote index or not.
+     * The index name is assumed to be individual index (no commas) but can contain `-`, wildcards,
+     * datemath, remote cluster name and any other syntax permissible in index expression component.
+     */
+    public static boolean isRemoteIndexName(String indexExpression) {
+        if (indexExpression.isEmpty() || indexExpression.charAt(0) == '<' || indexExpression.startsWith("-<")) {
+            // This is date math, but even if it is not, the remote can't start with '<'.
+            // Thus, whatever it is, this is definitely not a remote index.
+            return false;
+        }
+        // Note remote index name also can not start with ':'
+        return indexExpression.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR) > 0;
+    }
+
+    /**
+     * Split the index name into remote cluster alias and index name.
+     * The index expression is assumed to be individual index (no commas) but can contain `-`, wildcards,
+     * datemath, remote cluster name and any other syntax permissible in index expression component.
+     * There's no guarantee the components actually represent existing remote cluster or index, only
+     * rudimentary checks are done on the syntax.
+     */
+    public static String[] splitIndexName(String indexExpression) {
+        if (indexExpression.isEmpty() || indexExpression.charAt(0) == '<' || indexExpression.startsWith("-<")) {
+            // This is date math, but even if it is not, the remote can't start with '<'.
+            // Thus, whatever it is, this is definitely not a remote index.
+            return new String[] { null, indexExpression };
+        }
+        int i = indexExpression.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR);
+        if (i == 0) {
+            throw new IllegalArgumentException("index name [" + indexExpression + "] is invalid because the remote part is empty");
+        }
+        if (i < 0) {
+            return new String[] { null, indexExpression };
+        } else {
+            return new String[] { indexExpression.substring(0, i), indexExpression.substring(i + 1) };
+        }
+    }
+
     /**
      * Groups indices per cluster by splitting remote cluster-alias, index-name pairs on {@link #REMOTE_CLUSTER_INDEX_SEPARATOR}. All
      * indices per cluster are collected as a list in the returned map keyed by the cluster alias. Local indices are grouped under
@@ -77,18 +116,20 @@ public abstract class RemoteClusterAware {
         for (String index : requestIndices) {
             // ensure that `index` is a remote name and not a datemath expression which includes ':' symbol
             // Remote names can not start with '<' so we are assuming that if the first character is '<' then it is a datemath expression.
-            boolean isDateMathExpression = (index.charAt(0) == '<' || index.startsWith("-<"));
-            int i = index.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR);
-            if (isDateMathExpression == false && i >= 0) {
+            String[] split = splitIndexName(index);
+            if (split[0] != null) {
                 if (isRemoteClusterClientEnabled == false) {
                     assert remoteClusterNames.isEmpty() : remoteClusterNames;
                     throw new IllegalArgumentException("node [" + nodeName + "] does not have the remote cluster client role enabled");
                 }
-                int startIdx = index.charAt(0) == '-' ? 1 : 0;
-                String remoteClusterName = index.substring(startIdx, i);
-                List<String> clusters = ClusterNameExpressionResolver.resolveClusterNames(remoteClusterNames, remoteClusterName);
-                String indexName = index.substring(i + 1);
-                if (startIdx == 1) {
+                String remoteClusterName = split[0];
+                String indexName = split[1];
+                boolean isNegative = remoteClusterName.startsWith("-");
+                List<String> clusters = ClusterNameExpressionResolver.resolveClusterNames(
+                    remoteClusterNames,
+                    isNegative ? remoteClusterName.substring(1) : remoteClusterName
+                );
+                if (isNegative) {
                     if (indexName.equals("*") == false) {
                         throw new IllegalArgumentException(
                             Strings.format(

+ 1 - 1
server/src/test/java/org/elasticsearch/transport/RemoteClusterAwareTests.java

@@ -130,7 +130,7 @@ public class RemoteClusterAwareTests extends ESTestCase {
         RemoteClusterAwareTest remoteClusterAware = new RemoteClusterAwareTest();
         Set<String> remoteClusterNames = Set.of("cluster1", "cluster2", "some-cluster3");
 
-        mustThrowException(new String[] { ":foo" }, NoSuchRemoteClusterException.class, "no such remote cluster");
+        mustThrowException(new String[] { ":foo" }, IllegalArgumentException.class, "is invalid because the remote part is empty");
         mustThrowException(new String[] { "notacluster:foo" }, NoSuchRemoteClusterException.class, "no such remote cluster");
         // Cluster wildcard exclusion requires :*
         mustThrowException(

+ 3 - 5
test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java

@@ -851,11 +851,9 @@ public enum SearchResponseUtils {
         String index = get(SearchHit.Fields._INDEX, values, null);
         String clusterAlias = null;
         if (index != null) {
-            int indexOf = index.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR);
-            if (indexOf > 0) {
-                clusterAlias = index.substring(0, indexOf);
-                index = index.substring(indexOf + 1);
-            }
+            String[] split = RemoteClusterAware.splitIndexName(index);
+            clusterAlias = split[0];
+            index = split[1];
         }
         ShardId shardId = get(SearchHit.Fields._SHARD, values, null);
         String nodeId = get(SearchHit.Fields._NODE, values, null);

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/license/RemoteClusterLicenseChecker.java

@@ -239,7 +239,7 @@ public final class RemoteClusterLicenseChecker {
      * @return true if the collection of indices contains a remote index, otherwise false
      */
     public static boolean isRemoteIndex(final String index) {
-        return index.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR) != -1;
+        return RemoteClusterAware.isRemoteIndexName(index);
     }
 
     /**
@@ -275,7 +275,7 @@ public final class RemoteClusterLicenseChecker {
     public static List<String> remoteClusterAliases(final Set<String> remoteClusters, final List<String> indices) {
         return indices.stream()
             .filter(RemoteClusterLicenseChecker::isRemoteIndex)
-            .map(index -> index.substring(0, index.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR)))
+            .map(index -> RemoteClusterAware.splitIndexName(index)[0])
             .distinct()
             .flatMap(clusterExpression -> ClusterNameExpressionResolver.resolveClusterNames(remoteClusters, clusterExpression).stream())
             .distinct()

+ 4 - 6
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/util/StringUtils.java

@@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.network.InetAddresses;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.transport.RemoteClusterAware;
 import org.elasticsearch.xcontent.ToXContent;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentFactory;
@@ -27,7 +28,6 @@ import java.util.Locale;
 import java.util.StringJoiner;
 
 import static java.util.stream.Collectors.toList;
-import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
 import static org.elasticsearch.transport.RemoteClusterAware.buildRemoteIndexName;
 import static org.elasticsearch.xpack.esql.core.util.NumericUtils.isUnsignedLong;
 
@@ -378,10 +378,8 @@ public final class StringUtils {
     }
 
     public static Tuple<String, String> splitQualifiedIndex(String indexName) {
-        int separatorOffset = indexName.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR);
-        return separatorOffset > 0
-            ? Tuple.tuple(indexName.substring(0, separatorOffset), indexName.substring(separatorOffset + 1))
-            : Tuple.tuple(null, indexName);
+        String[] split = RemoteClusterAware.splitIndexName(indexName);
+        return Tuple.tuple(split[0], split[1]);
     }
 
     public static String qualifyAndJoinIndices(String cluster, String[] indices) {
@@ -393,7 +391,7 @@ public final class StringUtils {
     }
 
     public static boolean isQualified(String indexWildcard) {
-        return indexWildcard.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR) > 0;
+        return RemoteClusterAware.isRemoteIndexName(indexWildcard);
     }
 
     public static boolean isInteger(String value) {

+ 4 - 2
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/TableIdentifier.java

@@ -10,6 +10,8 @@ import org.elasticsearch.xpack.esql.core.tree.Source;
 
 import java.util.Objects;
 
+import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
+
 public class TableIdentifier {
 
     private final Source source;
@@ -55,7 +57,7 @@ public class TableIdentifier {
     }
 
     public String qualifiedIndex() {
-        return cluster != null ? cluster + ":" + index : index;
+        return cluster != null ? cluster + REMOTE_CLUSTER_INDEX_SEPARATOR + index : index;
     }
 
     @Override
@@ -63,7 +65,7 @@ public class TableIdentifier {
         StringBuilder builder = new StringBuilder();
         if (cluster != null) {
             builder.append(cluster);
-            builder.append(":");
+            builder.append(REMOTE_CLUSTER_INDEX_SEPARATOR);
         }
         builder.append(index);
         return builder.toString();

+ 4 - 2
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/TableIdentifier.java

@@ -10,6 +10,8 @@ import org.elasticsearch.xpack.ql.tree.Source;
 
 import java.util.Objects;
 
+import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
+
 public class TableIdentifier {
 
     private final Source source;
@@ -55,7 +57,7 @@ public class TableIdentifier {
     }
 
     public String qualifiedIndex() {
-        return cluster != null ? cluster + ":" + index : index;
+        return cluster != null ? cluster + REMOTE_CLUSTER_INDEX_SEPARATOR + index : index;
     }
 
     @Override
@@ -63,7 +65,7 @@ public class TableIdentifier {
         StringBuilder builder = new StringBuilder();
         if (cluster != null) {
             builder.append(cluster);
-            builder.append(":");
+            builder.append(REMOTE_CLUSTER_INDEX_SEPARATOR);
         }
         builder.append(index);
         return builder.toString();

+ 4 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java

@@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.network.InetAddresses;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.transport.RemoteClusterAware;
 import org.elasticsearch.xcontent.ToXContent;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentFactory;
@@ -27,7 +28,6 @@ import java.util.Locale;
 import java.util.StringJoiner;
 
 import static java.util.stream.Collectors.toList;
-import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
 import static org.elasticsearch.transport.RemoteClusterAware.buildRemoteIndexName;
 import static org.elasticsearch.xpack.ql.util.NumericUtils.isUnsignedLong;
 
@@ -375,10 +375,8 @@ public final class StringUtils {
     }
 
     public static Tuple<String, String> splitQualifiedIndex(String indexName) {
-        int separatorOffset = indexName.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR);
-        return separatorOffset > 0
-            ? Tuple.tuple(indexName.substring(0, separatorOffset), indexName.substring(separatorOffset + 1))
-            : Tuple.tuple(null, indexName);
+        String[] split = RemoteClusterAware.splitIndexName(indexName);
+        return Tuple.tuple(split[0], split[1]);
     }
 
     public static String qualifyAndJoinIndices(String cluster, String[] indices) {
@@ -390,6 +388,6 @@ public final class StringUtils {
     }
 
     public static boolean isQualified(String indexWildcard) {
-        return indexWildcard.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR) > 0;
+        return RemoteClusterAware.isRemoteIndexName(indexWildcard);
     }
 }

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

@@ -169,11 +169,9 @@ class IndicesAndAliasesResolver {
             // and no remote clusters are configured that match it
             if (split.getLocal().isEmpty() && split.getRemote().isEmpty()) {
                 for (String indexExpression : indices) {
-                    String[] clusterAndIndex = indexExpression.split(":", 2);
-                    if (clusterAndIndex.length == 2) {
-                        if (clusterAndIndex[0].contains("*")) {
-                            throw new NoSuchRemoteClusterException(clusterAndIndex[0]);
-                        }
+                    String[] clusterAndIndex = RemoteClusterAware.splitIndexName(indexExpression);
+                    if (clusterAndIndex[0] != null && clusterAndIndex[0].contains("*")) {
+                        throw new NoSuchRemoteClusterException(clusterAndIndex[0]);
                     }
                 }
             }

+ 2 - 2
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/interceptor/SearchRequestCacheDisablingInterceptor.java

@@ -11,6 +11,7 @@ import org.elasticsearch.action.search.SearchRequest;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.license.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
+import org.elasticsearch.transport.RemoteClusterAware;
 import org.elasticsearch.transport.TransportActionProxy;
 import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
 import org.elasticsearch.xpack.core.security.authz.AuthorizationServiceField;
@@ -18,7 +19,6 @@ import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessCo
 
 import java.util.Arrays;
 
-import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
 import static org.elasticsearch.xpack.core.security.SecurityField.DOCUMENT_LEVEL_SECURITY_FEATURE;
 import static org.elasticsearch.xpack.core.security.SecurityField.FIELD_LEVEL_SECURITY_FEATURE;
 
@@ -55,6 +55,6 @@ public class SearchRequestCacheDisablingInterceptor implements RequestIntercepto
 
     // package private for test
     static boolean hasRemoteIndices(SearchRequest request) {
-        return Arrays.stream(request.indices()).anyMatch(name -> name.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR) >= 0);
+        return Arrays.stream(request.indices()).anyMatch(RemoteClusterAware::isRemoteIndexName);
     }
 }