Skip to content

Commit 2aad499

Browse files
authored
Prevent version upgrade during remote migration (opensearch-project#13185)
Signed-off-by: Lakshya Taragi <lakshya.taragi@gmail.com>
1 parent efa06fe commit 2aad499

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java

+17-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
package org.opensearch.cluster.coordination;
3333

34+
import org.apache.logging.log4j.LogManager;
3435
import org.apache.logging.log4j.Logger;
3536
import org.opensearch.Version;
3637
import org.opensearch.cluster.ClusterState;
@@ -57,6 +58,7 @@
5758
import java.util.Collections;
5859
import java.util.HashMap;
5960
import java.util.List;
61+
import java.util.Locale;
6062
import java.util.Map;
6163
import java.util.Optional;
6264
import java.util.Set;
@@ -66,6 +68,7 @@
6668
import static org.opensearch.cluster.decommission.DecommissionHelper.nodeCommissioned;
6769
import static org.opensearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK;
6870
import static org.opensearch.node.remotestore.RemoteStoreNodeService.CompatibilityMode;
71+
import static org.opensearch.node.remotestore.RemoteStoreNodeService.CompatibilityMode.MIXED;
6972
import static org.opensearch.node.remotestore.RemoteStoreNodeService.CompatibilityMode.STRICT;
7073
import static org.opensearch.node.remotestore.RemoteStoreNodeService.REMOTE_STORE_COMPATIBILITY_MODE_SETTING;
7174

@@ -78,7 +81,7 @@ public class JoinTaskExecutor implements ClusterStateTaskExecutor<JoinTaskExecut
7881

7982
private final AllocationService allocationService;
8083

81-
private final Logger logger;
84+
private static Logger logger = LogManager.getLogger(JoinTaskExecutor.class);
8285
private final RerouteService rerouteService;
8386

8487
private final RemoteStoreNodeService remoteStoreNodeService;
@@ -142,7 +145,7 @@ public JoinTaskExecutor(
142145
RemoteStoreNodeService remoteStoreNodeService
143146
) {
144147
this.allocationService = allocationService;
145-
this.logger = logger;
148+
JoinTaskExecutor.logger = logger;
146149
this.rerouteService = rerouteService;
147150
this.remoteStoreNodeService = remoteStoreNodeService;
148151
}
@@ -521,7 +524,18 @@ private static void ensureRemoteStoreNodesCompatibility(DiscoveryNode joiningNod
521524
}
522525
}
523526
} else {
524-
if (remoteStoreCompatibilityMode == CompatibilityMode.MIXED) {
527+
if (MIXED.equals(remoteStoreCompatibilityMode)) {
528+
if (joiningNode.getVersion().after(currentNodes.getMaxNodeVersion())) {
529+
String reason = String.format(
530+
Locale.ROOT,
531+
"remote migration : a node [%s] of higher version [%s] is not allowed to join a cluster with maximum version [%s]",
532+
joiningNode,
533+
joiningNode.getVersion(),
534+
currentNodes.getMaxNodeVersion()
535+
);
536+
logger.warn(reason);
537+
throw new IllegalStateException(reason);
538+
}
525539
if (joiningNode.isRemoteStoreNode()) {
526540
Optional<DiscoveryNode> remoteDN = existingNodes.stream().filter(DiscoveryNode::isRemoteStoreNode).findFirst();
527541
remoteDN.ifPresent(discoveryNode -> ensureRemoteStoreNodesCompatibility(joiningNode, discoveryNode));

server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java

+59
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY;
7777
import static org.opensearch.node.remotestore.RemoteStoreNodeService.MIGRATION_DIRECTION_SETTING;
7878
import static org.opensearch.node.remotestore.RemoteStoreNodeService.REMOTE_STORE_COMPATIBILITY_MODE_SETTING;
79+
import static org.opensearch.test.VersionUtils.allOpenSearchVersions;
7980
import static org.opensearch.test.VersionUtils.allVersions;
8081
import static org.opensearch.test.VersionUtils.maxCompatibleVersion;
8182
import static org.opensearch.test.VersionUtils.randomCompatibleVersion;
@@ -885,6 +886,64 @@ public void testUpdatesClusterStateWithMultiNodeClusterAndSameRepository() throw
885886
validateRepositoryMetadata(result.resultingState, clusterManagerNode, 2);
886887
}
887888

889+
public void testNodeJoinInMixedMode() {
890+
Settings nodeSettings = Settings.builder().put(REMOTE_STORE_MIGRATION_EXPERIMENTAL, "true").build();
891+
FeatureFlags.initializeFeatureFlags(nodeSettings);
892+
893+
List<Version> versions = allOpenSearchVersions();
894+
assert versions.size() >= 2 : "test requires at least two open search versions";
895+
Version baseVersion = versions.get(versions.size() - 2);
896+
Version higherVersion = versions.get(versions.size() - 1);
897+
898+
DiscoveryNode currentNode1 = new DiscoveryNode(UUIDs.base64UUID(), buildNewFakeTransportAddress(), baseVersion);
899+
DiscoveryNode currentNode2 = new DiscoveryNode(UUIDs.base64UUID(), buildNewFakeTransportAddress(), baseVersion);
900+
DiscoveryNodes currentNodes = DiscoveryNodes.builder()
901+
.add(currentNode1)
902+
.localNodeId(currentNode1.getId())
903+
.add(currentNode2)
904+
.localNodeId(currentNode2.getId())
905+
.build();
906+
907+
Settings mixedModeCompatibilitySettings = Settings.builder()
908+
.put(REMOTE_STORE_COMPATIBILITY_MODE_SETTING.getKey(), RemoteStoreNodeService.CompatibilityMode.MIXED)
909+
.build();
910+
911+
Metadata metadata = Metadata.builder().persistentSettings(mixedModeCompatibilitySettings).build();
912+
913+
// joining node of a higher version than the current nodes
914+
DiscoveryNode joiningNode1 = new DiscoveryNode(
915+
randomAlphaOfLength(10),
916+
randomAlphaOfLength(10),
917+
buildNewFakeTransportAddress(),
918+
remoteStoreNodeAttributes(SEGMENT_REPO, TRANSLOG_REPO),
919+
Collections.singleton(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE),
920+
higherVersion
921+
);
922+
final IllegalStateException exception = expectThrows(
923+
IllegalStateException.class,
924+
() -> JoinTaskExecutor.ensureNodesCompatibility(joiningNode1, currentNodes, metadata)
925+
);
926+
String reason = String.format(
927+
Locale.ROOT,
928+
"remote migration : a node [%s] of higher version [%s] is not allowed to join a cluster with maximum version [%s]",
929+
joiningNode1,
930+
joiningNode1.getVersion(),
931+
currentNodes.getMaxNodeVersion()
932+
);
933+
assertEquals(reason, exception.getMessage());
934+
935+
// joining node of the same version as the current nodes
936+
DiscoveryNode joiningNode2 = new DiscoveryNode(
937+
randomAlphaOfLength(10),
938+
randomAlphaOfLength(10),
939+
buildNewFakeTransportAddress(),
940+
remoteStoreNodeAttributes(SEGMENT_REPO, TRANSLOG_REPO),
941+
Collections.singleton(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE),
942+
baseVersion
943+
);
944+
JoinTaskExecutor.ensureNodesCompatibility(joiningNode2, currentNodes, metadata);
945+
}
946+
888947
private void validateRepositoryMetadata(ClusterState updatedState, DiscoveryNode existingNode, int expectedRepositories)
889948
throws Exception {
890949

0 commit comments

Comments
 (0)