Skip to content

Commit d14799f

Browse files
committed
Prevent merging nodes' data paths (#42665)
Today Elasticsearch does not prevent you from reconfiguring a node's `path.data` to point to data paths that previously belonged to more than one node. There's no good reason to be able to do this, and the consequences can be quietly disastrous. Furthermore, #42489 might result in a user trying to split up a previously-shared collection of data paths by hand and there's definitely scope for mixing the paths up across nodes when doing this. This change adds a check during startup to ensure that each data path belongs to the same node.
1 parent b5527b3 commit d14799f

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

server/src/main/java/org/elasticsearch/env/NodeEnvironment.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import java.util.ArrayList;
7171
import java.util.Arrays;
7272
import java.util.Collection;
73+
import java.util.Collections;
7374
import java.util.HashMap;
7475
import java.util.HashSet;
7576
import java.util.Iterator;
@@ -403,10 +404,25 @@ private void maybeLogHeapDetails() {
403404
private static NodeMetaData loadOrCreateNodeMetaData(Settings settings, Logger logger,
404405
NodePath... nodePaths) throws IOException {
405406
final Path[] paths = Arrays.stream(nodePaths).map(np -> np.path).toArray(Path[]::new);
407+
408+
final Set<String> nodeIds = new HashSet<>();
409+
for (final Path path : paths) {
410+
final NodeMetaData metaData = NodeMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, path);
411+
if (metaData != null) {
412+
nodeIds.add(metaData.nodeId());
413+
}
414+
}
415+
if (nodeIds.size() > 1) {
416+
throw new IllegalStateException(
417+
"data paths " + Arrays.toString(paths) + " belong to multiple nodes with IDs " + nodeIds);
418+
}
419+
406420
NodeMetaData metaData = NodeMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, paths);
407421
if (metaData == null) {
422+
assert nodeIds.isEmpty() : nodeIds;
408423
metaData = new NodeMetaData(generateNodeId(settings), Version.CURRENT);
409424
} else {
425+
assert nodeIds.equals(Collections.singleton(metaData.nodeId())) : nodeIds + " doesn't match " + metaData;
410426
metaData = metaData.upgradeToCurrentVersion();
411427
}
412428

server/src/test/java/org/elasticsearch/env/NodeEnvironmentIT.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
import org.elasticsearch.test.ESIntegTestCase;
2727
import org.elasticsearch.test.InternalTestCluster;
2828

29+
import java.io.IOException;
2930
import java.nio.file.Path;
31+
import java.util.ArrayList;
32+
import java.util.List;
3033

3134
import static org.hamcrest.Matchers.allOf;
3235
import static org.hamcrest.Matchers.containsString;
@@ -35,7 +38,7 @@
3538

3639
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0)
3740
public class NodeEnvironmentIT extends ESIntegTestCase {
38-
public void testStartFailureOnDataForNonDataNode() throws Exception {
41+
public void testStartFailureOnDataForNonDataNode() {
3942
final String indexName = "test-fail-on-data";
4043

4144
logger.info("--> starting one node");
@@ -123,4 +126,33 @@ public void testFailsToStartIfUpgradedTooFar() {
123126
assertThat(illegalStateException.getMessage(),
124127
allOf(startsWith("cannot upgrade a node from version ["), endsWith("] directly to version [" + Version.CURRENT + "]")));
125128
}
129+
130+
public void testFailsToStartOnDataPathsFromMultipleNodes() throws IOException {
131+
final List<String> nodes = internalCluster().startNodes(2);
132+
ensureStableCluster(2);
133+
134+
final List<String> node0DataPaths = Environment.PATH_DATA_SETTING.get(internalCluster().dataPathSettings(nodes.get(0)));
135+
final List<String> node1DataPaths = Environment.PATH_DATA_SETTING.get(internalCluster().dataPathSettings(nodes.get(1)));
136+
137+
final List<String> allDataPaths = new ArrayList<>(node0DataPaths);
138+
allDataPaths.addAll(node1DataPaths);
139+
140+
internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodes.get(1)));
141+
internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodes.get(0)));
142+
143+
final IllegalStateException illegalStateException = expectThrows(IllegalStateException.class,
144+
() -> internalCluster().startNode(Settings.builder().putList(Environment.PATH_DATA_SETTING.getKey(), allDataPaths)));
145+
146+
assertThat(illegalStateException.getMessage(), containsString("belong to multiple nodes with IDs"));
147+
148+
final List<String> node0DataPathsPlusOne = new ArrayList<>(node0DataPaths);
149+
node0DataPathsPlusOne.add(createTempDir().toString());
150+
internalCluster().startNode(Settings.builder().putList(Environment.PATH_DATA_SETTING.getKey(), node0DataPathsPlusOne));
151+
152+
final List<String> node1DataPathsPlusOne = new ArrayList<>(node1DataPaths);
153+
node1DataPathsPlusOne.add(createTempDir().toString());
154+
internalCluster().startNode(Settings.builder().putList(Environment.PATH_DATA_SETTING.getKey(), node1DataPathsPlusOne));
155+
156+
ensureStableCluster(2);
157+
}
126158
}

0 commit comments

Comments
 (0)