Skip to content

Commit 10e5821

Browse files
authored
Validate cluster UUID when joining Zen1 cluster (#41063)
Today we fail to join a Zen2 cluster if the cluster UUID does not match our own, but we do not perform the same validation when joining a Zen1 cluster. This means that a Zen2 node will pass join validation and be added to a Zen1 cluster but will reject all cluster states from the master. Relates #37775
1 parent 8ee84f2 commit 10e5821

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

server/src/main/java/org/elasticsearch/cluster/coordination/JoinHelper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ public ClusterTasksResult<JoinTaskExecutor.Task> execute(ClusterState currentSta
153153
transportService.registerRequestHandler(MembershipAction.DISCOVERY_JOIN_VALIDATE_ACTION_NAME,
154154
ValidateJoinRequest::new, ThreadPool.Names.GENERIC,
155155
(request, channel, task) -> {
156+
final ClusterState localState = currentStateSupplier.get();
157+
if (localState.metaData().clusterUUIDCommitted() &&
158+
localState.metaData().clusterUUID().equals(request.getState().metaData().clusterUUID()) == false) {
159+
throw new CoordinationStateRejectedException("mixed-version cluster join validation on cluster state" +
160+
" with a different cluster uuid " + request.getState().metaData().clusterUUID() +
161+
" than local cluster uuid " + localState.metaData().clusterUUID()
162+
+ ", rejecting");
163+
}
156164
joinValidators.forEach(action -> action.accept(transportService.getLocalNode(), request.getState()));
157165
channel.sendResponse(Empty.INSTANCE);
158166
});

server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,19 @@
2020

2121
import org.apache.logging.log4j.Level;
2222
import org.elasticsearch.Version;
23+
import org.elasticsearch.action.ActionListenerResponseHandler;
24+
import org.elasticsearch.action.support.PlainActionFuture;
25+
import org.elasticsearch.cluster.ClusterName;
26+
import org.elasticsearch.cluster.ClusterState;
2327
import org.elasticsearch.cluster.NotMasterException;
28+
import org.elasticsearch.cluster.metadata.MetaData;
2429
import org.elasticsearch.cluster.node.DiscoveryNode;
2530
import org.elasticsearch.common.settings.Settings;
31+
import org.elasticsearch.discovery.zen.MembershipAction;
2632
import org.elasticsearch.test.ESTestCase;
2733
import org.elasticsearch.test.transport.CapturingTransport;
2834
import org.elasticsearch.test.transport.CapturingTransport.CapturedRequest;
35+
import org.elasticsearch.test.transport.MockTransport;
2936
import org.elasticsearch.transport.RemoteTransportException;
3037
import org.elasticsearch.transport.TransportException;
3138
import org.elasticsearch.transport.TransportResponse;
@@ -35,6 +42,7 @@
3542
import java.util.Optional;
3643

3744
import static org.elasticsearch.node.Node.NODE_NAME_SETTING;
45+
import static org.hamcrest.Matchers.containsString;
3846
import static org.hamcrest.Matchers.equalTo;
3947
import static org.hamcrest.core.Is.is;
4048

@@ -131,4 +139,48 @@ public void testFailedJoinAttemptLogLevel() {
131139
new RemoteTransportException("caused by NotMasterException",
132140
new NotMasterException("test"))), is(Level.DEBUG));
133141
}
142+
143+
public void testZen1JoinValidationRejectsMismatchedClusterUUID() {
144+
assertJoinValidationRejectsMismatchedClusterUUID(MembershipAction.DISCOVERY_JOIN_VALIDATE_ACTION_NAME,
145+
"mixed-version cluster join validation on cluster state with a different cluster uuid");
146+
}
147+
148+
public void testJoinValidationRejectsMismatchedClusterUUID() {
149+
assertJoinValidationRejectsMismatchedClusterUUID(JoinHelper.VALIDATE_JOIN_ACTION_NAME,
150+
"join validation on cluster state with a different cluster uuid");
151+
}
152+
153+
private void assertJoinValidationRejectsMismatchedClusterUUID(String actionName, String expectedMessage) {
154+
DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue(
155+
Settings.builder().put(NODE_NAME_SETTING.getKey(), "node0").build(), random());
156+
MockTransport mockTransport = new MockTransport();
157+
DiscoveryNode localNode = new DiscoveryNode("node0", buildNewFakeTransportAddress(), Version.CURRENT);
158+
159+
final ClusterState localClusterState = ClusterState.builder(ClusterName.DEFAULT).metaData(MetaData.builder()
160+
.generateClusterUuidIfNeeded().clusterUUIDCommitted(true)).build();
161+
162+
TransportService transportService = mockTransport.createTransportService(Settings.EMPTY,
163+
deterministicTaskQueue.getThreadPool(), TransportService.NOOP_TRANSPORT_INTERCEPTOR,
164+
x -> localNode, null, Collections.emptySet());
165+
new JoinHelper(Settings.EMPTY, null, null, transportService, () -> 0L, () -> localClusterState,
166+
(joinRequest, joinCallback) -> { throw new AssertionError(); }, startJoinRequest -> { throw new AssertionError(); },
167+
Collections.emptyList()); // registers request handler
168+
transportService.start();
169+
transportService.acceptIncomingRequests();
170+
171+
final ClusterState otherClusterState = ClusterState.builder(ClusterName.DEFAULT).metaData(MetaData.builder()
172+
.generateClusterUuidIfNeeded()).build();
173+
174+
final PlainActionFuture<TransportResponse.Empty> future = new PlainActionFuture<>();
175+
transportService.sendRequest(localNode, actionName,
176+
new ValidateJoinRequest(otherClusterState),
177+
new ActionListenerResponseHandler<>(future, in -> TransportResponse.Empty.INSTANCE));
178+
deterministicTaskQueue.runAllTasks();
179+
180+
final CoordinationStateRejectedException coordinationStateRejectedException
181+
= expectThrows(CoordinationStateRejectedException.class, future::actionGet);
182+
assertThat(coordinationStateRejectedException.getMessage(), containsString(expectedMessage));
183+
assertThat(coordinationStateRejectedException.getMessage(), containsString(localClusterState.metaData().clusterUUID()));
184+
assertThat(coordinationStateRejectedException.getMessage(), containsString(otherClusterState.metaData().clusterUUID()));
185+
}
134186
}

0 commit comments

Comments
 (0)