|  | @@ -76,6 +76,9 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |      private Path translogPath;
 | 
	
		
			
				|  |  |      private Path indexPath;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private static final Pattern NUM_CORRUPT_DOCS_PATTERN =
 | 
	
		
			
				|  |  | +        Pattern.compile("Corrupted Lucene index segments found -\\s+(?<docs>\\d+) documents will be lost.");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      @Before
 | 
	
		
			
				|  |  |      public void setup() throws IOException {
 | 
	
		
			
				|  |  |          shardId = new ShardId("index0", "_na_", 0);
 | 
	
	
		
			
				|  | @@ -154,11 +157,13 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |          final boolean corruptSegments = randomBoolean();
 | 
	
		
			
				|  |  |          CorruptionUtils.corruptIndex(random(), indexPath, corruptSegments);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // test corrupted shard
 | 
	
		
			
				|  |  | -        final IndexShard corruptedShard = reopenIndexShard(true);
 | 
	
		
			
				|  |  | -        allowShardFailures();
 | 
	
		
			
				|  |  | -        expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
 | 
	
		
			
				|  |  | -        closeShards(corruptedShard);
 | 
	
		
			
				|  |  | +        if (randomBoolean()) {
 | 
	
		
			
				|  |  | +            // test corrupted shard and add corruption marker
 | 
	
		
			
				|  |  | +            final IndexShard corruptedShard = reopenIndexShard(true);
 | 
	
		
			
				|  |  | +            allowShardFailures();
 | 
	
		
			
				|  |  | +            expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
 | 
	
		
			
				|  |  | +            closeShards(corruptedShard);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          final RemoveCorruptedShardDataCommand command = new RemoveCorruptedShardDataCommand();
 | 
	
		
			
				|  |  |          final MockTerminal t = new MockTerminal();
 | 
	
	
		
			
				|  | @@ -196,8 +201,7 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              final Set<String> shardDocUIDs = getShardDocUIDs(newShard);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            final Pattern pattern = Pattern.compile("Corrupted Lucene index segments found -\\s+(?<docs>\\d+) documents will be lost.");
 | 
	
		
			
				|  |  | -            final Matcher matcher = pattern.matcher(output);
 | 
	
		
			
				|  |  | +            final Matcher matcher = NUM_CORRUPT_DOCS_PATTERN.matcher(output);
 | 
	
		
			
				|  |  |              assertThat(matcher.find(), equalTo(true));
 | 
	
		
			
				|  |  |              final int expectedNumDocs = numDocs - Integer.parseInt(matcher.group("docs"));
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -272,12 +276,13 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          CorruptionUtils.corruptIndex(random(), indexPath, false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // test corrupted shard
 | 
	
		
			
				|  |  | -        final IndexShard corruptedShard = reopenIndexShard(true);
 | 
	
		
			
				|  |  | -        allowShardFailures();
 | 
	
		
			
				|  |  | -        expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
 | 
	
		
			
				|  |  | -        closeShards(corruptedShard);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        if (randomBoolean()) {
 | 
	
		
			
				|  |  | +            // test corrupted shard and add corruption marker
 | 
	
		
			
				|  |  | +            final IndexShard corruptedShard = reopenIndexShard(true);
 | 
	
		
			
				|  |  | +            allowShardFailures();
 | 
	
		
			
				|  |  | +            expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
 | 
	
		
			
				|  |  | +            closeShards(corruptedShard);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          TestTranslog.corruptRandomTranslogFile(logger, random(), Arrays.asList(translogPath));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          final RemoveCorruptedShardDataCommand command = new RemoveCorruptedShardDataCommand();
 | 
	
	
		
			
				|  | @@ -313,8 +318,7 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          final Set<String> shardDocUIDs = getShardDocUIDs(newShard);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        final Pattern pattern = Pattern.compile("Corrupted Lucene index segments found -\\s+(?<docs>\\d+) documents will be lost.");
 | 
	
		
			
				|  |  | -        final Matcher matcher = pattern.matcher(output);
 | 
	
		
			
				|  |  | +        final Matcher matcher = NUM_CORRUPT_DOCS_PATTERN.matcher(output);
 | 
	
		
			
				|  |  |          assertThat(matcher.find(), equalTo(true));
 | 
	
		
			
				|  |  |          final int expectedNumDocs = numDocsToKeep - Integer.parseInt(matcher.group("docs"));
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -347,6 +351,62 @@ public class RemoveCorruptedShardDataCommandTests extends IndexShardTestCase {
 | 
	
		
			
				|  |  |              shardPath -> assertThat(shardPath.resolveIndex(), equalTo(indexPath)));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    public void testCleanWithCorruptionMarker() throws Exception {
 | 
	
		
			
				|  |  | +        // index some docs in several segments
 | 
	
		
			
				|  |  | +        final int numDocs = indexDocs(indexShard, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        indexShard.store().markStoreCorrupted(null);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        closeShards(indexShard);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        allowShardFailures();
 | 
	
		
			
				|  |  | +        final IndexShard corruptedShard = reopenIndexShard(true);
 | 
	
		
			
				|  |  | +        expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
 | 
	
		
			
				|  |  | +        closeShards(corruptedShard);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final RemoveCorruptedShardDataCommand command = new RemoveCorruptedShardDataCommand();
 | 
	
		
			
				|  |  | +        final MockTerminal t = new MockTerminal();
 | 
	
		
			
				|  |  | +        final OptionParser parser = command.getParser();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final OptionSet options = parser.parse("-d", translogPath.toString());
 | 
	
		
			
				|  |  | +        // run command with dry-run
 | 
	
		
			
				|  |  | +        t.addTextInput("n"); // mean dry run
 | 
	
		
			
				|  |  | +        t.addTextInput("n"); // mean dry run
 | 
	
		
			
				|  |  | +        t.setVerbosity(Terminal.Verbosity.VERBOSE);
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            command.execute(t, options, environment);
 | 
	
		
			
				|  |  | +            fail();
 | 
	
		
			
				|  |  | +        } catch (ElasticsearchException e) {
 | 
	
		
			
				|  |  | +            assertThat(e.getMessage(), containsString("aborted by user"));
 | 
	
		
			
				|  |  | +            assertThat(t.getOutput(), containsString("Continue and remove corrupted data from the shard ?"));
 | 
	
		
			
				|  |  | +            assertThat(t.getOutput(), containsString("Lucene index is marked corrupted, but no corruption detected"));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        logger.info("--> output:\n{}", t.getOutput());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // run command without dry-run
 | 
	
		
			
				|  |  | +        t.reset();
 | 
	
		
			
				|  |  | +        t.addTextInput("y");
 | 
	
		
			
				|  |  | +        t.addTextInput("y");
 | 
	
		
			
				|  |  | +        command.execute(t, options, environment);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final String output = t.getOutput();
 | 
	
		
			
				|  |  | +        logger.info("--> output:\n{}", output);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        failOnShardFailures();
 | 
	
		
			
				|  |  | +        final IndexShard newShard = newStartedShard(p -> reopenIndexShard(false), true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final Set<String> shardDocUIDs = getShardDocUIDs(newShard);
 | 
	
		
			
				|  |  | +        assertEquals(numDocs, shardDocUIDs.size());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        assertThat(t.getOutput(), containsString("This shard has been marked as corrupted but no corruption can now be detected."));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final Matcher matcher = NUM_CORRUPT_DOCS_PATTERN.matcher(output);
 | 
	
		
			
				|  |  | +        assertFalse(matcher.find());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        closeShards(newShard);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private IndexShard reopenIndexShard(boolean corrupted) throws IOException {
 | 
	
		
			
				|  |  |          // open shard with the same location
 | 
	
		
			
				|  |  |          final ShardRouting shardRouting = ShardRoutingHelper.initWithSameId(indexShard.routingEntry(),
 |