Include details in ElasticsearchRestHealthIndicator

This commit adds more information to the ElasticSearch REST
health indicator.

When the ES instance responds with an error HTTP status,
the health details now include the actual status code and reason phrase.
When the ES instance returns a HTTP 200 response, the entire response
map is used as health details.

See gh-15366
pull/15472/head
Filip Hrisafov 6 years ago committed by Brian Clozel
parent 31284cd3df
commit 1ec2bbf54f

@ -18,8 +18,10 @@ package org.springframework.boot.actuate.elasticsearch;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.elasticsearch.client.Request; import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response; import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
@ -36,6 +38,7 @@ import org.springframework.util.StreamUtils;
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Brian Clozel * @author Brian Clozel
* @author Filip Hrisafov
* @since 2.1.1 * @since 2.1.1
*/ */
public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator { public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
@ -56,8 +59,11 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
protected void doHealthCheck(Health.Builder builder) throws Exception { protected void doHealthCheck(Health.Builder builder) throws Exception {
Response response = this.client Response response = this.client
.performRequest(new Request("GET", "/_cluster/health/")); .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.down();
builder.withDetail("statusCode", statusLine.getStatusCode());
builder.withDetail("reasonPhrase", statusLine.getReasonPhrase());
return; return;
} }
try (InputStream inputStream = response.getEntity().getContent()) { try (InputStream inputStream = response.getEntity().getContent()) {
@ -67,12 +73,16 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
} }
private void doHealthCheck(Health.Builder builder, String json) { private void doHealthCheck(Health.Builder builder, String json) {
String status = (String) this.jsonParser.parseMap(json).get("status"); Map<String, Object> response = this.jsonParser.parseMap(json);
String status = (String) response.get("status");
if (RED_STATUS.equals(status)) { if (RED_STATUS.equals(status)) {
builder.outOfService(); builder.outOfService();
return;
} }
builder.up(); else {
builder.up();
}
builder.withDetails(response);
} }
} }

@ -26,9 +26,11 @@ import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status; import org.springframework.boot.actuate.health.Status;
import static org.assertj.core.api.Assertions.assertThat; 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.ArgumentMatchers.any;
import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.when; import static org.mockito.BDDMockito.when;
@ -37,6 +39,7 @@ import static org.mockito.BDDMockito.when;
* Tests for {@link ElasticsearchRestHealthIndicator}. * Tests for {@link ElasticsearchRestHealthIndicator}.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Filip Hrisafov
*/ */
public class ElasticsearchRestHealthIndicatorTest { public class ElasticsearchRestHealthIndicatorTest {
@ -59,8 +62,48 @@ public class ElasticsearchRestHealthIndicatorTest {
when(response.getEntity()).thenReturn(httpEntity); when(response.getEntity()).thenReturn(httpEntity);
when(this.restClient.performRequest(any(Request.class))).thenReturn(response); when(this.restClient.performRequest(any(Request.class))).thenReturn(response);
assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) Health health = this.elasticsearchRestHealthIndicator.health();
.isEqualTo(Status.UP); 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 @Test
@ -68,8 +111,10 @@ public class ElasticsearchRestHealthIndicatorTest {
when(this.restClient.performRequest(any(Request.class))) when(this.restClient.performRequest(any(Request.class)))
.thenThrow(new IOException("Couldn't connect")); .thenThrow(new IOException("Couldn't connect"));
assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) Health health = this.elasticsearchRestHealthIndicator.health();
.isEqualTo(Status.DOWN); assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails())
.contains(entry("error", "java.io.IOException: Couldn't connect"));
} }
@Test @Test
@ -79,11 +124,14 @@ public class ElasticsearchRestHealthIndicatorTest {
StatusLine statusLine = mock(StatusLine.class); StatusLine statusLine = mock(StatusLine.class);
when(statusLine.getStatusCode()).thenReturn(500); when(statusLine.getStatusCode()).thenReturn(500);
when(statusLine.getReasonPhrase()).thenReturn("Internal server error");
when(response.getStatusLine()).thenReturn(statusLine); when(response.getStatusLine()).thenReturn(statusLine);
when(this.restClient.performRequest(any(Request.class))).thenReturn(response); when(this.restClient.performRequest(any(Request.class))).thenReturn(response);
assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) Health health = this.elasticsearchRestHealthIndicator.health();
.isEqualTo(Status.DOWN); assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).contains(entry("statusCode", 500),
entry("reasonPhrase", "Internal server error"));
} }
@Test @Test
@ -100,8 +148,18 @@ public class ElasticsearchRestHealthIndicatorTest {
when(response.getEntity()).thenReturn(httpEntity); when(response.getEntity()).thenReturn(httpEntity);
when(this.restClient.performRequest(any(Request.class))).thenReturn(response); when(this.restClient.performRequest(any(Request.class))).thenReturn(response);
assertThat(this.elasticsearchRestHealthIndicator.health().getStatus()) Health health = this.elasticsearchRestHealthIndicator.health();
.isEqualTo(Status.OUT_OF_SERVICE); 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) { private String createJsonResult(int responseCode, String status) {

Loading…
Cancel
Save