/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.Arrays;
import java.util.Map;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchTimeoutException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.ClusterAdminClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
/**
* Test for {@link ElasticsearchHealthIndicator}.
*
* @author Andy Wilkinson
*/
public class ElasticsearchHealthIndicatorTests {
@Mock
private Client client;
@Mock
private AdminClient admin;
@Mock
private ClusterAdminClient cluster;
private ElasticsearchHealthIndicator indicator;
private ElasticsearchHealthIndicatorProperties properties = new ElasticsearchHealthIndicatorProperties();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
given(this.client.admin()).willReturn(this.admin);
given(this.admin.cluster()).willReturn(this.cluster);
this.indicator = new ElasticsearchHealthIndicator(this.client, this.properties);
}
@Test
public void defaultConfigurationQueriesAllIndicesWith100msTimeout() {
TestActionFuture responseFuture = new TestActionFuture();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor
.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(responseFuture.getTimeout).isEqualTo(100L);
assertThat(requestCaptor.getValue().indices()).contains("_all");
assertThat(health.getStatus()).isEqualTo(Status.UP);
}
@Test
public void certainIndices() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor
.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
this.properties.getIndices()
.addAll(Arrays.asList("test-index-1", "test-index-2"));
Health health = this.indicator.health();
assertThat(requestCaptor.getValue().indices()).contains("test-index-1",
"test-index-2");
assertThat(health.getStatus()).isEqualTo(Status.UP);
}
@Test
public void customTimeout() {
TestActionFuture responseFuture = new TestActionFuture();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor
.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
this.properties.setResponseTimeout(1000L);
this.indicator.health();
assertThat(responseFuture.getTimeout).isEqualTo(1000L);
}
@Test
public void healthDetails() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse());
given(this.cluster.health(any(ClusterHealthRequest.class)))
.willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> details = health.getDetails();
assertDetail(details, "clusterName", "test-cluster");
assertDetail(details, "activeShards", 1);
assertDetail(details, "relocatingShards", 2);
assertDetail(details, "activePrimaryShards", 3);
assertDetail(details, "initializingShards", 4);
assertDetail(details, "unassignedShards", 5);
assertDetail(details, "numberOfNodes", 6);
assertDetail(details, "numberOfDataNodes", 7);
}
@Test
public void redResponseMapsToDown() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse(ClusterHealthStatus.RED));
given(this.cluster.health(any(ClusterHealthRequest.class)))
.willReturn(responseFuture);
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.DOWN);
}
@Test
public void yellowResponseMapsToUp() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture
.onResponse(new StubClusterHealthResponse(ClusterHealthStatus.YELLOW));
given(this.cluster.health(any(ClusterHealthRequest.class)))
.willReturn(responseFuture);
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
}
@Test
public void responseTimeout() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
given(this.cluster.health(any(ClusterHealthRequest.class)))
.willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat((String) health.getDetails().get("error"))
.contains(ElasticsearchTimeoutException.class.getName());
}
@SuppressWarnings("unchecked")
private <T> void assertDetail(Map<String, Object> details, String detail, T value) {
assertThat((T) details.get(detail)).isEqualTo(value);
}
private final static class StubClusterHealthResponse extends ClusterHealthResponse {
private final ClusterHealthStatus status;
private StubClusterHealthResponse() {
this(ClusterHealthStatus.GREEN);
}
private StubClusterHealthResponse(ClusterHealthStatus status) {
super("test-cluster", new String[0],
new ClusterState(null, 0, null, null, RoutingTable.builder().build(),
DiscoveryNodes.builder().build(),
ClusterBlocks.builder().build(), null, false));
this.status = status;
}
@Override
public int getActiveShards() {
return 1;
}
@Override
public int getRelocatingShards() {
return 2;
}
@Override
public int getActivePrimaryShards() {
return 3;
}
@Override
public int getInitializingShards() {
return 4;
}
@Override
public int getUnassignedShards() {
return 5;
}
@Override
public int getNumberOfNodes() {
return 6;
}
@Override
public int getNumberOfDataNodes() {
return 7;
}
@Override
public ClusterHealthStatus getStatus() {
return this.status;
}
}
private static class TestActionFuture
extends PlainActionFuture<ClusterHealthResponse> {
private long getTimeout = -1L;
@Override
public ClusterHealthResponse actionGet(long timeoutMillis)
throws ElasticsearchException {
this.getTimeout = timeoutMillis;
return super.actionGet(timeoutMillis);
}
}
}