|
30 | 30 | import org.elasticsearch.client.Client; |
31 | 31 | import org.elasticsearch.client.Requests; |
32 | 32 | import org.elasticsearch.cluster.ClusterState; |
| 33 | +import org.elasticsearch.cluster.coordination.CoordinationMetaData; |
33 | 34 | import org.elasticsearch.cluster.metadata.IndexGraveyard; |
34 | 35 | import org.elasticsearch.cluster.metadata.IndexMetaData; |
| 36 | +import org.elasticsearch.cluster.metadata.Manifest; |
35 | 37 | import org.elasticsearch.cluster.metadata.MappingMetaData; |
36 | 38 | import org.elasticsearch.cluster.metadata.MetaData; |
37 | 39 | import org.elasticsearch.cluster.routing.IndexRoutingTable; |
38 | 40 | import org.elasticsearch.cluster.routing.IndexShardRoutingTable; |
39 | 41 | import org.elasticsearch.cluster.routing.RoutingTable; |
40 | 42 | import org.elasticsearch.cluster.routing.ShardRoutingState; |
41 | 43 | import org.elasticsearch.cluster.routing.UnassignedInfo; |
| 44 | +import org.elasticsearch.cluster.service.ClusterService; |
42 | 45 | import org.elasticsearch.common.CheckedConsumer; |
43 | 46 | import org.elasticsearch.common.Priority; |
44 | 47 | import org.elasticsearch.common.settings.Settings; |
45 | 48 | import org.elasticsearch.common.xcontent.XContentFactory; |
46 | 49 | import org.elasticsearch.common.xcontent.XContentType; |
| 50 | +import org.elasticsearch.core.internal.io.IOUtils; |
47 | 51 | import org.elasticsearch.env.NodeEnvironment; |
48 | 52 | import org.elasticsearch.index.mapper.MapperParsingException; |
49 | 53 | import org.elasticsearch.indices.IndexClosedException; |
|
54 | 58 | import org.elasticsearch.test.InternalTestCluster.RestartCallback; |
55 | 59 |
|
56 | 60 | import java.io.IOException; |
| 61 | +import java.nio.file.Files; |
| 62 | +import java.nio.file.Path; |
57 | 63 | import java.util.List; |
58 | 64 | import java.util.Map; |
59 | 65 | import java.util.concurrent.TimeUnit; |
|
66 | 72 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; |
67 | 73 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; |
68 | 74 | import static org.hamcrest.Matchers.containsString; |
| 75 | +import static org.hamcrest.Matchers.empty; |
69 | 76 | import static org.hamcrest.Matchers.equalTo; |
70 | 77 | import static org.hamcrest.Matchers.greaterThan; |
71 | 78 | import static org.hamcrest.Matchers.notNullValue; |
@@ -509,6 +516,42 @@ public void testArchiveBrokenClusterSettings() throws Exception { |
509 | 516 | assertHitCount(client().prepareSearch().setQuery(matchAllQuery()).get(), 1L); |
510 | 517 | } |
511 | 518 |
|
| 519 | + public void testHalfDeletedIndexImport() throws Exception { |
| 520 | + // It's possible for a 6.x node to add a tombstone for an index but not actually delete the index metadata from disk since that |
| 521 | + // deletion is slightly deferred and may race against the node being shut down; if you upgrade to 7.x when in this state then the |
| 522 | + // node won't start. |
| 523 | + |
| 524 | + internalCluster().startNode(); |
| 525 | + createIndex("test", Settings.builder() |
| 526 | + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) |
| 527 | + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) |
| 528 | + .build()); |
| 529 | + ensureGreen("test"); |
| 530 | + |
| 531 | + final MetaData metaData = internalCluster().getInstance(ClusterService.class).state().metaData(); |
| 532 | + final Path[] paths = internalCluster().getInstance(NodeEnvironment.class).nodeDataPaths(); |
| 533 | + writeBrokenMeta(metaStateService -> { |
| 534 | + metaStateService.writeGlobalState("test", MetaData.builder(metaData) |
| 535 | + // we remove the manifest file, resetting the term and making this look like an upgrade from 6.x, so must also reset the |
| 536 | + // term in the coordination metadata |
| 537 | + .coordinationMetaData(CoordinationMetaData.builder(metaData.coordinationMetaData()).term(0L).build()) |
| 538 | + // add a tombstone but do not delete the index metadata from disk |
| 539 | + .putCustom(IndexGraveyard.TYPE, IndexGraveyard.builder().addTombstone(metaData.index("test").getIndex()).build()).build()); |
| 540 | + for (final Path path : paths) { |
| 541 | + try (Stream<Path> stateFiles = Files.list(path.resolve(MetaDataStateFormat.STATE_DIR_NAME))) { |
| 542 | + for (final Path manifestPath : stateFiles |
| 543 | + .filter(p -> p.getFileName().toString().startsWith(Manifest.FORMAT.getPrefix())).collect(Collectors.toList())) { |
| 544 | + IOUtils.rm(manifestPath); |
| 545 | + } |
| 546 | + } |
| 547 | + } |
| 548 | + }); |
| 549 | + |
| 550 | + ensureGreen(); |
| 551 | + |
| 552 | + assertBusy(() -> assertThat(internalCluster().getInstance(NodeEnvironment.class).availableIndexFolders(), empty())); |
| 553 | + } |
| 554 | + |
512 | 555 | private void writeBrokenMeta(CheckedConsumer<MetaStateService, IOException> writer) throws Exception { |
513 | 556 | Map<String, MetaStateService> metaStateServices = Stream.of(internalCluster().getNodeNames()) |
514 | 557 | .collect(Collectors.toMap(Function.identity(), nodeName -> internalCluster().getInstance(MetaStateService.class, nodeName))); |
|
0 commit comments