]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16250 Global Health Status should be yellow if there are less than 3 search...
authorZipeng WU <zipeng.wu@sonarsource.com>
Thu, 2 Jun 2022 12:22:41 +0000 (14:22 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 2 Jun 2022 20:03:19 +0000 (20:03 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusCheck.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusClusterCheck.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusNodeCheck.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusClusterCheckTest.java

index 3abb51837a5ad925ee465bb0d07a277134b20d30..28eae7cfb4ef3ae7b15a308ddd4648df536dd53a 100644 (file)
@@ -20,6 +20,7 @@
 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;
@@ -38,7 +39,7 @@ abstract class EsStatusCheck {
     .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();
@@ -49,25 +50,30 @@ abstract class EsStatusCheck {
     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);
+    }
   }
+
 }
index 5cfc998c7009e916acceea8f9553f01e0c1201f3..a0028c655b8197c516f9bc0b8e92afd1b96bf8da 100644 (file)
 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);
@@ -31,7 +34,24 @@ public class EsStatusClusterCheck extends EsStatusCheck implements ClusterHealth
 
   @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;
   }
 
 }
index 955cb2d4cfc3a9988954938436936eb86058d693..438e33fa5693fd57d13fbc401daa504884e99d58 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.health;
 
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
 import org.sonar.server.es.EsClient;
 
 /**
@@ -32,6 +33,7 @@ public class EsStatusNodeCheck extends EsStatusCheck implements NodeHealthCheck
 
   @Override
   public Health check() {
-    return super.checkEsStatus();
+    ClusterHealthResponse healthResponse = getEsClusterHealth();
+    return healthResponse != null ? extractStatusHealth(healthResponse) : RED_HEALTH_UNAVAILABLE;
   }
 }
index bc959def3ce26212e5b65440a5a3737b28a3d57b..fb99c2b1f22605337ead86a222100310d4d789a3 100644 (file)
@@ -34,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 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 {
 
@@ -56,12 +57,38 @@ 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)