Skip to content

Commit e88dd23

Browse files
[WIP] Allocation explain accepts path parameters
The allocation explain API previously only accepted its parameters in the request body. This change extends the API to accept parameters in either the URL as path parameters, or via the request body, but not as a mix of both.
1 parent 1c1907f commit e88dd23

File tree

2 files changed

+109
-17
lines changed

2 files changed

+109
-17
lines changed

server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
import org.elasticsearch.action.support.master.MasterNodeRequest;
1414
import org.elasticsearch.common.io.stream.StreamInput;
1515
import org.elasticsearch.common.io.stream.StreamOutput;
16+
import org.elasticsearch.common.util.set.Sets;
1617
import org.elasticsearch.core.Nullable;
1718
import org.elasticsearch.core.TimeValue;
19+
import org.elasticsearch.rest.RestUtils;
1820
import org.elasticsearch.xcontent.ObjectParser;
1921
import org.elasticsearch.xcontent.ParseField;
2022
import org.elasticsearch.xcontent.XContentParser;
2123

2224
import java.io.IOException;
25+
import java.util.Set;
2326

2427
import static org.elasticsearch.action.ValidateActions.addValidationError;
2528

@@ -28,12 +31,36 @@
2831
*/
2932
public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAllocationExplainRequest> {
3033

34+
public static final String INDEX_PARAMETER_NAME = "index";
35+
public static final String SHARD_PARAMETER_NAME = "shard";
36+
public static final String PRIMARY_PARAMETER_NAME = "primary";
37+
public static final String CURRENT_NODE_PARAMETER_NAME = "current_node";
38+
public static final String INCLUDE_YES_DECISIONS_PARAMETER_NAME = "include_yes_decisions";
39+
public static final String INCLUDE_DISK_INFO_PARAMETER_NAME = "include_disk_info";
40+
public static final Set<String> PATH_PARAMETERS = Set.of(
41+
INDEX_PARAMETER_NAME,
42+
SHARD_PARAMETER_NAME,
43+
PRIMARY_PARAMETER_NAME,
44+
CURRENT_NODE_PARAMETER_NAME
45+
);
46+
public static final Set<String> QUERY_PARAMETERS = Set.of(
47+
INCLUDE_YES_DECISIONS_PARAMETER_NAME,
48+
INCLUDE_DISK_INFO_PARAMETER_NAME,
49+
RestUtils.REST_MASTER_TIMEOUT_PARAM
50+
);
51+
public static final Set<String> ALL_SUPPORTED_PARAMETERS = Set.copyOf(
52+
Sets.union(
53+
PATH_PARAMETERS,
54+
QUERY_PARAMETERS
55+
)
56+
);
57+
3158
private static final ObjectParser<ClusterAllocationExplainRequest, Void> PARSER = new ObjectParser<>("cluster/allocation/explain");
3259
static {
33-
PARSER.declareString(ClusterAllocationExplainRequest::setIndex, new ParseField("index"));
34-
PARSER.declareInt(ClusterAllocationExplainRequest::setShard, new ParseField("shard"));
35-
PARSER.declareBoolean(ClusterAllocationExplainRequest::setPrimary, new ParseField("primary"));
36-
PARSER.declareString(ClusterAllocationExplainRequest::setCurrentNode, new ParseField("current_node"));
60+
PARSER.declareString(ClusterAllocationExplainRequest::setIndex, new ParseField(INDEX_PARAMETER_NAME));
61+
PARSER.declareInt(ClusterAllocationExplainRequest::setShard, new ParseField(SHARD_PARAMETER_NAME));
62+
PARSER.declareBoolean(ClusterAllocationExplainRequest::setPrimary, new ParseField(PRIMARY_PARAMETER_NAME));
63+
PARSER.declareString(ClusterAllocationExplainRequest::setCurrentNode, new ParseField(CURRENT_NODE_PARAMETER_NAME));
3764
}
3865

3966
@Nullable
@@ -221,14 +248,14 @@ public String toString() {
221248
if (this.useAnyUnassignedShard()) {
222249
sb.append("useAnyUnassignedShard=true");
223250
} else {
224-
sb.append("index=").append(index);
225-
sb.append(",shard=").append(shard);
226-
sb.append(",primary?=").append(primary);
251+
sb.append(INDEX_PARAMETER_NAME).append("=").append(index);
252+
sb.append(",").append(SHARD_PARAMETER_NAME).append("=").append(shard);
253+
sb.append(",").append(PRIMARY_PARAMETER_NAME).append("?=").append(primary);
227254
if (currentNode != null) {
228-
sb.append(",currentNode=").append(currentNode);
255+
sb.append(",").append(CURRENT_NODE_PARAMETER_NAME).append("=").append(currentNode);
229256
}
230257
}
231-
sb.append(",includeYesDecisions?=").append(includeYesDecisions);
258+
sb.append(",").append(INCLUDE_YES_DECISIONS_PARAMETER_NAME).append("?=").append(includeYesDecisions);
232259
return sb.toString();
233260
}
234261

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

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest;
1313
import org.elasticsearch.action.admin.cluster.allocation.TransportClusterAllocationExplainAction;
1414
import org.elasticsearch.client.internal.node.NodeClient;
15+
import org.elasticsearch.common.util.set.Sets;
1516
import org.elasticsearch.rest.BaseRestHandler;
1617
import org.elasticsearch.rest.RestRequest;
1718
import org.elasticsearch.rest.RestUtils;
@@ -21,7 +22,9 @@
2122
import org.elasticsearch.xcontent.XContentParser;
2223

2324
import java.io.IOException;
25+
import java.util.Iterator;
2426
import java.util.List;
27+
import java.util.Set;
2528

2629
import static org.elasticsearch.rest.RestRequest.Method.GET;
2730
import static org.elasticsearch.rest.RestRequest.Method.POST;
@@ -47,23 +50,85 @@ public boolean allowSystemIndexAccessByDefault() {
4750
return true;
4851
}
4952

53+
@Override
54+
public Set<String> allSupportedParameters() {
55+
return ClusterAllocationExplainRequest.ALL_SUPPORTED_PARAMETERS;
56+
}
57+
58+
@Override
59+
public Set<String> supportedQueryParameters() {
60+
return ClusterAllocationExplainRequest.QUERY_PARAMETERS;
61+
}
62+
5063
@Override
5164
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
52-
final var req = new ClusterAllocationExplainRequest(RestUtils.getMasterNodeTimeout(request));
53-
if (request.hasContentOrSourceParam()) {
54-
try (XContentParser parser = request.contentOrSourceParamParser()) {
55-
ClusterAllocationExplainRequest.parse(req, parser);
65+
/*
66+
https://github.com/elastic/elasticsearch/issues/127028 introduces dual behaviour for this API.
67+
We now support either, but not a mix of:
68+
1. Parameters being passed in the URL
69+
2. The legacy behaviour of passing parameters in the body of the request
70+
*/
71+
72+
// boolean userPassedParametersInPath = !request.params().isEmpty();
73+
74+
boolean userPassedParametersInPath = isPathParameterProvided(request.params().keySet());
75+
final var clusterAllocationExplainRequest = new ClusterAllocationExplainRequest(RestUtils.getMasterNodeTimeout(request));
76+
77+
if (userPassedParametersInPath) {
78+
String index = request.param(ClusterAllocationExplainRequest.INDEX_PARAMETER_NAME);
79+
if (index == null) {
80+
throw new IllegalArgumentException("The index parameter cannot be blank.");
81+
}
82+
clusterAllocationExplainRequest.setIndex(index);
83+
84+
String shard = request.param(ClusterAllocationExplainRequest.SHARD_PARAMETER_NAME);
85+
if (shard == null) {
86+
throw new IllegalArgumentException("The shard parameter cannot be blank.");
5687
}
57-
} // else ok, an empty body means "explain the first unassigned shard you find"
58-
req.includeYesDecisions(request.paramAsBoolean("include_yes_decisions", false));
59-
req.includeDiskInfo(request.paramAsBoolean("include_disk_info", false));
88+
clusterAllocationExplainRequest.setShard(Integer.parseInt(shard));
89+
90+
String primary = request.param(ClusterAllocationExplainRequest.PRIMARY_PARAMETER_NAME);
91+
if (primary == null) {
92+
throw new IllegalArgumentException("The primary parameter cannot be blank.");
93+
}
94+
clusterAllocationExplainRequest.setPrimary(Boolean.parseBoolean(primary));
95+
96+
String current_node = request.param(ClusterAllocationExplainRequest.CURRENT_NODE_PARAMETER_NAME);
97+
if (current_node == null) {
98+
throw new IllegalArgumentException("The current_node parameter cannot be blank.");
99+
}
100+
clusterAllocationExplainRequest.setCurrentNode(current_node);
101+
102+
// TODO - include_yes_decisions and include_disk_info
103+
} else {
104+
if (request.hasContentOrSourceParam()) {
105+
try (XContentParser parser = request.contentOrSourceParamParser()) {
106+
ClusterAllocationExplainRequest.parse(clusterAllocationExplainRequest, parser);
107+
}
108+
} // else ok, an empty body means "explain the first unassigned shard you find"
109+
// TODO - Set all 4 params to being consumed
110+
111+
// TODO - Can we move this outside the IF statement so it runs for both?
112+
clusterAllocationExplainRequest.includeYesDecisions(request.paramAsBoolean("include_yes_decisions", false));
113+
clusterAllocationExplainRequest.includeDiskInfo(request.paramAsBoolean("include_disk_info", false));
114+
}
115+
60116
return channel -> client.execute(
61117
TransportClusterAllocationExplainAction.TYPE,
62-
req,
118+
clusterAllocationExplainRequest,
63119
new RestRefCountedChunkedToXContentListener<>(channel)
64120
);
65121
}
66122

123+
private boolean isPathParameterProvided(Set<String> parameters) {
124+
for (String parameter : parameters) {
125+
if (ClusterAllocationExplainRequest.PATH_PARAMETERS.contains(parameter)) {
126+
return true;
127+
}
128+
}
129+
return false;
130+
}
131+
67132
@Override
68133
public boolean canTripCircuitBreaker() {
69134
return false;

0 commit comments

Comments
 (0)