|
18 | 18 | */ |
19 | 19 | package org.elasticsearch.env; |
20 | 20 |
|
| 21 | +import junit.framework.AssertionFailedError; |
| 22 | +import org.apache.logging.log4j.Level; |
| 23 | +import org.apache.logging.log4j.LogManager; |
| 24 | +import org.apache.logging.log4j.Logger; |
| 25 | +import org.apache.logging.log4j.LoggingException; |
| 26 | +import org.apache.logging.log4j.core.Appender; |
| 27 | +import org.apache.logging.log4j.core.ErrorHandler; |
| 28 | +import org.apache.logging.log4j.core.LogEvent; |
| 29 | +import org.apache.logging.log4j.core.appender.AbstractAppender; |
21 | 30 | import org.apache.lucene.index.SegmentInfos; |
| 31 | +import org.elasticsearch.common.logging.Loggers; |
22 | 32 | import org.elasticsearch.common.util.set.Sets; |
23 | 33 | import org.elasticsearch.core.internal.io.IOUtils; |
24 | 34 | import org.apache.lucene.util.LuceneTestCase; |
|
31 | 41 | import org.elasticsearch.index.Index; |
32 | 42 | import org.elasticsearch.index.IndexSettings; |
33 | 43 | import org.elasticsearch.index.shard.ShardId; |
| 44 | +import org.elasticsearch.node.Node; |
34 | 45 | import org.elasticsearch.test.ESTestCase; |
35 | 46 | import org.elasticsearch.test.IndexSettingsModule; |
36 | 47 |
|
|
53 | 64 | import static org.hamcrest.Matchers.containsString; |
54 | 65 | import static org.hamcrest.Matchers.empty; |
55 | 66 | import static org.hamcrest.Matchers.not; |
| 67 | +import static org.hamcrest.Matchers.startsWith; |
56 | 68 |
|
57 | 69 | @LuceneTestCase.SuppressFileSystems("ExtrasFS") // TODO: fix test to allow extras |
58 | 70 | public class NodeEnvironmentTests extends ESTestCase { |
@@ -475,6 +487,159 @@ public void testExistingTempFiles() throws IOException { |
475 | 487 | } |
476 | 488 | } |
477 | 489 |
|
| 490 | + // backported from 7.0, but in 6.x this only prints warnings. We keep the original test as is to ease further backports and ensure |
| 491 | + // that log messages convert into exceptions. |
| 492 | + public void testEnsureNoShardDataOrIndexMetaData6x() throws IOException, IllegalAccessException { |
| 493 | + // Convert warn log messages into exceptions and call original test case. |
| 494 | + Appender appender = new AbstractAppender("convertToException", null, null, false) { |
| 495 | + @Override |
| 496 | + public void append(LogEvent event) { |
| 497 | + if (event.getLevel() == Level.WARN |
| 498 | + && event.getMessage().getFormattedMessage() |
| 499 | + .endsWith(", this should be cleaned up (will refuse to start in 7.0). Create a backup copy before removing.")) { |
| 500 | + assertWarnings(event.getMessage().getFormattedMessage()); |
| 501 | + throw new LoggingException(new IllegalStateException(event.getMessage().getFormattedMessage())); |
| 502 | + } |
| 503 | + } |
| 504 | + }; |
| 505 | + appender.setHandler(new ErrorHandler() { |
| 506 | + @Override |
| 507 | + public void error(String msg) { |
| 508 | + } |
| 509 | + |
| 510 | + @Override |
| 511 | + public void error(String msg, Throwable t) { |
| 512 | + } |
| 513 | + |
| 514 | + @Override |
| 515 | + public void error(String msg, LogEvent event, Throwable t) { |
| 516 | + } |
| 517 | + }); |
| 518 | + appender.start(); |
| 519 | + Logger nodeEnvironmentLogger = LogManager.getLogger(NodeEnvironment.class.getName().replace("org.elasticsearch.", |
| 520 | + "org.elasticsearch.deprecation.")); |
| 521 | + Loggers.addAppender(nodeEnvironmentLogger, appender); |
| 522 | + try { |
| 523 | + testEnsureNoShardDataOrIndexMetaData(); |
| 524 | + } finally { |
| 525 | + Loggers.removeAppender(nodeEnvironmentLogger, appender); |
| 526 | + appender.stop(); |
| 527 | + } |
| 528 | + } |
| 529 | + |
| 530 | + private static <T extends Throwable> T expectLoggingThrows(Class<T> expectedType, |
| 531 | + String noExceptionMessage, |
| 532 | + ThrowingRunnable runnable) { |
| 533 | + try { |
| 534 | + runnable.run(); |
| 535 | + } catch (Throwable e) { |
| 536 | + if (e instanceof LoggingException) { |
| 537 | + e = e.getCause(); |
| 538 | + } |
| 539 | + if (expectedType.isInstance(e)) { |
| 540 | + return expectedType.cast(e); |
| 541 | + } |
| 542 | + AssertionFailedError assertion = |
| 543 | + new AssertionFailedError("Unexpected exception type, expected " + expectedType.getSimpleName() + " but got " + e); |
| 544 | + assertion.initCause(e); |
| 545 | + throw assertion; |
| 546 | + } |
| 547 | + throw new AssertionFailedError(noExceptionMessage); |
| 548 | + } |
| 549 | + |
| 550 | + // exact 7.0 copy (except private on purpose to disable test and expectLoggingThrows used) |
| 551 | + private void testEnsureNoShardDataOrIndexMetaData() throws IOException { |
| 552 | + Settings settings = buildEnvSettings(Settings.EMPTY); |
| 553 | + Index index = new Index("test", "testUUID"); |
| 554 | + |
| 555 | + // build settings using same path.data as original but with node.data=false and node.master=false |
| 556 | + Settings noDataNoMasterSettings = Settings.builder() |
| 557 | + .put(settings) |
| 558 | + .put(Node.NODE_DATA_SETTING.getKey(), false) |
| 559 | + .put(Node.NODE_MASTER_SETTING.getKey(), false) |
| 560 | + .build(); |
| 561 | + |
| 562 | + // test that we can create data=false and master=false with no meta information |
| 563 | + newNodeEnvironment(noDataNoMasterSettings).close(); |
| 564 | + |
| 565 | + Path indexPath; |
| 566 | + try (NodeEnvironment env = newNodeEnvironment(settings)) { |
| 567 | + for (Path path : env.indexPaths(index)) { |
| 568 | + Files.createDirectories(path.resolve(MetaDataStateFormat.STATE_DIR_NAME)); |
| 569 | + } |
| 570 | + indexPath = env.indexPaths(index)[0]; |
| 571 | + } |
| 572 | + |
| 573 | + verifyFailsOnMetaData(noDataNoMasterSettings, indexPath); |
| 574 | + |
| 575 | + // build settings using same path.data as original but with node.data=false |
| 576 | + Settings noDataSettings = Settings.builder() |
| 577 | + .put(settings) |
| 578 | + .put(Node.NODE_DATA_SETTING.getKey(), false).build(); |
| 579 | + |
| 580 | + String shardDataDirName = Integer.toString(randomInt(10)); |
| 581 | + |
| 582 | + // test that we can create data=false env with only meta information. Also create shard data for following asserts |
| 583 | + try (NodeEnvironment env = newNodeEnvironment(noDataSettings)) { |
| 584 | + for (Path path : env.indexPaths(index)) { |
| 585 | + Files.createDirectories(path.resolve(shardDataDirName)); |
| 586 | + } |
| 587 | + } |
| 588 | + |
| 589 | + verifyFailsOnShardData(noDataSettings, indexPath, shardDataDirName); |
| 590 | + |
| 591 | + // assert that we get the stricter message on meta-data when both conditions fail |
| 592 | + verifyFailsOnMetaData(noDataNoMasterSettings, indexPath); |
| 593 | + |
| 594 | + // build settings using same path.data as original but with node.master=false |
| 595 | + Settings noMasterSettings = Settings.builder() |
| 596 | + .put(settings) |
| 597 | + .put(Node.NODE_MASTER_SETTING.getKey(), false) |
| 598 | + .build(); |
| 599 | + |
| 600 | + // test that we can create master=false env regardless of data. |
| 601 | + newNodeEnvironment(noMasterSettings).close(); |
| 602 | + |
| 603 | + // test that we can create data=true, master=true env. Also remove state dir to leave only shard data for following asserts |
| 604 | + try (NodeEnvironment env = newNodeEnvironment(settings)) { |
| 605 | + for (Path path : env.indexPaths(index)) { |
| 606 | + Files.delete(path.resolve(MetaDataStateFormat.STATE_DIR_NAME)); |
| 607 | + } |
| 608 | + } |
| 609 | + |
| 610 | + // assert that we fail on shard data even without the metadata dir. |
| 611 | + verifyFailsOnShardData(noDataSettings, indexPath, shardDataDirName); |
| 612 | + verifyFailsOnShardData(noDataNoMasterSettings, indexPath, shardDataDirName); |
| 613 | + } |
| 614 | + |
| 615 | + private void verifyFailsOnShardData(Settings settings, Path indexPath, String shardDataDirName) { |
| 616 | + IllegalStateException ex = expectLoggingThrows(IllegalStateException.class, |
| 617 | + "Must fail creating NodeEnvironment on a data path that has shard data if node.data=false", |
| 618 | + () -> newNodeEnvironment(settings).close()); |
| 619 | + |
| 620 | + assertThat(ex.getMessage(), |
| 621 | + containsString(indexPath.resolve(shardDataDirName).toAbsolutePath().toString())); |
| 622 | + assertThat(ex.getMessage(), |
| 623 | + startsWith("Node is started with " |
| 624 | + + Node.NODE_DATA_SETTING.getKey() |
| 625 | + + "=false, but has shard data")); |
| 626 | + } |
| 627 | + |
| 628 | + private void verifyFailsOnMetaData(Settings settings, Path indexPath) { |
| 629 | + IllegalStateException ex = expectLoggingThrows(IllegalStateException.class, |
| 630 | + "Must fail creating NodeEnvironment on a data path that has index meta-data if node.data=false and node.master=false", |
| 631 | + () -> newNodeEnvironment(settings).close()); |
| 632 | + |
| 633 | + assertThat(ex.getMessage(), |
| 634 | + containsString(indexPath.resolve(MetaDataStateFormat.STATE_DIR_NAME).toAbsolutePath().toString())); |
| 635 | + assertThat(ex.getMessage(), |
| 636 | + startsWith("Node is started with " |
| 637 | + + Node.NODE_DATA_SETTING.getKey() |
| 638 | + + "=false and " |
| 639 | + + Node.NODE_MASTER_SETTING.getKey() |
| 640 | + + "=false, but has index metadata")); |
| 641 | + } |
| 642 | + |
478 | 643 | /** Converts an array of Strings to an array of Paths, adding an additional child if specified */ |
479 | 644 | private Path[] stringsToPaths(String[] strings, String additional) { |
480 | 645 | Path[] locations = new Path[strings.length]; |
|
0 commit comments