|
25 | 25 | import org.elasticsearch.common.settings.Settings;
|
26 | 26 | import org.elasticsearch.core.Strings;
|
27 | 27 | import org.elasticsearch.core.Tuple;
|
| 28 | +import org.elasticsearch.health.GetHealthAction; |
| 29 | +import org.elasticsearch.health.node.FetchHealthInfoCacheAction; |
28 | 30 | import org.elasticsearch.reservedstate.action.ReservedClusterSettingsAction;
|
29 | 31 | import org.elasticsearch.test.ESIntegTestCase;
|
30 | 32 | import org.junit.Before;
|
|
37 | 39 | import java.util.concurrent.ExecutionException;
|
38 | 40 | import java.util.concurrent.TimeUnit;
|
39 | 41 | import java.util.concurrent.atomic.AtomicLong;
|
| 42 | +import java.util.stream.Stream; |
40 | 43 |
|
| 44 | +import static org.elasticsearch.health.HealthStatus.YELLOW; |
41 | 45 | import static org.elasticsearch.indices.recovery.RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING;
|
42 | 46 | import static org.elasticsearch.node.Node.INITIAL_STATE_TIMEOUT_SETTING;
|
| 47 | +import static org.elasticsearch.test.NodeRoles.dataNode; |
43 | 48 | import static org.elasticsearch.test.NodeRoles.dataOnlyNode;
|
44 | 49 | import static org.elasticsearch.test.NodeRoles.masterNode;
|
45 | 50 | import static org.hamcrest.Matchers.allOf;
|
@@ -498,6 +503,82 @@ public void testSettingsAppliedOnMasterReElection() throws Exception {
|
498 | 503 | assertClusterStateSaveOK(savedClusterState.v1(), savedClusterState.v2(), "43mb");
|
499 | 504 | }
|
500 | 505 |
|
| 506 | + public void testHealthIndicatorWithSingleNode() throws Exception { |
| 507 | + internalCluster().setBootstrapMasterNodeIndex(0); |
| 508 | + logger.info("--> start the node"); |
| 509 | + String nodeName = internalCluster().startNode(); |
| 510 | + FileSettingsService masterFileSettingsService = internalCluster().getInstance(FileSettingsService.class, nodeName); |
| 511 | + assertBusy(() -> assertTrue(masterFileSettingsService.watching())); |
| 512 | + |
| 513 | + ensureStableCluster(1); |
| 514 | + |
| 515 | + testHealthIndicatorOnError(nodeName, nodeName); |
| 516 | + } |
| 517 | + |
| 518 | + public void testHealthIndicatorWithSeparateHealthNode() throws Exception { |
| 519 | + internalCluster().setBootstrapMasterNodeIndex(0); |
| 520 | + logger.info("--> start a data node to act as the health node"); |
| 521 | + String healthNode = internalCluster().startNode( |
| 522 | + Settings.builder().put(dataOnlyNode()).put("discovery.initial_state_timeout", "1s") |
| 523 | + ); |
| 524 | + |
| 525 | + logger.info("--> start master node"); |
| 526 | + final String masterNode = internalCluster().startMasterOnlyNode( |
| 527 | + Settings.builder().put(INITIAL_STATE_TIMEOUT_SETTING.getKey(), "0s").build() |
| 528 | + ); |
| 529 | + FileSettingsService masterFileSettingsService = internalCluster().getInstance(FileSettingsService.class, masterNode); |
| 530 | + assertBusy(() -> assertTrue(masterFileSettingsService.watching())); |
| 531 | + |
| 532 | + ensureStableCluster(2); |
| 533 | + |
| 534 | + testHealthIndicatorOnError(masterNode, healthNode); |
| 535 | + } |
| 536 | + |
| 537 | + /** |
| 538 | + * {@code masterNode} and {@code healthNode} can be the same node. |
| 539 | + */ |
| 540 | + private void testHealthIndicatorOnError(String masterNode, String healthNode) throws Exception { |
| 541 | + logger.info("--> ensure all is well before the error"); |
| 542 | + assertBusy(() -> { |
| 543 | + FetchHealthInfoCacheAction.Response healthNodeResponse = client().execute( |
| 544 | + FetchHealthInfoCacheAction.INSTANCE, |
| 545 | + new FetchHealthInfoCacheAction.Request() |
| 546 | + ).get(); |
| 547 | + assertEquals(0, healthNodeResponse.getHealthInfo().fileSettingsHealthInfo().failureStreak()); |
| 548 | + }); |
| 549 | + |
| 550 | + logger.info("--> induce an error and wait for it to be processed"); |
| 551 | + var savedClusterState = setupClusterStateListenerForError(masterNode); |
| 552 | + writeJSONFile(masterNode, testErrorJSON, logger, versionCounter.incrementAndGet()); |
| 553 | + boolean awaitSuccessful = savedClusterState.v1().await(20, TimeUnit.SECONDS); |
| 554 | + assertTrue(awaitSuccessful); |
| 555 | + |
| 556 | + logger.info("--> ensure the health node also reports it"); |
| 557 | + assertBusy(() -> { |
| 558 | + FetchHealthInfoCacheAction.Response healthNodeResponse = client().execute( |
| 559 | + FetchHealthInfoCacheAction.INSTANCE, |
| 560 | + new FetchHealthInfoCacheAction.Request() |
| 561 | + ).get(); |
| 562 | + assertEquals( |
| 563 | + "Cached info on health node should report one failure", |
| 564 | + 1, |
| 565 | + healthNodeResponse.getHealthInfo().fileSettingsHealthInfo().failureStreak() |
| 566 | + ); |
| 567 | + |
| 568 | + for (var node : Stream.of(masterNode, healthNode).distinct().toList()) { |
| 569 | + GetHealthAction.Response getHealthResponse = client(node).execute( |
| 570 | + GetHealthAction.INSTANCE, |
| 571 | + new GetHealthAction.Request(false, 123) |
| 572 | + ).get(); |
| 573 | + assertEquals( |
| 574 | + "Health should be yellow on node " + node, |
| 575 | + YELLOW, |
| 576 | + getHealthResponse.findIndicator(FileSettingsService.FileSettingsHealthIndicatorService.NAME).status() |
| 577 | + ); |
| 578 | + } |
| 579 | + }); |
| 580 | + } |
| 581 | + |
501 | 582 | private void assertHasErrors(AtomicLong waitForMetadataVersion, String expectedError) {
|
502 | 583 | var errorMetadata = getErrorMetadata(waitForMetadataVersion);
|
503 | 584 | assertThat(errorMetadata, is(notNullValue()));
|
|
0 commit comments