diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java index 963faa73bd..1024dded02 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java @@ -18,8 +18,10 @@ package org.springframework.boot.actuate.elasticsearch; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.Map; import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; @@ -36,6 +38,7 @@ import org.springframework.util.StreamUtils; * * @author Artsiom Yudovin * @author Brian Clozel + * @author Filip Hrisafov * @since 2.1.1 */ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator { @@ -56,8 +59,11 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator { protected void doHealthCheck(Health.Builder builder) throws Exception { Response response = this.client .performRequest(new Request("GET", "/_cluster/health/")); - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + StatusLine statusLine = response.getStatusLine(); + if (statusLine.getStatusCode() != HttpStatus.SC_OK) { builder.down(); + builder.withDetail("statusCode", statusLine.getStatusCode()); + builder.withDetail("reasonPhrase", statusLine.getReasonPhrase()); return; } try (InputStream inputStream = response.getEntity().getContent()) { @@ -67,12 +73,16 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator { } private void doHealthCheck(Health.Builder builder, String json) { - String status = (String) this.jsonParser.parseMap(json).get("status"); + Map response = this.jsonParser.parseMap(json); + String status = (String) response.get("status"); if (RED_STATUS.equals(status)) { builder.outOfService(); - return; } - builder.up(); + else { + builder.up(); + } + + builder.withDetails(response); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicatorTest.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicatorTest.java index 67caab025a..7f3bd7427d 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicatorTest.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicatorTest.java @@ -26,9 +26,11 @@ import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; import org.junit.Test; +import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.when; @@ -37,6 +39,7 @@ import static org.mockito.BDDMockito.when; * Tests for {@link ElasticsearchRestHealthIndicator}. * * @author Artsiom Yudovin + * @author Filip Hrisafov */ public class ElasticsearchRestHealthIndicatorTest { @@ -59,8 +62,48 @@ public class ElasticsearchRestHealthIndicatorTest { when(response.getEntity()).thenReturn(httpEntity); when(this.restClient.performRequest(any(Request.class))).thenReturn(response); - assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) - .isEqualTo(Status.UP); + Health health = this.elasticsearchRestHealthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + + assertThat(health.getDetails()).contains(entry("cluster_name", "elasticsearch"), + entry("status", "green"), entry("timed_out", false), + entry("number_of_nodes", 1), entry("number_of_data_nodes", 1), + entry("active_primary_shards", 0), entry("active_shards", 0), + entry("relocating_shards", 0), entry("initializing_shards", 0), + entry("unassigned_shards", 0), entry("delayed_unassigned_shards", 0), + entry("number_of_pending_tasks", 0), + entry("number_of_in_flight_fetch", 0), + entry("task_max_waiting_in_queue_millis", 0), + entry("active_shards_percent_as_number", 100.0)); + } + + @Test + public void elasticsearchWithYellowStatusIsUp() throws IOException { + BasicHttpEntity httpEntity = new BasicHttpEntity(); + httpEntity.setContent( + new ByteArrayInputStream(createJsonResult(200, "yellow").getBytes())); + + Response response = mock(Response.class); + StatusLine statusLine = mock(StatusLine.class); + + when(statusLine.getStatusCode()).thenReturn(200); + when(response.getStatusLine()).thenReturn(statusLine); + when(response.getEntity()).thenReturn(httpEntity); + when(this.restClient.performRequest(any(Request.class))).thenReturn(response); + + Health health = this.elasticsearchRestHealthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + + assertThat(health.getDetails()).contains(entry("cluster_name", "elasticsearch"), + entry("status", "yellow"), entry("timed_out", false), + entry("number_of_nodes", 1), entry("number_of_data_nodes", 1), + entry("active_primary_shards", 0), entry("active_shards", 0), + entry("relocating_shards", 0), entry("initializing_shards", 0), + entry("unassigned_shards", 0), entry("delayed_unassigned_shards", 0), + entry("number_of_pending_tasks", 0), + entry("number_of_in_flight_fetch", 0), + entry("task_max_waiting_in_queue_millis", 0), + entry("active_shards_percent_as_number", 100.0)); } @Test @@ -68,8 +111,10 @@ public class ElasticsearchRestHealthIndicatorTest { when(this.restClient.performRequest(any(Request.class))) .thenThrow(new IOException("Couldn't connect")); - assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) - .isEqualTo(Status.DOWN); + Health health = this.elasticsearchRestHealthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()) + .contains(entry("error", "java.io.IOException: Couldn't connect")); } @Test @@ -79,11 +124,14 @@ public class ElasticsearchRestHealthIndicatorTest { StatusLine statusLine = mock(StatusLine.class); when(statusLine.getStatusCode()).thenReturn(500); + when(statusLine.getReasonPhrase()).thenReturn("Internal server error"); when(response.getStatusLine()).thenReturn(statusLine); when(this.restClient.performRequest(any(Request.class))).thenReturn(response); - assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) - .isEqualTo(Status.DOWN); + Health health = this.elasticsearchRestHealthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).contains(entry("statusCode", 500), + entry("reasonPhrase", "Internal server error")); } @Test @@ -100,8 +148,18 @@ public class ElasticsearchRestHealthIndicatorTest { when(response.getEntity()).thenReturn(httpEntity); when(this.restClient.performRequest(any(Request.class))).thenReturn(response); - assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) - .isEqualTo(Status.OUT_OF_SERVICE); + Health health = this.elasticsearchRestHealthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.OUT_OF_SERVICE); + assertThat(health.getDetails()).contains(entry("cluster_name", "elasticsearch"), + entry("status", "red"), entry("timed_out", false), + entry("number_of_nodes", 1), entry("number_of_data_nodes", 1), + entry("active_primary_shards", 0), entry("active_shards", 0), + entry("relocating_shards", 0), entry("initializing_shards", 0), + entry("unassigned_shards", 0), entry("delayed_unassigned_shards", 0), + entry("number_of_pending_tasks", 0), + entry("number_of_in_flight_fetch", 0), + entry("task_max_waiting_in_queue_millis", 0), + entry("active_shards_percent_as_number", 100.0)); } private String createJsonResult(int responseCode, String status) {