package org.sonar.server.health;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
.setStatus(Health.Status.RED)
.addCause("Elasticsearch status is RED")
.build();
- private static final Health RED_HEALTH_UNAVAILABLE = newHealthCheckBuilder()
+ protected static final Health RED_HEALTH_UNAVAILABLE = newHealthCheckBuilder()
.setStatus(Health.Status.RED)
.addCause("Elasticsearch status is RED (unavailable)")
.build();
this.esClient = esClient;
}
- Health checkEsStatus() {
+ protected ClusterHealthResponse getEsClusterHealth() {
try {
- ClusterHealthStatus esStatus = esClient.clusterHealth(new ClusterHealthRequest()).getStatus();
- if (esStatus == null) {
- return RED_HEALTH_UNAVAILABLE;
- }
- switch (esStatus) {
- case GREEN:
- return Health.GREEN;
- case YELLOW:
- return YELLOW_HEALTH;
- case RED:
- return RED_HEALTH;
- default:
- throw new IllegalArgumentException("Unsupported Elasticsearch status " + esStatus);
- }
+ return esClient.clusterHealth(new ClusterHealthRequest());
} catch (Exception e) {
LOG.error("Failed to query ES status", e);
+ return null;
+ }
+ }
+
+ protected static Health extractStatusHealth(ClusterHealthResponse healthResponse) {
+ ClusterHealthStatus esStatus = healthResponse.getStatus();
+ if (esStatus == null) {
return RED_HEALTH_UNAVAILABLE;
}
+ switch (esStatus) {
+ case GREEN:
+ return Health.GREEN;
+ case YELLOW:
+ return YELLOW_HEALTH;
+ case RED:
+ return RED_HEALTH;
+ default:
+ throw new IllegalArgumentException("Unsupported Elasticsearch status " + esStatus);
+ }
}
+
}
package org.sonar.server.health;
import java.util.Set;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.sonar.process.cluster.health.NodeHealth;
import org.sonar.server.es.EsClient;
public class EsStatusClusterCheck extends EsStatusCheck implements ClusterHealthCheck {
+ private static final String MINIMUM_NODE_MESSAGE = "There should be at least three search nodes";
+ private static final int RECOMMENDED_MIN_NUMBER_OF_ES_NODES = 3;
public EsStatusClusterCheck(EsClient esClient) {
super(esClient);
@Override
public Health check(Set<NodeHealth> nodeHealths) {
- return checkEsStatus();
+ ClusterHealthResponse esClusterHealth = this.getEsClusterHealth();
+ if (esClusterHealth != null) {
+ Health minimumNodes = checkMinimumNodes(esClusterHealth);
+ Health clusterStatus = extractStatusHealth(esClusterHealth);
+ return HealthReducer.INSTANCE.apply(minimumNodes, clusterStatus);
+ }
+ return RED_HEALTH_UNAVAILABLE;
+ }
+
+ private static Health checkMinimumNodes(ClusterHealthResponse esClusterHealth) {
+ int nodeCount = esClusterHealth.getNumberOfNodes();
+ if (nodeCount < RECOMMENDED_MIN_NUMBER_OF_ES_NODES) {
+ return Health.newHealthCheckBuilder()
+ .setStatus(Health.Status.YELLOW)
+ .addCause(MINIMUM_NODE_MESSAGE)
+ .build();
+ }
+ return Health.GREEN;
}
}
*/
package org.sonar.server.health;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.sonar.server.es.EsClient;
/**
@Override
public Health check() {
- return super.checkEsStatus();
+ ClusterHealthResponse healthResponse = getEsClusterHealth();
+ return healthResponse != null ? extractStatusHealth(healthResponse) : RED_HEALTH_UNAVAILABLE;
}
}
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.when;
+import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
public class EsStatusClusterCheckTest {
public void check_ignores_NodeHealth_arg_and_returns_GREEN_without_cause_if_ES_cluster_status_is_GREEN() {
Set<NodeHealth> nodeHealths = ImmutableSet.of(newNodeHealth(NodeHealth.Status.YELLOW));
when(esClient.clusterHealth(any()).getStatus()).thenReturn(ClusterHealthStatus.GREEN);
+ when(esClient.clusterHealth(any()).getNumberOfNodes()).thenReturn(3);
Health health = underTest.check(nodeHealths);
assertThat(health).isEqualTo(Health.GREEN);
}
+ @Test
+ public void check_returns_YELLOW_with_cause_if_ES_cluster_has_less_than_three_nodes_but_status_is_green() {
+ Set<NodeHealth> nodeHealths = ImmutableSet.of(newNodeHealth(GREEN));
+ when(esClient.clusterHealth(any()).getStatus()).thenReturn(ClusterHealthStatus.GREEN);
+ when(esClient.clusterHealth(any()).getNumberOfNodes()).thenReturn(2);
+
+ Health health = underTest.check(nodeHealths);
+
+ assertThat(health.getStatus()).isEqualTo(Health.Status.YELLOW);
+ assertThat(health.getCauses()).containsOnly("There should be at least three search nodes");
+ }
+
+ @Test
+ public void check_returns_RED_with_cause_if_ES_cluster_has_less_than_three_nodes_and_status_is_RED() {
+ Set<NodeHealth> nodeHealths = ImmutableSet.of(newNodeHealth(GREEN));
+ when(esClient.clusterHealth(any()).getStatus()).thenReturn(ClusterHealthStatus.RED);
+ when(esClient.clusterHealth(any()).getNumberOfNodes()).thenReturn(2);
+
+ Health health = underTest.check(nodeHealths);
+
+ assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+ assertThat(health.getCauses()).contains("Elasticsearch status is RED", "There should be at least three search nodes");
+ }
+
+
private NodeHealth newNodeHealth(NodeHealth.Status status) {
return NodeHealth.newNodeHealthBuilder()
.setStatus(status)