From b98881369270d6fe764a341c7ef0df24b695665b Mon Sep 17 00:00:00 2001 From: Michael Peterson Date: Thu, 9 Jan 2025 10:02:05 -0500 Subject: [PATCH] Core changes ported over from the earlier PR branch. No major changes to TransportResolveClusterAction --- .../indices/cluster/ResolveClusterIT.java | 89 ++++++++++++++++++- .../org/elasticsearch/TransportVersions.java | 1 + .../resolve/ResolveClusterActionRequest.java | 62 ++++++++++--- .../indices/resolve/ResolveClusterInfo.java | 20 ++--- .../TransportResolveClusterAction.java | 9 +- .../indices/RestResolveClusterAction.java | 14 ++- .../transport/RemoteClusterService.java | 29 +++++- .../ResolveClusterActionRequestTests.java | 37 ++++---- .../ResolveClusterActionResponseTests.java | 3 +- .../TransportResolveClusterActionTests.java | 8 +- 10 files changed, 217 insertions(+), 55 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/cluster/ResolveClusterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/cluster/ResolveClusterIT.java index 4bdc5d63f4a2f..cb4ff4acd0411 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/cluster/ResolveClusterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/cluster/ResolveClusterIT.java @@ -16,6 +16,7 @@ import org.elasticsearch.action.admin.indices.resolve.ResolveClusterActionResponse; import org.elasticsearch.action.admin.indices.resolve.ResolveClusterInfo; import org.elasticsearch.action.admin.indices.resolve.TransportResolveClusterAction; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.Strings; @@ -405,6 +406,52 @@ public void testClusterResolveWithIndices() throws IOException { } } + // corresponds to the GET _resolve/cluster endpoint with no index expression specified + public void testClusterResolveWithNoIndexExpression() throws IOException { + Map testClusterInfo = setupThreeClusters(false); + boolean skipUnavailable1 = (Boolean) testClusterInfo.get("remote1.skip_unavailable"); + boolean skipUnavailable2 = true; + + { + String[] noIndexSpecified = new String[0]; + boolean clusterInfoOnly = true; + boolean runningOnQueryingCluster = true; + ResolveClusterActionRequest request = new ResolveClusterActionRequest( + noIndexSpecified, + IndicesOptions.DEFAULT, + clusterInfoOnly, + runningOnQueryingCluster + ); + + ActionFuture future = client(LOCAL_CLUSTER).admin() + .indices() + .execute(TransportResolveClusterAction.TYPE, request); + ResolveClusterActionResponse response = future.actionGet(30, TimeUnit.SECONDS); + assertNotNull(response); + + Map clusterInfo = response.getResolveClusterInfo(); + assertEquals(2, clusterInfo.size()); + + // only remote clusters should be present (not local) + Set expectedClusterNames = Set.of(REMOTE_CLUSTER_1, REMOTE_CLUSTER_2); + assertThat(clusterInfo.keySet(), equalTo(expectedClusterNames)); + + ResolveClusterInfo remote1 = clusterInfo.get(REMOTE_CLUSTER_1); + assertThat(remote1.isConnected(), equalTo(true)); + assertThat(remote1.getSkipUnavailable(), equalTo(skipUnavailable1)); + assertThat(remote1.getMatchingIndices(), equalTo(null)); // should not be set + assertNotNull(remote1.getBuild().version()); + assertNull(remote1.getError()); + + ResolveClusterInfo remote2 = clusterInfo.get(REMOTE_CLUSTER_2); + assertThat(remote2.isConnected(), equalTo(true)); + assertThat(remote2.getSkipUnavailable(), equalTo(skipUnavailable2)); + assertThat(remote2.getMatchingIndices(), equalTo(null)); // should not be set + assertNotNull(remote2.getBuild().version()); + assertNull(remote2.getError()); + } + } + public void testClusterResolveWithMatchingAliases() throws IOException { Map testClusterInfo = setupThreeClusters(true); String localAlias = (String) testClusterInfo.get("local.alias"); @@ -615,9 +662,49 @@ public void testClusterResolveDisconnectedAndErrorScenarios() throws Exception { assertNotNull(local.getBuild().version()); assertNull(local.getError()); } + + // cluster1 was stopped/disconnected, so it should return a connected:false response when querying with no index expression, + // corresponding to GET _resolve/cluster endpoint + { + String[] noIndexSpecified = new String[0]; + boolean clusterInfoOnly = true; + boolean runningOnQueryingCluster = true; + ResolveClusterActionRequest request = new ResolveClusterActionRequest( + noIndexSpecified, + IndicesOptions.DEFAULT, + clusterInfoOnly, + runningOnQueryingCluster + ); + + ActionFuture future = client(LOCAL_CLUSTER).admin() + .indices() + .execute(TransportResolveClusterAction.TYPE, request); + ResolveClusterActionResponse response = future.actionGet(30, TimeUnit.SECONDS); + assertNotNull(response); + + Map clusterInfo = response.getResolveClusterInfo(); + assertEquals(2, clusterInfo.size()); + // local cluster is not present when querying without an index expression + Set expectedClusterNames = Set.of(REMOTE_CLUSTER_1, REMOTE_CLUSTER_2); + assertThat(clusterInfo.keySet(), equalTo(expectedClusterNames)); + + ResolveClusterInfo remote1 = clusterInfo.get(REMOTE_CLUSTER_1); + assertThat(remote1.isConnected(), equalTo(false)); + assertThat(remote1.getSkipUnavailable(), equalTo(skipUnavailable1)); + assertNull(remote1.getMatchingIndices()); + assertNull(remote1.getBuild()); + assertNull(remote1.getError()); + + ResolveClusterInfo remote2 = clusterInfo.get(REMOTE_CLUSTER_2); + assertThat(remote2.isConnected(), equalTo(true)); + assertThat(remote2.getSkipUnavailable(), equalTo(skipUnavailable2)); + assertNull(remote2.getMatchingIndices()); // not present when no index expression specified + assertNotNull(remote2.getBuild().version()); + assertNull(remote2.getError()); + } } - private Map setupThreeClusters(boolean useAlias) throws IOException { + private Map setupThreeClusters(boolean useAlias) { String localAlias = randomAlphaOfLengthBetween(5, 25); String remoteAlias1 = randomAlphaOfLengthBetween(5, 25); String remoteAlias2 = randomAlphaOfLengthBetween(5, 25); diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index c5bb47ce1e4f7..361156ce6514f 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -155,6 +155,7 @@ static TransportVersion def(int id) { public static final TransportVersion TRACK_INDEX_FAILED_DUE_TO_VERSION_CONFLICT_METRIC = def(8_820_00_0); public static final TransportVersion REPLACE_FAILURE_STORE_OPTIONS_WITH_SELECTOR_SYNTAX = def(8_821_00_0); public static final TransportVersion ELASTIC_INFERENCE_SERVICE_UNIFIED_CHAT_COMPLETIONS_INTEGRATION = def(8_822_00_0); + public static final TransportVersion RESOLVE_CLUSTER_NO_INDEX_EXPRESSION = def(8_823_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java index ebc9b0fea1be4..7637960830ec2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequest.java @@ -14,7 +14,6 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.IndicesRequest; -import org.elasticsearch.action.ValidateActions; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; @@ -53,15 +52,25 @@ public class ResolveClusterActionRequest extends ActionRequest implements Indice private boolean localIndicesRequested = false; private IndicesOptions indicesOptions; + // true if the user did not provide any index expression - they only want cluster level info, not index matching + private boolean clusterInfoOnly; + // Whether this request is being processed on the primary ("local") cluster being queried or on a remote. + // This is needed when clusterInfoOnly=true since we need to know whether to list out all possible remotes + // on a node. (We don't want cross-cluster chaining on remotes that might be configured with their own remotes.) + private boolean isQueryingCluster; + public ResolveClusterActionRequest(String[] names) { - this(names, DEFAULT_INDICES_OPTIONS); + this(names, DEFAULT_INDICES_OPTIONS, false, true); + assert names != null && names.length > 0 : "One or more index expressions must be included with this constructor"; } @SuppressWarnings("this-escape") - public ResolveClusterActionRequest(String[] names, IndicesOptions indicesOptions) { + public ResolveClusterActionRequest(String[] names, IndicesOptions indicesOptions, boolean clusterInfoOnly, boolean queryingCluster) { this.names = names; this.localIndicesRequested = localIndicesPresent(names); this.indicesOptions = indicesOptions; + this.clusterInfoOnly = clusterInfoOnly; + this.isQueryingCluster = queryingCluster; } @SuppressWarnings("this-escape") @@ -73,6 +82,13 @@ public ResolveClusterActionRequest(StreamInput in) throws IOException { this.names = in.readStringArray(); this.indicesOptions = IndicesOptions.readIndicesOptions(in); this.localIndicesRequested = localIndicesPresent(names); + if (in.getTransportVersion().onOrAfter(TransportVersions.RESOLVE_CLUSTER_NO_INDEX_EXPRESSION)) { + this.clusterInfoOnly = in.readBoolean(); + this.isQueryingCluster = in.readBoolean(); + } else { + this.clusterInfoOnly = false; + this.isQueryingCluster = false; + } } @Override @@ -83,9 +99,13 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeStringArray(names); indicesOptions.writeIndicesOptions(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.RESOLVE_CLUSTER_NO_INDEX_EXPRESSION)) { + out.writeBoolean(clusterInfoOnly); + out.writeBoolean(isQueryingCluster); + } } - private String createVersionErrorMessage(TransportVersion versionFound) { + static String createVersionErrorMessage(TransportVersion versionFound) { return Strings.format( "%s %s but was %s", TRANSPORT_VERSION_ERROR_MESSAGE_PREFIX, @@ -96,11 +116,7 @@ private String createVersionErrorMessage(TransportVersion versionFound) { @Override public ActionRequestValidationException validate() { - ActionRequestValidationException validationException = null; - if (names == null || names.length == 0) { - validationException = ValidateActions.addValidationError("no index expressions specified", validationException); - } - return validationException; + return null; } @Override @@ -123,6 +139,14 @@ public String[] indices() { return names; } + public boolean clusterInfoOnly() { + return clusterInfoOnly; + } + + public boolean queryingCluster() { + return isQueryingCluster; + } + public boolean isLocalIndicesRequested() { return localIndicesRequested; } @@ -160,7 +184,11 @@ public Task createTask(long id, String type, String action, TaskId parentTaskId, return new CancellableTask(id, type, action, "", parentTaskId, headers) { @Override public String getDescription() { - return "resolve/cluster for " + Arrays.toString(indices()); + if (indices().length == 0) { + return "resolve/cluster"; + } else { + return "resolve/cluster for " + Arrays.toString(indices()); + } } }; } @@ -173,4 +201,18 @@ boolean localIndicesPresent(String[] indices) { } return false; } + + @Override + public String toString() { + return "ResolveClusterActionRequest{" + + "indices=" + + Arrays.toString(names) + + ", localIndicesRequested=" + + localIndicesRequested + + ", clusterInfoOnly=" + + clusterInfoOnly + + ", queryingCluster=" + + isQueryingCluster + + '}'; + } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterInfo.java b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterInfo.java index b539c87adc484..5c4a9c75f26d2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterInfo.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterInfo.java @@ -22,7 +22,7 @@ public class ResolveClusterInfo implements Writeable { private final boolean connected; private final Boolean skipUnavailable; // remote clusters don't know their setting, so they put null and querying cluster fills in - private final Boolean matchingIndices; // null means 'unknown' when not connected + private final Boolean matchingIndices; // null means no index expression requested by user or remote cluster was not connected private final Build build; private final String error; @@ -38,8 +38,14 @@ public ResolveClusterInfo(boolean connected, Boolean skipUnavailable, Boolean ma this(connected, skipUnavailable, matchingIndices, build, null); } - public ResolveClusterInfo(ResolveClusterInfo copyFrom, boolean skipUnavailable) { - this(copyFrom.isConnected(), skipUnavailable, copyFrom.getMatchingIndices(), copyFrom.getBuild(), copyFrom.getError()); + public ResolveClusterInfo(ResolveClusterInfo copyFrom, boolean skipUnavailable, boolean clusterInfoOnly) { + this( + copyFrom.isConnected(), + skipUnavailable, + clusterInfoOnly ? null : copyFrom.getMatchingIndices(), + copyFrom.getBuild(), + clusterInfoOnly ? null : copyFrom.getError() + ); } private ResolveClusterInfo(boolean connected, Boolean skipUnavailable, Boolean matchingIndices, Build build, String error) { @@ -48,7 +54,6 @@ private ResolveClusterInfo(boolean connected, Boolean skipUnavailable, Boolean m this.matchingIndices = matchingIndices; this.build = build; this.error = error; - assert error != null || matchingIndices != null || connected == false : "If matchingIndices is null, connected must be false"; } public ResolveClusterInfo(StreamInput in) throws IOException { @@ -67,12 +72,7 @@ public ResolveClusterInfo(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().before(TransportVersions.V_8_13_0)) { - throw new UnsupportedOperationException( - "ResolveClusterAction requires at least version " - + TransportVersions.V_8_13_0.toReleaseVersion() - + " but was " - + out.getTransportVersion().toReleaseVersion() - ); + throw new UnsupportedOperationException(ResolveClusterActionRequest.createVersionErrorMessage(out.getTransportVersion())); } out.writeBoolean(connected); out.writeOptionalBoolean(skipUnavailable); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterAction.java index 50dbaf33d2e4f..1346f43e47b36 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterAction.java @@ -142,7 +142,12 @@ protected void doExecuteForked(Task task, ResolveClusterActionRequest request, A searchCoordinationExecutor, RemoteClusterService.DisconnectedStrategy.FAIL_IF_DISCONNECTED ); - var remoteRequest = new ResolveClusterActionRequest(originalIndices.indices(), request.indicesOptions()); + var remoteRequest = new ResolveClusterActionRequest( + originalIndices.indices(), + request.indicesOptions(), + request.clusterInfoOnly(), + false + ); // allow cancellation requests to propagate to remote clusters remoteRequest.setParentTask(clusterService.localNode().getId(), task.getId()); @@ -155,7 +160,7 @@ public void onResponse(ResolveClusterActionResponse response) { } ResolveClusterInfo info = response.getResolveClusterInfo().get(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); if (info != null) { - clusterInfoMap.put(clusterAlias, new ResolveClusterInfo(info, skipUnavailable)); + clusterInfoMap.put(clusterAlias, new ResolveClusterInfo(info, skipUnavailable, request.clusterInfoOnly())); } if (resolveClusterTask.isCancelled()) { releaseResourcesOnCancel(clusterInfoMap); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveClusterAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveClusterAction.java index a4e8edc2bc801..e6e0fba1161e2 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveClusterAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestResolveClusterAction.java @@ -39,10 +39,20 @@ public List routes() { @Override protected BaseRestHandler.RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - String[] indexExpressions = Strings.splitStringByCommaToArray(request.param("name")); + String[] indexExpressions; + boolean clusterInfoOnly; + if (request.hasParam("name")) { + indexExpressions = Strings.splitStringByCommaToArray(request.param("name")); + clusterInfoOnly = false; + } else { + indexExpressions = new String[0]; + clusterInfoOnly = true; + } ResolveClusterActionRequest resolveRequest = new ResolveClusterActionRequest( indexExpressions, - IndicesOptions.fromRequest(request, ResolveIndexAction.Request.DEFAULT_INDICES_OPTIONS) + IndicesOptions.fromRequest(request, ResolveIndexAction.Request.DEFAULT_INDICES_OPTIONS), + clusterInfoOnly, + true ); return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).admin() .indices() diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java index 5e955539ee2ee..10ed5a065467b 100644 --- a/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java +++ b/server/src/main/java/org/elasticsearch/transport/RemoteClusterService.java @@ -183,12 +183,24 @@ boolean isRemoteNodeConnected(final String remoteCluster, final DiscoveryNode no return remoteClusters.get(remoteCluster).isNodeConnected(node); } - public Map groupIndices(IndicesOptions indicesOptions, String[] indices) { + /** + * Group indices by cluster alias mapped to OriginalIndices for that cluster. + * @param indicesOptions IndicesOptions to clarify how the index expressions should be parsed/applied + * @param indices Multiple index expressions as string[]. + * @param returnLocalAll whether to support the _all functionality needed by _search + * (See https://github.com/elastic/elasticsearch/pull/33899). If true, and no indices are specified, + * then a Map with one entry for the local cluster with an empty index array is returned. + * If false, an empty map is returned when when no indices are specified. + * @return Map keyed by cluster alias having OriginalIndices as the map value parsed from the String[] indices argument + */ + public Map groupIndices(IndicesOptions indicesOptions, String[] indices, boolean returnLocalAll) { final Map originalIndicesMap = new HashMap<>(); final Map> groupedIndices = groupClusterIndices(getRemoteClusterNames(), indices); if (groupedIndices.isEmpty()) { - // search on _all in the local cluster if neither local indices nor remote indices were specified - originalIndicesMap.put(LOCAL_CLUSTER_GROUP_KEY, new OriginalIndices(Strings.EMPTY_ARRAY, indicesOptions)); + if (returnLocalAll) { + // search on _all in the local cluster if neither local indices nor remote indices were specified + originalIndicesMap.put(LOCAL_CLUSTER_GROUP_KEY, new OriginalIndices(Strings.EMPTY_ARRAY, indicesOptions)); + } } else { for (Map.Entry> entry : groupedIndices.entrySet()) { String clusterAlias = entry.getKey(); @@ -199,6 +211,17 @@ public Map groupIndices(IndicesOptions indicesOptions, return originalIndicesMap; } + /** + * If no indices are specified, then a Map with one entry for the local cluster with an empty index array is returned. + * For details see {@code groupIndices(IndicesOptions indicesOptions, String[] indices, boolean returnLocalAll)} + * @param indicesOptions IndicesOptions to clarify how the index expressions should be parsed/applied + * @param indices Multiple index expressions as string[]. + * @return Map keyed by cluster alias having OriginalIndices as the map value parsed from the String[] indices argument + */ + public Map groupIndices(IndicesOptions indicesOptions, String[] indices) { + return groupIndices(indicesOptions, indices, true); + } + /** * Returns true iff the given cluster is configured as a remote cluster. Otherwise false */ diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequestTests.java index fdfe643f96066..68db5d3673376 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionRequestTests.java @@ -9,7 +9,6 @@ package org.elasticsearch.action.admin.indices.resolve; -import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.util.ArrayUtils; @@ -31,19 +30,23 @@ protected Writeable.Reader instanceReader() { @Override protected ResolveClusterActionRequest createTestInstance() { - String[] names = generateRandomStringArray(1, 7, false); - IndicesOptions indicesOptions = IndicesOptions.fromOptions( - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean(), - randomBoolean() - ); - return new ResolveClusterActionRequest(names, indicesOptions); + if (randomInt(5) == 3) { + return new ResolveClusterActionRequest(new String[0], IndicesOptions.DEFAULT, true, randomBoolean()); + } else { + String[] names = generateRandomStringArray(1, 7, false); + IndicesOptions indicesOptions = IndicesOptions.fromOptions( + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean(), + randomBoolean() + ); + return new ResolveClusterActionRequest(names, indicesOptions, false, randomBoolean()); + } } @Override @@ -71,12 +74,6 @@ protected ResolveClusterActionRequest mutateInstance(ResolveClusterActionRequest return mutatedInstance; } - public void testValidation() { - ResolveClusterActionRequest request = new ResolveClusterActionRequest(new String[0]); - ActionRequestValidationException exception = request.validate(); - assertNotNull(exception); - } - public void testLocalIndicesPresent() { { String[] indicesOrig = new String[] { "*" }; diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionResponseTests.java index 1d4ddde0c75e4..1410beb1ac41a 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/ResolveClusterActionResponseTests.java @@ -54,11 +54,12 @@ private Map randomResolveClusterInfoMap(ResolveClust } static ResolveClusterInfo randomResolveClusterInfo() { - int val = randomIntBetween(1, 3); + int val = randomIntBetween(1, 4); return switch (val) { case 1 -> new ResolveClusterInfo(false, randomBoolean()); case 2 -> new ResolveClusterInfo(randomBoolean(), randomBoolean(), randomAlphaOfLength(15)); case 3 -> new ResolveClusterInfo(randomBoolean(), randomBoolean(), randomBoolean(), Build.current()); + case 4 -> new ResolveClusterInfo(true, randomBoolean(), null, Build.current()); default -> throw new UnsupportedOperationException("should not get here"); }; } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterActionTests.java index 2c618f19a3c75..824ad22b1af20 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/resolve/TransportResolveClusterActionTests.java @@ -66,12 +66,8 @@ public void testCCSCompatibilityCheck() { ResolveClusterActionRequest request = new ResolveClusterActionRequest(new String[] { "test" }) { @Override public void writeTo(StreamOutput out) throws IOException { - throw new UnsupportedOperationException( - "ResolveClusterAction requires at least version " - + TransportVersions.V_8_13_0.toReleaseVersion() - + " but was " - + out.getTransportVersion().toReleaseVersion() - ); + String versionErrorMessage = ResolveClusterActionRequest.createVersionErrorMessage(out.getTransportVersion()); + throw new UnsupportedOperationException(versionErrorMessage); } }; ClusterService clusterService = new ClusterService(