Skip to content

Commit 135b165

Browse files
authored
Use valid REST version when determining capabilities (#123864)
If the rest api version is not specified, infer the correct one to use from the major versions present on the cluster, determined using features
1 parent e3c953f commit 135b165

File tree

7 files changed

+88
-46
lines changed

7 files changed

+88
-46
lines changed

server/src/main/java/module-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@
429429
provides org.elasticsearch.features.FeatureSpecification
430430
with
431431
org.elasticsearch.action.bulk.BulkFeatures,
432-
org.elasticsearch.features.FeatureInfrastructureFeatures,
432+
org.elasticsearch.features.InfrastructureFeatures,
433433
org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures,
434434
org.elasticsearch.index.mapper.MapperFeatures,
435435
org.elasticsearch.index.IndexFeatures,

server/src/main/java/org/elasticsearch/action/admin/cluster/node/capabilities/NodesCapabilitiesRequest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
1313
import org.elasticsearch.common.Strings;
14+
import org.elasticsearch.core.Nullable;
1415
import org.elasticsearch.core.RestApiVersion;
1516
import org.elasticsearch.rest.RestRequest;
1617

18+
import java.util.Optional;
1719
import java.util.Set;
1820

1921
public class NodesCapabilitiesRequest extends BaseNodesRequest {
@@ -22,7 +24,7 @@ public class NodesCapabilitiesRequest extends BaseNodesRequest {
2224
private String path = "/";
2325
private Set<String> parameters = Set.of();
2426
private Set<String> capabilities = Set.of();
25-
private RestApiVersion restApiVersion = RestApiVersion.current();
27+
private @Nullable RestApiVersion restApiVersion;
2628

2729
public NodesCapabilitiesRequest() {
2830
// send to all nodes
@@ -75,7 +77,7 @@ public NodesCapabilitiesRequest restApiVersion(RestApiVersion restApiVersion) {
7577
return this;
7678
}
7779

78-
public RestApiVersion restApiVersion() {
79-
return restApiVersion;
80+
public Optional<RestApiVersion> restApiVersion() {
81+
return Optional.ofNullable(restApiVersion);
8082
}
8183
}

server/src/main/java/org/elasticsearch/action/admin/cluster/node/capabilities/TransportNodesCapabilitiesAction.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import org.elasticsearch.common.io.stream.StreamInput;
1919
import org.elasticsearch.common.io.stream.StreamOutput;
2020
import org.elasticsearch.core.RestApiVersion;
21-
import org.elasticsearch.core.UpdateForV9;
21+
import org.elasticsearch.features.FeatureService;
22+
import org.elasticsearch.features.InfrastructureFeatures;
2223
import org.elasticsearch.injection.guice.Inject;
2324
import org.elasticsearch.rest.RestController;
2425
import org.elasticsearch.rest.RestRequest;
@@ -41,14 +42,16 @@ public class TransportNodesCapabilitiesAction extends TransportNodesAction<
4142
public static final ActionType<NodesCapabilitiesResponse> TYPE = new ActionType<>("cluster:monitor/nodes/capabilities");
4243

4344
private final RestController restController;
45+
private final FeatureService featureService;
4446

4547
@Inject
4648
public TransportNodesCapabilitiesAction(
4749
ThreadPool threadPool,
4850
ClusterService clusterService,
4951
TransportService transportService,
5052
ActionFilters actionFilters,
51-
RestController restController
53+
RestController restController,
54+
FeatureService featureService
5255
) {
5356
super(
5457
TYPE.name(),
@@ -59,6 +62,7 @@ public TransportNodesCapabilitiesAction(
5962
threadPool.executor(ThreadPool.Names.MANAGEMENT)
6063
);
6164
this.restController = restController;
65+
this.featureService = featureService;
6266
}
6367

6468
@Override
@@ -72,13 +76,21 @@ protected NodesCapabilitiesResponse newResponse(
7276

7377
@Override
7478
protected NodeCapabilitiesRequest newNodeRequest(NodesCapabilitiesRequest request) {
75-
return new NodeCapabilitiesRequest(
76-
request.method(),
77-
request.path(),
78-
request.parameters(),
79-
request.capabilities(),
80-
request.restApiVersion()
81-
);
79+
RestApiVersion restVersion;
80+
if (request.restApiVersion().isPresent()) {
81+
// explicit version - just use it, and see what happens
82+
restVersion = request.restApiVersion().get();
83+
} else if (featureService.clusterHasFeature(clusterService.state(), InfrastructureFeatures.CURRENT_VERSION)) {
84+
restVersion = RestApiVersion.current(); // every node is at least this major version, so use that
85+
} else {
86+
// not all nodes are the current version. previous major version nodes do not understand
87+
// the new REST API version, so query using the previous version.
88+
// Capabilities can come and go, so it's ok for the response to change
89+
// when the nodes change
90+
restVersion = RestApiVersion.previous();
91+
}
92+
93+
return new NodeCapabilitiesRequest(request.method(), request.path(), request.parameters(), request.capabilities(), restVersion);
8294
}
8395

8496
@Override
@@ -129,10 +141,6 @@ public NodeCapabilitiesRequest(
129141
this.restApiVersion = restApiVersion;
130142
}
131143

132-
@UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) // 8.x blows up in a mixed cluster when trying to read RestApiVersion.forMajor(9)
133-
// ./gradlew ":qa:mixed-cluster:v8.16.0#mixedClusterTest"
134-
// -Dtests.class="org.elasticsearch.backwards.MixedClusterClientYamlTestSuiteIT"
135-
// -Dtests.method="test {p0=capabilities/10_basic/Capabilities API}"
136144
@Override
137145
public void writeTo(StreamOutput out) throws IOException {
138146
super.writeTo(out);
@@ -141,9 +149,7 @@ public void writeTo(StreamOutput out) throws IOException {
141149
out.writeString(path);
142150
out.writeCollection(parameters, StreamOutput::writeString);
143151
out.writeCollection(capabilities, StreamOutput::writeString);
144-
// Fixme: lies! all lies!
145-
out.writeVInt(8);
146-
// out.writeVInt(restApiVersion.major);
152+
out.writeVInt(restApiVersion.major);
147153
}
148154
}
149155
}

server/src/main/java/org/elasticsearch/features/FeatureInfrastructureFeatures.java

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.features;
11+
12+
import org.elasticsearch.core.RestApiVersion;
13+
14+
import java.util.Set;
15+
16+
/**
17+
* This class specifies features for Elasticsearch infrastructure.
18+
*/
19+
public class InfrastructureFeatures implements FeatureSpecification {
20+
21+
/*
22+
* These features are auto-generated from the constants in RestApiVersion.
23+
*
24+
* When there's a new major version, CURRENT becomes N+1 and PREVIOUS becomes N.
25+
* Because PREVIOUS is marked as assumed, this doesn't stop N+1 nodes from joining the cluster.
26+
* A little table helps:
27+
*
28+
* Major | 9 | 10 | 11
29+
* ---------|-----|---- |-----
30+
* CURRENT | 9 | 10 | 11
31+
* PREVIOUS | 8 | 9 | 10
32+
*
33+
* v9 knows about REST API 9 and 8. v10 knows about REST API 10 and 9.
34+
* A v10 node can join a v9 cluster, as the ES_V_8 feature known by v9 is assumed.
35+
* But the v9 nodes don't know about ES_V_10, so that feature isn't active
36+
* on the v10 nodes until the cluster is fully upgraded,
37+
* at which point the ES_V_8 feature also disappears from the cluster.
38+
*
39+
* One thing you must not do is check the PREVIOUS_VERSION feature existence on the cluster,
40+
* as the answer will be wrong (v9 nodes will assume that v10 nodes have the v8 feature) - hence why it is private.
41+
* That feature only exists here so that upgrades work to remove the feature from the cluster.
42+
*/
43+
public static final NodeFeature CURRENT_VERSION = new NodeFeature("ES_" + RestApiVersion.current());
44+
private static final NodeFeature PREVIOUS_VERSION = new NodeFeature("ES_" + RestApiVersion.previous(), true);
45+
46+
@Override
47+
public Set<NodeFeature> getFeatures() {
48+
return Set.of(CURRENT_VERSION, PREVIOUS_VERSION);
49+
}
50+
51+
@Override
52+
public Set<NodeFeature> getTestFeatures() {
53+
return Set.of(FeatureService.TEST_FEATURES_ENABLED);
54+
}
55+
}

server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesCapabilitiesAction.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
6666
NodesCapabilitiesRequest r = requestNodes.method(RestRequest.Method.valueOf(request.param("method", "GET")))
6767
.path(path)
6868
.parameters(request.paramAsStringArray("parameters", Strings.EMPTY_ARRAY))
69-
.capabilities(request.paramAsStringArray("capabilities", Strings.EMPTY_ARRAY))
70-
.restApiVersion(request.getRestApiVersion());
69+
.capabilities(request.paramAsStringArray("capabilities", Strings.EMPTY_ARRAY));
70+
if (request.hasExplicitRestApiVersion()) {
71+
r.restApiVersion(request.getRestApiVersion());
72+
}
7173

7274
return channel -> client.admin().cluster().nodesCapabilities(r, new NodesResponseRestListener<>(channel));
7375
}

server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#
99

1010
org.elasticsearch.action.bulk.BulkFeatures
11-
org.elasticsearch.features.FeatureInfrastructureFeatures
11+
org.elasticsearch.features.InfrastructureFeatures
1212
org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures
1313
org.elasticsearch.index.IndexFeatures
1414
org.elasticsearch.index.mapper.MapperFeatures

0 commit comments

Comments
 (0)