|
19 | 19 | package org.elasticsearch.cluster.coordination;
|
20 | 20 |
|
21 | 21 | import com.carrotsearch.randomizedtesting.RandomizedContext;
|
22 |
| - |
23 | 22 | import org.apache.logging.log4j.CloseableThreadContext;
|
24 | 23 | import org.apache.logging.log4j.LogManager;
|
25 | 24 | import org.apache.logging.log4j.Logger;
|
|
53 | 52 | import org.elasticsearch.common.settings.Settings.Builder;
|
54 | 53 | import org.elasticsearch.common.transport.TransportAddress;
|
55 | 54 | import org.elasticsearch.common.unit.TimeValue;
|
| 55 | +import org.elasticsearch.common.util.set.Sets; |
56 | 56 | import org.elasticsearch.discovery.zen.PublishClusterStateStats;
|
57 | 57 | import org.elasticsearch.discovery.zen.UnicastHostsProvider.HostsResolver;
|
58 | 58 | import org.elasticsearch.env.NodeEnvironment;
|
|
93 | 93 | import static org.elasticsearch.cluster.coordination.CoordinationStateTests.clusterState;
|
94 | 94 | import static org.elasticsearch.cluster.coordination.CoordinationStateTests.setValue;
|
95 | 95 | import static org.elasticsearch.cluster.coordination.CoordinationStateTests.value;
|
96 |
| -import static org.elasticsearch.cluster.coordination.Coordinator.PUBLISH_TIMEOUT_SETTING; |
97 | 96 | import static org.elasticsearch.cluster.coordination.Coordinator.Mode.CANDIDATE;
|
98 | 97 | import static org.elasticsearch.cluster.coordination.Coordinator.Mode.FOLLOWER;
|
99 | 98 | import static org.elasticsearch.cluster.coordination.Coordinator.Mode.LEADER;
|
| 99 | +import static org.elasticsearch.cluster.coordination.Coordinator.PUBLISH_TIMEOUT_SETTING; |
100 | 100 | import static org.elasticsearch.cluster.coordination.CoordinatorTests.Cluster.DEFAULT_DELAY_VARIABILITY;
|
101 | 101 | import static org.elasticsearch.cluster.coordination.ElectionSchedulerFactory.ELECTION_BACK_OFF_TIME_SETTING;
|
102 | 102 | import static org.elasticsearch.cluster.coordination.ElectionSchedulerFactory.ELECTION_DURATION_SETTING;
|
|
117 | 117 | import static org.elasticsearch.transport.TransportService.NOOP_TRANSPORT_INTERCEPTOR;
|
118 | 118 | import static org.hamcrest.Matchers.containsString;
|
119 | 119 | import static org.hamcrest.Matchers.empty;
|
120 |
| -import static org.hamcrest.Matchers.endsWith; |
121 | 120 | import static org.hamcrest.Matchers.equalTo;
|
122 | 121 | import static org.hamcrest.Matchers.greaterThan;
|
123 | 122 | import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
@@ -745,7 +744,7 @@ public void testSettingInitialConfigurationTriggersElection() {
|
745 | 744 | assertThat(nodeId + " should have found all peers", foundPeers, hasSize(cluster.size()));
|
746 | 745 | }
|
747 | 746 |
|
748 |
| - final ClusterNode bootstrapNode = cluster.getAnyNode(); |
| 747 | + final ClusterNode bootstrapNode = cluster.getAnyBootstrappableNode(); |
749 | 748 | bootstrapNode.applyInitialConfiguration();
|
750 | 749 | assertTrue(bootstrapNode.getId() + " has been bootstrapped", bootstrapNode.coordinator.isInitialConfigurationSet());
|
751 | 750 |
|
@@ -775,20 +774,30 @@ public void testCannotSetInitialConfigurationTwice() {
|
775 | 774 | public void testCannotSetInitialConfigurationWithoutQuorum() {
|
776 | 775 | final Cluster cluster = new Cluster(randomIntBetween(1, 5));
|
777 | 776 | final Coordinator coordinator = cluster.getAnyNode().coordinator;
|
778 |
| - final VotingConfiguration unknownNodeConfiguration = new VotingConfiguration(Collections.singleton("unknown-node")); |
| 777 | + final VotingConfiguration unknownNodeConfiguration = new VotingConfiguration( |
| 778 | + Sets.newHashSet(coordinator.getLocalNode().getId(), "unknown-node")); |
779 | 779 | final String exceptionMessage = expectThrows(CoordinationStateRejectedException.class,
|
780 | 780 | () -> coordinator.setInitialConfiguration(unknownNodeConfiguration)).getMessage();
|
781 | 781 | assertThat(exceptionMessage,
|
782 | 782 | startsWith("not enough nodes discovered to form a quorum in the initial configuration [knownNodes=["));
|
783 |
| - assertThat(exceptionMessage, |
784 |
| - endsWith("], VotingConfiguration{unknown-node}]")); |
| 783 | + assertThat(exceptionMessage, containsString("unknown-node")); |
785 | 784 | assertThat(exceptionMessage, containsString(coordinator.getLocalNode().toString()));
|
786 | 785 |
|
787 | 786 | // This is VERY BAD: setting a _different_ initial configuration. Yet it works if the first attempt will never be a quorum.
|
788 | 787 | assertTrue(coordinator.setInitialConfiguration(new VotingConfiguration(Collections.singleton(coordinator.getLocalNode().getId()))));
|
789 | 788 | cluster.stabilise();
|
790 | 789 | }
|
791 | 790 |
|
| 791 | + public void testCannotSetInitialConfigurationWithoutLocalNode() { |
| 792 | + final Cluster cluster = new Cluster(randomIntBetween(1, 5)); |
| 793 | + final Coordinator coordinator = cluster.getAnyNode().coordinator; |
| 794 | + final VotingConfiguration unknownNodeConfiguration = new VotingConfiguration(Sets.newHashSet("unknown-node")); |
| 795 | + final String exceptionMessage = expectThrows(CoordinationStateRejectedException.class, |
| 796 | + () -> coordinator.setInitialConfiguration(unknownNodeConfiguration)).getMessage(); |
| 797 | + assertThat(exceptionMessage, |
| 798 | + equalTo("local node is not part of initial configuration")); |
| 799 | + } |
| 800 | + |
792 | 801 | public void testDiffBasedPublishing() {
|
793 | 802 | final Cluster cluster = new Cluster(randomIntBetween(1, 5));
|
794 | 803 | cluster.runRandomly();
|
@@ -1331,7 +1340,7 @@ void bootstrapIfNecessary() {
|
1331 | 1340 | assertThat("setting initial configuration may fail with disconnected nodes", disconnectedNodes, empty());
|
1332 | 1341 | assertThat("setting initial configuration may fail with blackholed nodes", blackholedNodes, empty());
|
1333 | 1342 | runFor(defaultMillis(DISCOVERY_FIND_PEERS_INTERVAL_SETTING) * 2, "discovery prior to setting initial configuration");
|
1334 |
| - final ClusterNode bootstrapNode = getAnyMasterEligibleNode(); |
| 1343 | + final ClusterNode bootstrapNode = getAnyBootstrappableNode(); |
1335 | 1344 | bootstrapNode.applyInitialConfiguration();
|
1336 | 1345 | } else {
|
1337 | 1346 | logger.info("setting initial configuration not required");
|
@@ -1402,8 +1411,10 @@ boolean nodeExists(DiscoveryNode node) {
|
1402 | 1411 | return clusterNodes.stream().anyMatch(cn -> cn.getLocalNode().equals(node));
|
1403 | 1412 | }
|
1404 | 1413 |
|
1405 |
| - ClusterNode getAnyMasterEligibleNode() { |
1406 |
| - return randomFrom(clusterNodes.stream().filter(n -> n.getLocalNode().isMasterNode()).collect(Collectors.toList())); |
| 1414 | + ClusterNode getAnyBootstrappableNode() { |
| 1415 | + return randomFrom(clusterNodes.stream().filter(n -> n.getLocalNode().isMasterNode()) |
| 1416 | + .filter(n -> initialConfiguration.getNodeIds().contains(n.getLocalNode().getId())) |
| 1417 | + .collect(Collectors.toList())); |
1407 | 1418 | }
|
1408 | 1419 |
|
1409 | 1420 | ClusterNode getAnyNode() {
|
@@ -1737,8 +1748,14 @@ void applyInitialConfiguration() {
|
1737 | 1748 | Stream.generate(() -> BOOTSTRAP_PLACEHOLDER_PREFIX + UUIDs.randomBase64UUID(random()))
|
1738 | 1749 | .limit((Math.max(initialConfiguration.getNodeIds().size(), 2) - 1) / 2)
|
1739 | 1750 | .forEach(nodeIdsWithPlaceholders::add);
|
1740 |
| - final VotingConfiguration configurationWithPlaceholders = new VotingConfiguration(new HashSet<>( |
1741 |
| - randomSubsetOf(initialConfiguration.getNodeIds().size(), nodeIdsWithPlaceholders))); |
| 1751 | + final Set<String> nodeIds = new HashSet<>( |
| 1752 | + randomSubsetOf(initialConfiguration.getNodeIds().size(), nodeIdsWithPlaceholders)); |
| 1753 | + // initial configuration should not have a place holder for local node |
| 1754 | + if (initialConfiguration.getNodeIds().contains(localNode.getId()) && nodeIds.contains(localNode.getId()) == false) { |
| 1755 | + nodeIds.remove(nodeIds.iterator().next()); |
| 1756 | + nodeIds.add(localNode.getId()); |
| 1757 | + } |
| 1758 | + final VotingConfiguration configurationWithPlaceholders = new VotingConfiguration(nodeIds); |
1742 | 1759 | try {
|
1743 | 1760 | coordinator.setInitialConfiguration(configurationWithPlaceholders);
|
1744 | 1761 | logger.info("successfully set initial configuration to {}", configurationWithPlaceholders);
|
|
0 commit comments