From b1ad59d2e84fe3619144be9c263b42b6fe970010 Mon Sep 17 00:00:00 2001 From: Yu Date: Wed, 9 May 2018 13:14:07 +0200 Subject: [PATCH] Add `coordinating_only` node selector (#30313) Today we can execute cluster API actions on only master, data or ingest nodes using the `master:true`, `data:true` and `ingest:true` filters, but it is not so easy to select coordinating-only nodes (i.e. those nodes that are neither master nor data nor ingest nodes). This change fixes this by adding support for a `coordinating_only` filter such that `coordinating_only:true` adds all coordinating-only nodes to the set of selected nodes, and `coordinating_only:false` deletes them. Resolves #28831. --- docs/CHANGELOG.asciidoc | 3 +++ .../cluster/node/DiscoveryNode.java | 2 ++ .../cluster/node/DiscoveryNodes.java | 21 ++++++++++++++++++- .../cluster/node/DiscoveryNodesTests.java | 21 ++++++++++++++++--- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index d571700c4c5a1..60c1772f65db5 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -333,6 +333,9 @@ started or stopped. ({pull}30118[#30118]) Added put index template API to the high level rest client ({pull}30400[#30400]) +Add ability to filter coordinating-only nodes when interacting with cluster +APIs. ({pull}30313[#30313]) + [float] === Bug Fixes diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index c24880d9917bd..19805b52b2632 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -44,6 +44,8 @@ */ public class DiscoveryNode implements Writeable, ToXContentFragment { + static final String COORDINATING_ONLY = "coordinating_only"; + public static boolean nodeRequiresLocalStorage(Settings settings) { boolean localStorageEnable = Node.NODE_LOCAL_STORAGE_SETTING.get(settings); if (localStorageEnable == false && diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java index 4686ffba5b4a7..24a35983f8d35 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java @@ -147,6 +147,19 @@ public ImmutableOpenMap getMasterAndDataNodes() { return nodes.build(); } + /** + * Get a {@link Map} of the coordinating only nodes (nodes which are neither master, nor data, nor ingest nodes) arranged by their ids + * + * @return {@link Map} of the coordinating only nodes arranged by their ids + */ + public ImmutableOpenMap getCoordinatingOnlyNodes() { + ImmutableOpenMap.Builder nodes = ImmutableOpenMap.builder(this.nodes); + nodes.removeAll(masterNodes.keys()); + nodes.removeAll(dataNodes.keys()); + nodes.removeAll(ingestNodes.keys()); + return nodes.build(); + } + /** * Get a node by its id * @@ -294,7 +307,7 @@ public DiscoveryNode resolveNode(String node) { * - "_local" or "_master" for the relevant nodes * - a node id * - a wild card pattern that will be matched against node names - * - a "attr:value" pattern, where attr can be a node role (master, data, ingest etc.) in which case the value can be true of false + * - a "attr:value" pattern, where attr can be a node role (master, data, ingest etc.) in which case the value can be true or false, * or a generic node attribute name in which case value will be treated as a wildcard and matched against the node attribute values. */ public String[] resolveNodes(String... nodes) { @@ -346,6 +359,12 @@ public String[] resolveNodes(String... nodes) { } else { resolvedNodesIds.removeAll(ingestNodes.keys()); } + } else if (DiscoveryNode.COORDINATING_ONLY.equals(matchAttrName)) { + if (Booleans.parseBoolean(matchAttrValue, true)) { + resolvedNodesIds.addAll(getCoordinatingOnlyNodes().keys()); + } else { + resolvedNodesIds.removeAll(getCoordinatingOnlyNodes().keys()); + } } else { for (DiscoveryNode node : this) { for (Map.Entry entry : node.getAttributes().entrySet()) { diff --git a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java index 0401f51a58797..8ecf6be28df99 100644 --- a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java @@ -101,9 +101,17 @@ public void testCoordinatorOnlyNodes() { .map(DiscoveryNode::getId) .toArray(String[]::new); - assertThat( - discoveryNodes.resolveNodes("_all", "data:false", "ingest:false", "master:false"), - arrayContainingInAnyOrder(coordinatorOnlyNodes)); + final String[] nonCoordinatorOnlyNodes = + StreamSupport.stream(discoveryNodes.getNodes().values().spliterator(), false) + .map(n -> n.value) + .filter(n -> n.isMasterNode() || n.isDataNode() || n.isIngestNode()) + .map(DiscoveryNode::getId) + .toArray(String[]::new); + + assertThat(discoveryNodes.resolveNodes("coordinating_only:true"), arrayContainingInAnyOrder(coordinatorOnlyNodes)); + assertThat(discoveryNodes.resolveNodes("_all", "data:false", "ingest:false", "master:false"), + arrayContainingInAnyOrder(coordinatorOnlyNodes)); + assertThat(discoveryNodes.resolveNodes("_all", "coordinating_only:false"), arrayContainingInAnyOrder(nonCoordinatorOnlyNodes)); } public void testResolveNodesIds() { @@ -266,6 +274,13 @@ Set matchingNodeIds(DiscoveryNodes nodes) { nodes.getIngestNodes().keysIt().forEachRemaining(ids::add); return ids; } + }, COORDINATING_ONLY(DiscoveryNode.COORDINATING_ONLY + ":true") { + @Override + Set matchingNodeIds(DiscoveryNodes nodes) { + Set ids = new HashSet<>(); + nodes.getCoordinatingOnlyNodes().keysIt().forEachRemaining(ids::add); + return ids; + } }, CUSTOM_ATTRIBUTE("attr:value") { @Override Set matchingNodeIds(DiscoveryNodes nodes) {