/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.cluster.node;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.elasticsearch.Version;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.nullValue;
public class DiscoveryNodesTests extends ESTestCase {
public void testResolveNodeByIdOrName() {
DiscoveryNodes discoveryNodes = buildDiscoveryNodes();
DiscoveryNode[] nodes = discoveryNodes.getNodes().values().toArray(DiscoveryNode.class);
DiscoveryNode node = randomFrom(nodes);
DiscoveryNode resolvedNode = discoveryNodes.resolveNode(randomBoolean() ? node.getId() : node.getName());
assertThat(resolvedNode.getId(), equalTo(node.getId()));
}
public void testResolveNodeByAttribute() {
DiscoveryNodes discoveryNodes = buildDiscoveryNodes();
NodeSelector nodeSelector = randomFrom(NodeSelector.values());
Set<String> matchingNodeIds = nodeSelector.matchingNodeIds(discoveryNodes);
try {
DiscoveryNode resolvedNode = discoveryNodes.resolveNode(nodeSelector.selector);
assertThat(matchingNodeIds.size(), equalTo(1));
assertThat(resolvedNode.getId(), equalTo(matchingNodeIds.iterator().next()));
} catch (IllegalArgumentException e) {
if (matchingNodeIds.size() == 0) {
assertThat(e.getMessage(), equalTo("failed to resolve [" + nodeSelector.selector + "], no matching nodes"));
} else if (matchingNodeIds.size() > 1) {
assertThat(e.getMessage(), containsString("where expected to be resolved to a single node"));
} else {
fail("resolveNode shouldn't have failed for [" + nodeSelector.selector + "]");
}
}
}
public void testResolveNodesIds() {
DiscoveryNodes discoveryNodes = buildDiscoveryNodes();
int numSelectors = randomIntBetween(1, 5);
Set<String> nodeSelectors = new HashSet<>();
Set<String> expectedNodeIdsSet = new HashSet<>();
for (int i = 0; i < numSelectors; i++) {
NodeSelector nodeSelector = randomFrom(NodeSelector.values());
if (nodeSelectors.add(nodeSelector.selector)) {
expectedNodeIdsSet.addAll(nodeSelector.matchingNodeIds(discoveryNodes));
}
}
int numNodeIds = randomIntBetween(0, 3);
String[] nodeIds = discoveryNodes.getNodes().keys().toArray(String.class);
for (int i = 0; i < numNodeIds; i++) {
String nodeId = randomFrom(nodeIds);
nodeSelectors.add(nodeId);
expectedNodeIdsSet.add(nodeId);
}
int numNodeNames = randomIntBetween(0, 3);
DiscoveryNode[] nodes = discoveryNodes.getNodes().values().toArray(DiscoveryNode.class);
for (int i = 0; i < numNodeNames; i++) {
DiscoveryNode discoveryNode = randomFrom(nodes);
nodeSelectors.add(discoveryNode.getName());
expectedNodeIdsSet.add(discoveryNode.getId());
}
String[] resolvedNodesIds = discoveryNodes.resolveNodes(nodeSelectors.toArray(new String[nodeSelectors.size()]));
Arrays.sort(resolvedNodesIds);
String[] expectedNodesIds = expectedNodeIdsSet.toArray(new String[expectedNodeIdsSet.size()]);
Arrays.sort(expectedNodesIds);
assertThat(resolvedNodesIds, equalTo(expectedNodesIds));
}
public void testDeltas() {
Set<DiscoveryNode> nodesA = new HashSet<>();
nodesA.addAll(randomNodes(1 + randomInt(10)));
Set<DiscoveryNode> nodesB = new HashSet<>();
nodesB.addAll(randomNodes(1 + randomInt(5)));
for (DiscoveryNode node : randomSubsetOf(nodesA)) {
if (randomBoolean()) {
// change an attribute
Map<String, String> attrs = new HashMap<>(node.getAttributes());
attrs.put("new", "new");
node = new DiscoveryNode(node.getName(), node.getId(), node.getAddress(), attrs, node.getRoles(), node.getVersion());
}
nodesB.add(node);
}
DiscoveryNode masterA = randomBoolean() ? null : RandomPicks.randomFrom(random(), nodesA);
DiscoveryNode masterB = randomBoolean() ? null : RandomPicks.randomFrom(random(), nodesB);
DiscoveryNodes.Builder builderA = DiscoveryNodes.builder();
nodesA.stream().forEach(builderA::add);
final String masterAId = masterA == null ? null : masterA.getId();
builderA.masterNodeId(masterAId);
builderA.localNodeId(RandomPicks.randomFrom(random(), nodesA).getId());
DiscoveryNodes.Builder builderB = DiscoveryNodes.builder();
nodesB.stream().forEach(builderB::add);
final String masterBId = masterB == null ? null : masterB.getId();
builderB.masterNodeId(masterBId);
builderB.localNodeId(RandomPicks.randomFrom(random(), nodesB).getId());
final DiscoveryNodes discoNodesA = builderA.build();
final DiscoveryNodes discoNodesB = builderB.build();
logger.info("nodes A: {}", discoNodesA);
logger.info("nodes B: {}", discoNodesB);
DiscoveryNodes.Delta delta = discoNodesB.delta(discoNodesA);
if (masterB == null || Objects.equals(masterAId, masterBId)) {
assertFalse(delta.masterNodeChanged());
assertThat(delta.previousMasterNode(), nullValue());
assertThat(delta.newMasterNode(), nullValue());
} else {
assertTrue(delta.masterNodeChanged());
assertThat(delta.newMasterNode().getId(), equalTo(masterBId));
assertThat(delta.previousMasterNode() != null ? delta.previousMasterNode().getId() : null,
equalTo(masterAId));
}
Set<DiscoveryNode> newNodes = new HashSet<>(nodesB);
newNodes.removeAll(nodesA);
assertThat(delta.added(), equalTo(newNodes.isEmpty() == false));
assertThat(delta.addedNodes(), containsInAnyOrder(newNodes.stream().collect(Collectors.toList()).toArray()));
assertThat(delta.addedNodes().size(), equalTo(newNodes.size()));
Set<DiscoveryNode> removedNodes = new HashSet<>(nodesA);
removedNodes.removeAll(nodesB);
assertThat(delta.removed(), equalTo(removedNodes.isEmpty() == false));
assertThat(delta.removedNodes(), containsInAnyOrder(removedNodes.stream().collect(Collectors.toList()).toArray()));
assertThat(delta.removedNodes().size(), equalTo(removedNodes.size()));
}
private static AtomicInteger idGenerator = new AtomicInteger();
private static List<DiscoveryNode> randomNodes(final int numNodes) {
List<DiscoveryNode> nodesList = new ArrayList<>();
for (int i = 0; i < numNodes; i++) {
Map<String, String> attributes = new HashMap<>();
if (frequently()) {
attributes.put("custom", randomBoolean() ? "match" : randomAlphaOfLengthBetween(3, 5));
}
final DiscoveryNode node = newNode(idGenerator.getAndIncrement(), attributes,
new HashSet<>(randomSubsetOf(Arrays.asList(DiscoveryNode.Role.values()))));
nodesList.add(node);
}
return nodesList;
}
private static DiscoveryNodes buildDiscoveryNodes() {
int numNodes = randomIntBetween(1, 10);
DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder();
List<DiscoveryNode> nodesList = randomNodes(numNodes);
for (DiscoveryNode node : nodesList) {
discoBuilder = discoBuilder.add(node);
}
discoBuilder.localNodeId(randomFrom(nodesList).getId());
discoBuilder.masterNodeId(randomFrom(nodesList).getId());
return discoBuilder.build();
}
private static DiscoveryNode newNode(int nodeId, Map<String, String> attributes, Set<DiscoveryNode.Role> roles) {
return new DiscoveryNode("name_" + nodeId, "node_" + nodeId, buildNewFakeTransportAddress(), attributes, roles,
Version.CURRENT);
}
private enum NodeSelector {
LOCAL("_local") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
return Collections.singleton(nodes.getLocalNodeId());
}
}, ELECTED_MASTER("_master") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
return Collections.singleton(nodes.getMasterNodeId());
}
}, MASTER_ELIGIBLE(DiscoveryNode.Role.MASTER.getRoleName() + ":true") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
Set<String> ids = new HashSet<>();
nodes.getMasterNodes().keysIt().forEachRemaining(ids::add);
return ids;
}
}, DATA(DiscoveryNode.Role.DATA.getRoleName() + ":true") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
Set<String> ids = new HashSet<>();
nodes.getDataNodes().keysIt().forEachRemaining(ids::add);
return ids;
}
}, INGEST(DiscoveryNode.Role.INGEST.getRoleName() + ":true") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
Set<String> ids = new HashSet<>();
nodes.getIngestNodes().keysIt().forEachRemaining(ids::add);
return ids;
}
}, CUSTOM_ATTRIBUTE("attr:value") {
@Override
Set<String> matchingNodeIds(DiscoveryNodes nodes) {
Set<String> ids = new HashSet<>();
nodes.getNodes().valuesIt().forEachRemaining(node -> {
if ("value".equals(node.getAttributes().get("attr"))) {
ids.add(node.getId());
}
});
return ids;
}
};
private final String selector;
NodeSelector(String selector) {
this.selector = selector;
}
abstract Set<String> matchingNodeIds(DiscoveryNodes nodes);
}
public void testMaxMinNodeVersion() {
DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder();
discoBuilder.add(new DiscoveryNode("name_" + 1, "node_" + 1, buildNewFakeTransportAddress(), Collections.emptyMap(),
new HashSet<>(randomSubsetOf(Arrays.asList(DiscoveryNode.Role.values()))),
Version.fromString("5.1.0")));
discoBuilder.add(new DiscoveryNode("name_" + 2, "node_" + 2, buildNewFakeTransportAddress(), Collections.emptyMap(),
new HashSet<>(randomSubsetOf(Arrays.asList(DiscoveryNode.Role.values()))),
Version.fromString("6.3.0")));
discoBuilder.add(new DiscoveryNode("name_" + 3, "node_" + 3, buildNewFakeTransportAddress(), Collections.emptyMap(),
new HashSet<>(randomSubsetOf(Arrays.asList(DiscoveryNode.Role.values()))),
Version.fromString("1.1.0")));
discoBuilder.localNodeId("name_1");
discoBuilder.masterNodeId("name_2");
DiscoveryNodes build = discoBuilder.build();
assertEquals( Version.fromString("6.3.0"), build.getMaxNodeVersion());
assertEquals( Version.fromString("1.1.0"), build.getMinNodeVersion());
}
}