Skip to content

Commit f872c72

Browse files
authored
Run Node deprecation checks locally (#38065) (#38250)
At times, we need to check for usage of deprecated settings in settings which should not be returned by the NodeInfo API. This commit changes the deprecation info API to run all node checks locally so that these settings can be checked without exposing them via any externally accessible API.
1 parent 1899658 commit f872c72

File tree

11 files changed

+500
-74
lines changed

11 files changed

+500
-74
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecationInfoAction.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.elasticsearch.action.ActionResponse;
1212
import org.elasticsearch.action.IndicesRequest;
1313
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
14-
import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
1514
import org.elasticsearch.action.support.IndicesOptions;
1615
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
1716
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
@@ -35,7 +34,6 @@
3534
import java.util.Map;
3635
import java.util.Objects;
3736
import java.util.Set;
38-
import java.util.function.BiFunction;
3937
import java.util.function.Function;
4038
import java.util.stream.Collectors;
4139

@@ -62,6 +60,23 @@ public static <T> List<DeprecationIssue> filterChecks(List<T> checks, Function<T
6260
return checks.stream().map(mapper).filter(Objects::nonNull).collect(Collectors.toList());
6361
}
6462

63+
private static List<DeprecationIssue> mergeNodeIssues(NodesDeprecationCheckResponse response) {
64+
Map<DeprecationIssue, List<String>> issueListMap = new HashMap<>();
65+
for (NodesDeprecationCheckAction.NodeResponse resp : response.getNodes()) {
66+
for (DeprecationIssue issue : resp.getDeprecationIssues()) {
67+
issueListMap.computeIfAbsent(issue, (key) -> new ArrayList<>()).add(resp.getNode().getName());
68+
}
69+
}
70+
71+
return issueListMap.entrySet().stream()
72+
.map(entry -> {
73+
DeprecationIssue issue = entry.getKey();
74+
String details = issue.getDetails() != null ? issue.getDetails() + " " : "";
75+
return new DeprecationIssue(issue.getLevel(), issue.getMessage(), issue.getUrl(),
76+
details + "(nodes impacted: " + entry.getValue() + ")");
77+
}).collect(Collectors.toList());
78+
}
79+
6580
@Override
6681
public Response newResponse() {
6782
return new Response();
@@ -159,32 +174,29 @@ public int hashCode() {
159174
* this function will run through all the checks and build out the final list of issues that exist in the
160175
* cluster.
161176
*
162-
* @param nodesInfo The list of {@link NodeInfo} metadata objects for retrieving node-level information
163-
* @param nodesStats The list of {@link NodeStats} metadata objects for retrieving node-level information
164177
* @param state The cluster state
165178
* @param indexNameExpressionResolver Used to resolve indices into their concrete names
166179
* @param indices The list of index expressions to evaluate using `indexNameExpressionResolver`
167180
* @param indicesOptions The options to use when resolving and filtering which indices to check
168181
* @param datafeeds The ml datafeed configurations
169-
* @param clusterSettingsChecks The list of cluster-level checks
170-
* @param nodeSettingsChecks The list of node-level checks
182+
* @param nodeDeprecationResponse The response containing the deprecation issues found on each node
171183
* @param indexSettingsChecks The list of index-level checks that will be run across all specified
172184
* concrete indices
185+
* @param clusterSettingsChecks The list of cluster-level checks
173186
* @param mlSettingsCheck The list of ml checks
174187
* @return The list of deprecation issues found in the cluster
175188
*/
176-
public static DeprecationInfoAction.Response from(List<NodeInfo> nodesInfo, List<NodeStats> nodesStats, ClusterState state,
177-
IndexNameExpressionResolver indexNameExpressionResolver,
178-
String[] indices, IndicesOptions indicesOptions,
179-
List<DatafeedConfig> datafeeds,
180-
List<Function<ClusterState,DeprecationIssue>>clusterSettingsChecks,
181-
List<BiFunction<List<NodeInfo>, List<NodeStats>, DeprecationIssue>> nodeSettingsChecks,
182-
List<Function<IndexMetaData, DeprecationIssue>> indexSettingsChecks,
183-
List<Function<DatafeedConfig, DeprecationIssue>> mlSettingsCheck) {
189+
public static DeprecationInfoAction.Response from(ClusterState state,
190+
IndexNameExpressionResolver indexNameExpressionResolver,
191+
String[] indices, IndicesOptions indicesOptions,
192+
List<DatafeedConfig> datafeeds,
193+
NodesDeprecationCheckResponse nodeDeprecationResponse,
194+
List<Function<IndexMetaData, DeprecationIssue>> indexSettingsChecks,
195+
List<Function<ClusterState, DeprecationIssue>> clusterSettingsChecks,
196+
List<Function<DatafeedConfig, DeprecationIssue>> mlSettingsCheck) {
184197
List<DeprecationIssue> clusterSettingsIssues = filterChecks(clusterSettingsChecks,
185198
(c) -> c.apply(state));
186-
List<DeprecationIssue> nodeSettingsIssues = filterChecks(nodeSettingsChecks,
187-
(c) -> c.apply(nodesInfo, nodesStats));
199+
List<DeprecationIssue> nodeSettingsIssues = mergeNodeIssues(nodeDeprecationResponse);
188200
List<DeprecationIssue> mlSettingsIssues = new ArrayList<>();
189201
for (DatafeedConfig config : datafeeds) {
190202
mlSettingsIssues.addAll(filterChecks(mlSettingsCheck, (c) -> c.apply(config)));
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.deprecation;
8+
9+
import org.elasticsearch.action.Action;
10+
import org.elasticsearch.action.support.nodes.BaseNodeRequest;
11+
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
12+
import org.elasticsearch.action.support.nodes.NodesOperationRequestBuilder;
13+
import org.elasticsearch.client.ElasticsearchClient;
14+
import org.elasticsearch.cluster.node.DiscoveryNode;
15+
import org.elasticsearch.common.io.stream.StreamInput;
16+
import org.elasticsearch.common.io.stream.StreamOutput;
17+
18+
import java.io.IOException;
19+
import java.util.List;
20+
import java.util.Objects;
21+
22+
/**
23+
* Runs deprecation checks on each node. Deprecation checks are performed locally so that filtered settings
24+
* can be accessed in the deprecation checks.
25+
*/
26+
public class NodesDeprecationCheckAction extends Action<NodesDeprecationCheckResponse> {
27+
public static final NodesDeprecationCheckAction INSTANCE = new NodesDeprecationCheckAction();
28+
public static final String NAME = "cluster:admin/xpack/deprecation/nodes/info";
29+
30+
private NodesDeprecationCheckAction() {
31+
super(NAME);
32+
}
33+
34+
@Override
35+
public NodesDeprecationCheckResponse newResponse() {
36+
return new NodesDeprecationCheckResponse();
37+
}
38+
39+
public static class NodeRequest extends BaseNodeRequest {
40+
41+
NodesDeprecationCheckRequest request;
42+
43+
public NodeRequest() {}
44+
public NodeRequest(String nodeId, NodesDeprecationCheckRequest request) {
45+
super(nodeId);
46+
this.request = request;
47+
}
48+
49+
@Override
50+
public void readFrom(StreamInput in) throws IOException {
51+
super.readFrom(in);
52+
request = new NodesDeprecationCheckRequest();
53+
request.readFrom(in);
54+
}
55+
56+
@Override
57+
public void writeTo(StreamOutput out) throws IOException {
58+
super.writeTo(out);
59+
request.writeTo(out);
60+
}
61+
}
62+
63+
public static class NodeResponse extends BaseNodeResponse {
64+
private List<DeprecationIssue> deprecationIssues;
65+
66+
public NodeResponse() {
67+
super();
68+
}
69+
70+
public NodeResponse(DiscoveryNode node, List<DeprecationIssue> deprecationIssues) {
71+
super(node);
72+
this.deprecationIssues = deprecationIssues;
73+
}
74+
75+
@Override
76+
public void readFrom(StreamInput in) throws IOException {
77+
super.readFrom(in);
78+
deprecationIssues = in.readList(DeprecationIssue::new);
79+
}
80+
81+
@Override
82+
public void writeTo(StreamOutput out) throws IOException {
83+
super.writeTo(out);
84+
out.writeList(this.deprecationIssues);
85+
}
86+
87+
public static NodeResponse readNodeResponse(StreamInput in) throws IOException {
88+
NodeResponse nodeResponse = new NodeResponse();
89+
nodeResponse.readFrom(in);
90+
return nodeResponse;
91+
}
92+
93+
public List<DeprecationIssue> getDeprecationIssues() {
94+
return deprecationIssues;
95+
}
96+
97+
@Override
98+
public boolean equals(Object o) {
99+
if (this == o) return true;
100+
if (o == null || getClass() != o.getClass()) return false;
101+
NodeResponse that = (NodeResponse) o;
102+
return Objects.equals(getDeprecationIssues(), that.getDeprecationIssues())
103+
&& Objects.equals(getNode(), that.getNode());
104+
}
105+
106+
@Override
107+
public int hashCode() {
108+
return Objects.hash(getNode(), getDeprecationIssues());
109+
}
110+
}
111+
112+
public static class RequestBuilder extends NodesOperationRequestBuilder<NodesDeprecationCheckRequest,
113+
NodesDeprecationCheckResponse, RequestBuilder> {
114+
115+
protected RequestBuilder(ElasticsearchClient client,
116+
Action<NodesDeprecationCheckResponse> action,
117+
NodesDeprecationCheckRequest request) {
118+
super(client, action, request);
119+
}
120+
}
121+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.deprecation;
8+
9+
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
10+
import org.elasticsearch.common.io.stream.StreamInput;
11+
import org.elasticsearch.common.io.stream.StreamOutput;
12+
13+
import java.io.IOException;
14+
import java.util.Arrays;
15+
import java.util.Objects;
16+
17+
public class NodesDeprecationCheckRequest extends BaseNodesRequest<NodesDeprecationCheckRequest> {
18+
public NodesDeprecationCheckRequest() {}
19+
20+
public NodesDeprecationCheckRequest(String... nodesIds) {
21+
super(nodesIds);
22+
}
23+
24+
@Override
25+
public void readFrom(StreamInput in) throws IOException {
26+
super.readFrom(in);
27+
}
28+
29+
@Override
30+
public void writeTo(StreamOutput out) throws IOException {
31+
super.writeTo(out);
32+
}
33+
34+
@Override
35+
public int hashCode() {
36+
return Objects.hash((Object[]) this.nodesIds());
37+
}
38+
39+
@Override
40+
public boolean equals(Object obj) {
41+
if (this == obj) {
42+
return true;
43+
}
44+
if (obj == null || getClass() != obj.getClass()) {
45+
return false;
46+
}
47+
NodesDeprecationCheckRequest that = (NodesDeprecationCheckRequest) obj;
48+
return Arrays.equals(this.nodesIds(), that.nodesIds());
49+
}
50+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.deprecation;
8+
9+
import org.elasticsearch.action.FailedNodeException;
10+
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
11+
import org.elasticsearch.cluster.ClusterName;
12+
import org.elasticsearch.common.io.stream.StreamInput;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
14+
15+
import java.io.IOException;
16+
import java.util.List;
17+
import java.util.Objects;
18+
19+
public class NodesDeprecationCheckResponse extends BaseNodesResponse<NodesDeprecationCheckAction.NodeResponse> {
20+
21+
public NodesDeprecationCheckResponse() {}
22+
23+
public NodesDeprecationCheckResponse(ClusterName clusterName,
24+
List<NodesDeprecationCheckAction.NodeResponse> nodes,
25+
List<FailedNodeException> failures) {
26+
super(clusterName, nodes, failures);
27+
}
28+
29+
@Override
30+
protected List<NodesDeprecationCheckAction.NodeResponse> readNodesFrom(StreamInput in) throws IOException {
31+
return in.readList(NodesDeprecationCheckAction.NodeResponse::readNodeResponse);
32+
}
33+
34+
@Override
35+
protected void writeNodesTo(StreamOutput out, List<NodesDeprecationCheckAction.NodeResponse> nodes) throws IOException {
36+
out.writeStreamableList(nodes);
37+
}
38+
39+
@Override
40+
public boolean equals(Object o) {
41+
if (this == o) return true;
42+
if (o == null || getClass() != o.getClass()) return false;
43+
NodesDeprecationCheckResponse that = (NodesDeprecationCheckResponse) o;
44+
return Objects.equals(getClusterName(), that.getClusterName())
45+
&& Objects.equals(getNodes(), that.getNodes())
46+
&& Objects.equals(failures(), that.failures());
47+
}
48+
49+
@Override
50+
public int hashCode() {
51+
return Objects.hash(getClusterName(), getNodes(), failures());
52+
}
53+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/deprecation/DeprecationInfoActionResponseTests.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.core.deprecation;
77

8-
import org.elasticsearch.Build;
98
import org.elasticsearch.Version;
10-
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
11-
import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
129
import org.elasticsearch.action.support.IndicesOptions;
1310
import org.elasticsearch.cluster.ClusterName;
1411
import org.elasticsearch.cluster.ClusterState;
@@ -31,7 +28,6 @@
3128
import java.util.HashMap;
3229
import java.util.List;
3330
import java.util.Map;
34-
import java.util.function.BiFunction;
3531
import java.util.function.Function;
3632
import java.util.stream.Collectors;
3733
import java.util.stream.Stream;
@@ -78,12 +74,6 @@ public void testFrom() throws IOException {
7874
DiscoveryNode discoveryNode = DiscoveryNode.createLocal(Settings.EMPTY,
7975
new TransportAddress(TransportAddress.META_ADDRESS, 9300), "test");
8076
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metaData(metadata).build();
81-
List<NodeInfo> nodeInfos = Collections.singletonList(new NodeInfo(Version.CURRENT, Build.CURRENT,
82-
discoveryNode, null, null, null, null,
83-
null, null, null, null, null, null));
84-
List<NodeStats> nodeStats = Collections.singletonList(new NodeStats(discoveryNode, 0L, null,
85-
null, null, null, null, null, null, null, null,
86-
null, null, null, null));
8777
List<DatafeedConfig> datafeeds = Collections.singletonList(DatafeedConfigTests.createRandomizedDatafeedConfig("foo"));
8878
IndexNameExpressionResolver resolver = new IndexNameExpressionResolver();
8979
IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false,
@@ -97,11 +87,6 @@ public void testFrom() throws IOException {
9787
Collections.unmodifiableList(Arrays.asList(
9888
(s) -> clusterIssueFound ? foundIssue : null
9989
));
100-
List<BiFunction<List<NodeInfo>, List<NodeStats>, DeprecationIssue>> nodeSettingsChecks =
101-
Collections.unmodifiableList(Arrays.asList(
102-
(ln, ls) -> nodeIssueFound ? foundIssue : null
103-
));
104-
10590
List<Function<IndexMetaData, DeprecationIssue>> indexSettingsChecks =
10691
Collections.unmodifiableList(Arrays.asList(
10792
(idx) -> indexIssueFound ? foundIssue : null
@@ -111,9 +96,17 @@ public void testFrom() throws IOException {
11196
(idx) -> mlIssueFound ? foundIssue : null
11297
));
11398

114-
DeprecationInfoAction.Response response = DeprecationInfoAction.Response.from(nodeInfos, nodeStats, state,
99+
NodesDeprecationCheckResponse nodeDeprecationIssues = new NodesDeprecationCheckResponse(
100+
new ClusterName(randomAlphaOfLength(5)),
101+
nodeIssueFound
102+
? Collections.singletonList(
103+
new NodesDeprecationCheckAction.NodeResponse(discoveryNode, Collections.singletonList(foundIssue)))
104+
: Collections.emptyList(),
105+
Collections.emptyList());
106+
107+
DeprecationInfoAction.Response response = DeprecationInfoAction.Response.from(state,
115108
resolver, Strings.EMPTY_ARRAY, indicesOptions, datafeeds,
116-
clusterSettingsChecks, nodeSettingsChecks, indexSettingsChecks, mlSettingsChecks);
109+
nodeDeprecationIssues, indexSettingsChecks, clusterSettingsChecks, mlSettingsChecks);
117110

118111
if (clusterIssueFound) {
119112
assertThat(response.getClusterSettingsIssues(), equalTo(Collections.singletonList(foundIssue)));
@@ -122,7 +115,10 @@ public void testFrom() throws IOException {
122115
}
123116

124117
if (nodeIssueFound) {
125-
assertThat(response.getNodeSettingsIssues(), equalTo(Collections.singletonList(foundIssue)));
118+
String details = foundIssue.getDetails() != null ? foundIssue.getDetails() + " " : "";
119+
DeprecationIssue mergedFoundIssue = new DeprecationIssue(foundIssue.getLevel(), foundIssue.getMessage(), foundIssue.getUrl(),
120+
details + "(nodes impacted: [" + discoveryNode.getName() + "])");
121+
assertThat(response.getNodeSettingsIssues(), equalTo(Collections.singletonList(mergedFoundIssue)));
126122
} else {
127123
assertTrue(response.getNodeSettingsIssues().isEmpty());
128124
}

0 commit comments

Comments
 (0)