]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19991 Don't depend on sonar-webserver-webapi in sonar-webserver-webapi-v2.
authorWojtek Wajerowicz <115081248+wojciech-wajerowicz-sonarsource@users.noreply.github.com>
Thu, 27 Jul 2023 13:10:52 +0000 (15:10 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 28 Jul 2023 20:03:15 +0000 (20:03 +0000)
66 files changed:
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/AppNodeClusterCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/CeStatusNodeCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthSubCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/DbConnectionNodeCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusClusterCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusNodeCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/HealthReducer.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/NodeHealthCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerSafemodeNodeCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerStatusNodeCheck.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessChecker.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessCheckerImpl.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImpl.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/AppNodeClusterCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/CeStatusNodeCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/DbConnectionNodeCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusClusterCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusNodeCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/HealthAssert.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerSafemodeNodeCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerStatusNodeCheckTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/LivenessCheckerImplTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImplTest.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/build.gradle
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DefaultLivenessController.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java
server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DefaultLivenessControllerTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/CeStatusNodeCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthSubCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/DbConnectionNodeCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusClusterCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusNodeCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/HealthCheckerImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/HealthReducer.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/NodeHealthCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerSafemodeNodeCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerStatusNodeCheck.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/HealthCheckerModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessActionSupport.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeHealthCheckerModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafemodeSystemWsModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SystemWsModule.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/CeStatusNodeCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/DbConnectionNodeCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusClusterCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusNodeCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/HealthAssert.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/HealthCheckerImplTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerSafemodeNodeCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerStatusNodeCheckTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/HealthCheckerModuleTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeHealthCheckerModuleTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java [deleted file]

diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/AppNodeClusterCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/AppNodeClusterCheck.java
new file mode 100644 (file)
index 0000000..ac7061e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.sonar.process.cluster.health.NodeDetails;
+import org.sonar.process.cluster.health.NodeHealth;
+import org.sonar.server.health.Health;
+
+import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
+import static org.sonar.process.cluster.health.NodeHealth.Status.RED;
+import static org.sonar.process.cluster.health.NodeHealth.Status.YELLOW;
+
+public class AppNodeClusterCheck implements ClusterHealthCheck {
+
+  @Override
+  public Health check(Set<NodeHealth> nodeHealths) {
+    Set<NodeHealth> appNodes = nodeHealths.stream()
+      .filter(s -> s.getDetails().getType() == NodeDetails.Type.APPLICATION)
+      .collect(Collectors.toSet());
+
+    return Arrays.stream(AppNodeClusterHealthSubChecks.values())
+      .map(s -> s.check(appNodes))
+      .reduce(Health.GREEN, HealthReducer::merge);
+  }
+
+  private enum AppNodeClusterHealthSubChecks implements ClusterHealthSubCheck {
+    NO_APPLICATION_NODE() {
+      @Override
+      public Health check(Set<NodeHealth> appNodes) {
+        int appNodeCount = appNodes.size();
+        if (appNodeCount == 0) {
+          return Health.builder()
+            .setStatus(Health.Status.RED)
+            .addCause("No application node")
+            .build();
+        }
+        return Health.GREEN;
+      }
+    },
+    MIN_APPLICATION_NODE_COUNT() {
+      @Override
+      public Health check(Set<NodeHealth> appNodes) {
+        int appNodeCount = appNodes.size();
+        if (appNodeCount == 1) {
+          return Health.builder()
+            .setStatus(Health.Status.YELLOW)
+            .addCause("There should be at least two application nodes")
+            .build();
+        }
+        return Health.GREEN;
+      }
+    },
+    REPORT_RED_OR_YELLOW_NODES() {
+      @Override
+      public Health check(Set<NodeHealth> appNodes) {
+        int appNodeCount = appNodes.size();
+        if (appNodeCount == 0) {
+          // skipping this check
+          return Health.GREEN;
+        }
+
+        long redNodesCount = withStatus(appNodes, RED).count();
+        long yellowNodesCount = withStatus(appNodes, YELLOW).count();
+        if (redNodesCount == 0 && yellowNodesCount == 0) {
+          return Health.GREEN;
+        }
+
+        Health.Builder builder = Health.builder();
+        if (redNodesCount == appNodeCount) {
+          return builder
+            .setStatus(Health.Status.RED)
+            .addCause("Status of all application nodes is RED")
+            .build();
+        } else if (redNodesCount > 0) {
+          builder.addCause("At least one application node is RED");
+        }
+        if (yellowNodesCount == appNodeCount) {
+          return builder
+            .setStatus(Health.Status.YELLOW)
+            .addCause("Status of all application nodes is YELLOW")
+            .build();
+        } else if (yellowNodesCount > 0) {
+          builder.addCause("At least one application node is YELLOW");
+        }
+        long greenNodesCount = withStatus(appNodes, GREEN).count();
+        builder.setStatus(greenNodesCount > 0 || yellowNodesCount > 0 ? Health.Status.YELLOW : Health.Status.RED);
+
+        return builder.build();
+      }
+    }
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/CeStatusNodeCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/CeStatusNodeCheck.java
new file mode 100644 (file)
index 0000000..aa32447
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.sonar.server.app.ProcessCommandWrapper;
+import org.sonar.server.health.Health;
+
+public class CeStatusNodeCheck implements NodeHealthCheck {
+  private static final Health RED_HEALTH = Health.builder()
+    .setStatus(Health.Status.RED)
+    .addCause("Compute Engine is not operational")
+    .build();
+
+  private final ProcessCommandWrapper processCommandWrapper;
+
+  public CeStatusNodeCheck(ProcessCommandWrapper processCommandWrapper) {
+    this.processCommandWrapper = processCommandWrapper;
+  }
+
+  @Override
+  public Health check() {
+    if (processCommandWrapper.isCeOperational()) {
+      return Health.GREEN;
+    }
+
+    return RED_HEALTH;
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthCheck.java
new file mode 100644 (file)
index 0000000..ab1884d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.Set;
+import org.sonar.process.cluster.health.NodeHealth;
+import org.sonar.server.health.Health;
+
+public interface ClusterHealthCheck {
+  Health check(Set<NodeHealth> nodeHealths);
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthSubCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/ClusterHealthSubCheck.java
new file mode 100644 (file)
index 0000000..fa172f0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import org.sonar.process.cluster.health.NodeHealth;
+
+interface ClusterHealthSubCheck extends ClusterHealthCheck {
+
+  default Stream<NodeHealth> withStatus(Set<NodeHealth> searchNodes, NodeHealth.Status... statuses) {
+    return searchNodes.stream()
+        .filter(t -> {
+          for (NodeHealth.Status status : statuses) {
+            if (status == t.getStatus()) {
+              return true;
+            }
+          }
+          return false;
+        });
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/DbConnectionNodeCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/DbConnectionNodeCheck.java
new file mode 100644 (file)
index 0000000..2510f65
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.IsAliveMapper;
+import org.sonar.server.health.Health;
+
+/**
+ * Checks Web Server can connect to the Database.
+ */
+public class DbConnectionNodeCheck implements NodeHealthCheck {
+  private static final Logger LOGGER = LoggerFactory.getLogger(DbConnectionNodeCheck.class);
+  private static final Health RED_HEALTH = Health.builder().setStatus(Health.Status.RED).addCause("Can't connect to DB").build();
+
+  private final DbClient dbClient;
+
+  public DbConnectionNodeCheck(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public Health check() {
+    if (isConnectedToDB()) {
+      return Health.GREEN;
+    }
+    return RED_HEALTH;
+  }
+
+  private boolean isConnectedToDB() {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      return dbSession.getMapper(IsAliveMapper.class).isAlive() == IsAliveMapper.IS_ALIVE_RETURNED_VALUE;
+    } catch (RuntimeException e) {
+      LOGGER.trace("DB connection is down: {}", e.getMessage(), e);
+      return false;
+    }
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusCheck.java
new file mode 100644 (file)
index 0000000..62aec91
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.health.Health;
+
+abstract class EsStatusCheck {
+  private static final Logger LOG = LoggerFactory.getLogger(EsStatusCheck.class);
+
+  private static final Health YELLOW_HEALTH = Health.builder()
+    .setStatus(Health.Status.YELLOW)
+    .addCause("Elasticsearch status is YELLOW")
+    .build();
+  private static final Health RED_HEALTH = Health.builder()
+    .setStatus(Health.Status.RED)
+    .addCause("Elasticsearch status is RED")
+    .build();
+  protected static final Health RED_HEALTH_UNAVAILABLE = Health.builder()
+    .setStatus(Health.Status.RED)
+    .addCause("Elasticsearch status is RED (unavailable)")
+    .build();
+
+  private final EsClient esClient;
+
+  EsStatusCheck(EsClient esClient) {
+    this.esClient = esClient;
+  }
+
+  protected ClusterHealthResponse getEsClusterHealth() {
+    try {
+      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);
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusClusterCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusClusterCheck.java
new file mode 100644 (file)
index 0000000..de8ac07
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.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;
+import org.sonar.server.health.Health;
+
+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) {
+    ClusterHealthResponse esClusterHealth = this.getEsClusterHealth();
+    if (esClusterHealth != null) {
+      Health minimumNodes = checkMinimumNodes(esClusterHealth);
+      Health clusterStatus = extractStatusHealth(esClusterHealth);
+      return HealthReducer.merge(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.builder()
+        .setStatus(Health.Status.YELLOW)
+        .addCause(MINIMUM_NODE_MESSAGE)
+        .build();
+    }
+    return Health.GREEN;
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusNodeCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/EsStatusNodeCheck.java
new file mode 100644 (file)
index 0000000..e03c74e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.health.Health;
+
+/**
+ * Checks the ElasticSearch cluster status.
+ */
+public class EsStatusNodeCheck extends EsStatusCheck implements NodeHealthCheck {
+
+  public EsStatusNodeCheck(EsClient esClient) {
+    super(esClient);
+  }
+
+  @Override
+  public Health check() {
+    ClusterHealthResponse healthResponse = getEsClusterHealth();
+    return healthResponse != null ? extractStatusHealth(healthResponse) : RED_HEALTH_UNAVAILABLE;
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/HealthReducer.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/HealthReducer.java
new file mode 100644 (file)
index 0000000..44b09d0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+
+import org.sonar.server.health.Health;
+
+public class HealthReducer {
+
+  private HealthReducer() {
+    // no public constructor
+  }
+
+  public static Health merge(Health left, Health right) {
+    Health.Builder builder = Health.builder()
+      .setStatus(worseOf(left.getStatus(), right.getStatus()));
+    left.getCauses().forEach(builder::addCause);
+    right.getCauses().forEach(builder::addCause);
+    return builder.build();
+  }
+
+  private static Health.Status worseOf(Health.Status left, Health.Status right) {
+    if (left == right) {
+      return left;
+    }
+    if (left.ordinal() > right.ordinal()) {
+      return left;
+    }
+    return right;
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/NodeHealthCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/NodeHealthCheck.java
new file mode 100644 (file)
index 0000000..10eafd6
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.sonar.server.health.Health;
+
+public interface NodeHealthCheck {
+  Health check();
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerSafemodeNodeCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerSafemodeNodeCheck.java
new file mode 100644 (file)
index 0000000..e4bb09d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.sonar.server.health.Health;
+
+/**
+ * Checks the running status of the WebServer when the WebServer is in safemode.
+ * Obviously, it statically returns a red health status.
+ */
+public class WebServerSafemodeNodeCheck implements NodeHealthCheck {
+
+  private static final Health RED_HEALTH = Health.builder()
+    .setStatus(Health.Status.RED)
+    .addCause("SonarQube webserver is not up")
+    .build();
+
+  @Override
+  public Health check() {
+    return RED_HEALTH;
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerStatusNodeCheck.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/health/WebServerStatusNodeCheck.java
new file mode 100644 (file)
index 0000000..40013cb
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.EnumSet;
+import org.sonar.server.app.RestartFlagHolder;
+import org.sonar.server.health.Health;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.platform.db.migration.DatabaseMigrationState;
+
+/**
+ * Checks the running status of the WebServer when it is not anymore in safemode.
+ */
+public class WebServerStatusNodeCheck implements NodeHealthCheck {
+  private static final EnumSet<DatabaseMigrationState.Status> VALID_DATABASEMIGRATION_STATUSES = EnumSet.of(
+    DatabaseMigrationState.Status.NONE, DatabaseMigrationState.Status.SUCCEEDED);
+
+  private final DatabaseMigrationState migrationState;
+  private final Platform platform;
+  private final RestartFlagHolder restartFlagHolder;
+
+  public WebServerStatusNodeCheck(DatabaseMigrationState migrationState, Platform platform, RestartFlagHolder restartFlagHolder) {
+    this.migrationState = migrationState;
+    this.platform = platform;
+    this.restartFlagHolder = restartFlagHolder;
+  }
+
+  @Override
+  public Health check() {
+    Platform.Status platformStatus = platform.status();
+    if (platformStatus == Platform.Status.UP
+      && VALID_DATABASEMIGRATION_STATUSES.contains(migrationState.getStatus())
+      && !restartFlagHolder.isRestarting()) {
+      return Health.GREEN;
+    }
+    return Health.builder()
+      .setStatus(Health.Status.RED)
+      .addCause("SonarQube webserver is not up")
+      .build();
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessChecker.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessChecker.java
new file mode 100644 (file)
index 0000000..58de1fe
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.platform;
+
+public interface LivenessChecker {
+  boolean liveness();
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessCheckerImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/LivenessCheckerImpl.java
new file mode 100644 (file)
index 0000000..7ae54f0
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.platform;
+
+import javax.annotation.Nullable;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
+import org.sonar.server.health.Health;
+
+public class LivenessCheckerImpl implements LivenessChecker {
+
+  private final DbConnectionNodeCheck dbConnectionNodeCheck;
+  private final CeStatusNodeCheck ceStatusNodeCheck;
+  @Nullable
+  private final EsStatusNodeCheck esStatusNodeCheck;
+  private final WebServerStatusNodeCheck webServerStatusNodeCheck;
+
+  public LivenessCheckerImpl(DbConnectionNodeCheck dbConnectionNodeCheck,
+    WebServerStatusNodeCheck webServerStatusNodeCheck, CeStatusNodeCheck ceStatusNodeCheck, @Nullable EsStatusNodeCheck esStatusNodeCheck) {
+    this.dbConnectionNodeCheck = dbConnectionNodeCheck;
+    this.webServerStatusNodeCheck = webServerStatusNodeCheck;
+    this.ceStatusNodeCheck = ceStatusNodeCheck;
+    this.esStatusNodeCheck = esStatusNodeCheck;
+  }
+
+  public boolean liveness() {
+
+    if (!Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus())) {
+      return false;
+    }
+
+    if (!Health.Status.GREEN.equals(webServerStatusNodeCheck.check().getStatus())) {
+      return false;
+    }
+
+    if (!Health.Status.GREEN.equals(ceStatusNodeCheck.check().getStatus())) {
+      return false;
+    }
+
+    if (esStatusNodeCheck != null && Health.Status.RED.equals(esStatusNodeCheck.check().getStatus())) {
+      return false;
+    }
+
+    return true;
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImpl.java
new file mode 100644 (file)
index 0000000..97216c0
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.platform;
+
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.health.Health;
+
+public class SafeModeLivenessCheckerImpl implements LivenessChecker {
+
+  private final DbConnectionNodeCheck dbConnectionNodeCheck;
+
+  public SafeModeLivenessCheckerImpl(DbConnectionNodeCheck dbConnectionNodeCheck) {
+    this.dbConnectionNodeCheck = dbConnectionNodeCheck;
+  }
+
+  public boolean liveness() {
+    return Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus());
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/AppNodeClusterCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/AppNodeClusterCheckTest.java
new file mode 100644 (file)
index 0000000..0687a1e
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.sonar.process.cluster.health.NodeDetails;
+import org.sonar.process.cluster.health.NodeHealth;
+import org.sonar.server.health.Health;
+
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.of;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
+import static org.sonar.process.cluster.health.NodeHealth.Status.RED;
+import static org.sonar.process.cluster.health.NodeHealth.Status.YELLOW;
+import static org.sonar.server.common.health.HealthAssert.assertThat;
+
+public class AppNodeClusterCheckTest {
+  private final Random random = new Random();
+
+  private AppNodeClusterCheck underTest = new AppNodeClusterCheck();
+
+  @Test
+  public void status_RED_when_no_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths().collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.RED)
+      .andCauses("No application node");
+  }
+
+  @Test
+  public void status_RED_when_single_RED_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(RED).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.RED)
+      .andCauses("Status of all application nodes is RED",
+        "There should be at least two application nodes");
+  }
+
+  @Test
+  public void status_YELLOW_when_single_YELLOW_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses(
+        "Status of all application nodes is YELLOW",
+        "There should be at least two application nodes");
+  }
+
+  @Test
+  public void status_YELLOW_when_single_GREEN_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(GREEN).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("There should be at least two application nodes");
+  }
+
+  @Test
+  public void status_RED_when_two_RED_application_nodes() {
+    Set<NodeHealth> nodeHealths = nodeHealths(RED, RED).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.RED)
+      .andCauses("Status of all application nodes is RED");
+  }
+
+  @Test
+  public void status_YELLOW_when_two_YELLOW_application_nodes() {
+    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, YELLOW).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("Status of all application nodes is YELLOW");
+  }
+
+  @Test
+  public void status_YELLOW_when_one_RED_node_and_one_YELLOW_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(RED, YELLOW).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses(
+        "At least one application node is RED",
+        "At least one application node is YELLOW");
+  }
+
+  @Test
+  public void status_YELLOW_when_one_RED_node_and_one_GREEN_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(RED, GREEN).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("At least one application node is RED");
+  }
+
+  @Test
+  public void status_YELLOW_when_one_YELLOW_node_and_one_GREEN_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, GREEN).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("At least one application node is YELLOW");
+  }
+
+  @Test
+  public void status_GREEN_when_two_GREEN_application_node() {
+    Set<NodeHealth> nodeHealths = nodeHealths(GREEN, GREEN).collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.GREEN)
+      .andCauses();
+  }
+
+  @Test
+  public void status_GREEN_when_two_GREEN_application_node_and_any_number_of_other_is_GREEN() {
+    Set<NodeHealth> nodeHealths = of(
+      // at least 1 extra GREEN
+      of(appNodeHealth(GREEN)),
+      // 0 to 10 GREEN
+      randomNumberOfAppNodeHealthOfAnyStatus(GREEN),
+      // 2 GREEN
+      nodeHealths(GREEN, GREEN))
+      .flatMap(s -> s)
+      .collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.GREEN)
+      .andCauses();
+  }
+
+  @Test
+  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_YELLOW_or_GREEN() {
+    Set<NodeHealth> nodeHealths = of(
+      // at least 1 YELLOW
+      of(appNodeHealth(YELLOW)),
+      // 0 to 10 YELLOW/GREEN
+      randomNumberOfAppNodeHealthOfAnyStatus(GREEN, YELLOW),
+      // 2 GREEN
+      nodeHealths(GREEN, GREEN))
+      .flatMap(s -> s)
+      .collect(toSet());
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("At least one application node is YELLOW");
+  }
+
+  @Test
+  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_RED_or_GREEN() {
+    Set<NodeHealth> nodeHealths = of(
+      // at least 1 RED
+      of(appNodeHealth(RED)),
+      // 0 to 10 RED/GREEN
+      randomNumberOfAppNodeHealthOfAnyStatus(GREEN, RED),
+      // 2 GREEN
+      nodeHealths(GREEN, GREEN))
+      .flatMap(s -> s)
+      .collect(toSet());
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses("At least one application node is RED");
+  }
+
+  @Test
+  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_either_RED_or_YELLOW() {
+    Set<NodeHealth> nodeHealths = of(
+      // at least 1 RED
+      of(appNodeHealth(RED)),
+      // at least 1 YELLOW
+      of(appNodeHealth(YELLOW)),
+      // 0 to 10 RED/YELLOW/GREEN
+      randomNumberOfAppNodeHealthOfAnyStatus(RED, YELLOW, GREEN),
+      // 2 GREEN
+      nodeHealths(GREEN, GREEN))
+      .flatMap(s -> s)
+      .collect(toSet());
+
+    Health check = underTest.check(nodeHealths);
+
+    assertThat(check)
+      .forInput(nodeHealths)
+      .hasStatus(Health.Status.YELLOW)
+      .andCauses(
+        "At least one application node is YELLOW",
+        "At least one application node is RED");
+  }
+
+  /**
+   * Between 0 and 10 NodeHealth of Application node with any of the specified statuses.
+   */
+  private Stream<NodeHealth> randomNumberOfAppNodeHealthOfAnyStatus(NodeHealth.Status... randomStatuses) {
+    return IntStream.range(0, random.nextInt(10))
+      .mapToObj(i -> appNodeHealth(randomStatuses[random.nextInt(randomStatuses.length)]));
+  }
+
+  private Stream<NodeHealth> nodeHealths(NodeHealth.Status... appNodeStatuses) {
+    return of(
+      // random number of Search nodes with random status
+      IntStream.range(0, random.nextInt(3))
+        .mapToObj(i -> appNodeHealth(NodeDetails.Type.SEARCH, NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)])),
+      Arrays.stream(appNodeStatuses).map(this::appNodeHealth))
+      .flatMap(s -> s);
+  }
+
+  private NodeHealth appNodeHealth(NodeHealth.Status status) {
+    return appNodeHealth(NodeDetails.Type.APPLICATION, status);
+  }
+
+  private NodeHealth appNodeHealth(NodeDetails.Type type, NodeHealth.Status status) {
+    return NodeHealth.newNodeHealthBuilder()
+      .setStatus(status)
+      .setDetails(NodeDetails.newNodeDetailsBuilder()
+        .setType(type)
+        .setHost(randomAlphanumeric(32))
+        .setName(randomAlphanumeric(32))
+        .setPort(1 + random.nextInt(88))
+        .setStartedAt(1 + random.nextInt(54))
+        .build())
+      .build();
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/CeStatusNodeCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/CeStatusNodeCheckTest.java
new file mode 100644 (file)
index 0000000..d485948
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.junit.Test;
+import org.sonar.server.app.ProcessCommandWrapper;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.health.Health;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CeStatusNodeCheckTest {
+  private ProcessCommandWrapper processCommandWrapper = mock(ProcessCommandWrapper.class);
+  private CeStatusNodeCheck underTest = new CeStatusNodeCheck(processCommandWrapper);
+
+  @Test
+  public void check_returns_GREEN_status_without_cause_if_ce_is_operational() {
+    when(processCommandWrapper.isCeOperational()).thenReturn(true);
+
+    Health health = underTest.check();
+
+    assertThat(health).isEqualTo(Health.GREEN);
+  }
+
+  @Test
+  public void check_returns_RED_status_with_cause_if_ce_is_not_operational() {
+    when(processCommandWrapper.isCeOperational()).thenReturn(false);
+
+    Health health = underTest.check();
+
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("Compute Engine is not operational");
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/DbConnectionNodeCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/DbConnectionNodeCheckTest.java
new file mode 100644 (file)
index 0000000..b02342d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.IsAliveMapper;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.health.Health;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DbConnectionNodeCheckTest {
+  private DbClient dbClient = mock(DbClient.class);
+  private DbSession dbSession = mock(DbSession.class);
+  private IsAliveMapper isAliveMapper = mock(IsAliveMapper.class);
+
+  private DbConnectionNodeCheck underTest = new DbConnectionNodeCheck(dbClient);
+
+  @Before
+  public void wireMocks() {
+    when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+    when(dbSession.getMapper(IsAliveMapper.class)).thenReturn(isAliveMapper);
+  }
+
+  @Test
+  public void status_is_GREEN_without_cause_if_isAlive_returns_1() {
+    when(isAliveMapper.isAlive()).thenReturn(1);
+
+    Health health = underTest.check();
+
+    assertThat(health).isEqualTo(Health.GREEN);
+  }
+
+  @Test
+  public void status_is_RED_with_single_cause_if_any_error_occurs_when_checking_DB() {
+    when(isAliveMapper.isAlive()).thenThrow(new RuntimeException("simulated runtime exception when querying DB"));
+
+    Health health = underTest.check();
+
+    verifyRedStatus(health);
+  }
+
+  /**
+   * By contract {@link IsAliveMapper#isAlive()} can not return anything but 1. Still we write this test as a
+   * protection against change in this contract.
+   */
+  @Test
+  public void status_is_RED_with_single_cause_if_isAlive_does_not_return_1() {
+    when(isAliveMapper.isAlive()).thenReturn(12);
+
+    Health health = underTest.check();
+
+    verifyRedStatus(health);
+  }
+
+  private void verifyRedStatus(Health health) {
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("Can't connect to DB");
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusClusterCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusClusterCheckTest.java
new file mode 100644 (file)
index 0000000..9aa2c7c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Random;
+import java.util.Set;
+import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.process.cluster.health.NodeDetails;
+import org.sonar.process.cluster.health.NodeHealth;
+import org.sonar.server.common.health.EsStatusClusterCheck;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.health.Health;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+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 {
+
+  private EsClient esClient = Mockito.mock(EsClient.class, RETURNS_DEEP_STUBS);
+  private Random random = new Random();
+  private EsStatusClusterCheck underTest = new EsStatusClusterCheck(esClient);
+
+  @Test
+  public void check_ignores_NodeHealth_arg_and_returns_RED_with_cause_if_an_exception_occurs_checking_ES_cluster_status() {
+    Set<NodeHealth> nodeHealths = ImmutableSet.of(newNodeHealth(NodeHealth.Status.GREEN));
+    when(esClient.clusterHealth(any())).thenThrow(new RuntimeException("Faking an exception occurring while using the EsClient"));
+
+    Health health = new EsStatusClusterCheck(esClient).check(nodeHealths);
+
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("Elasticsearch status is RED (unavailable)");
+  }
+
+  @Test
+  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)
+      .setDetails(NodeDetails.newNodeDetailsBuilder()
+        .setType(random.nextBoolean() ? NodeDetails.Type.APPLICATION : NodeDetails.Type.SEARCH)
+        .setName(randomAlphanumeric(23))
+        .setHost(randomAlphanumeric(23))
+        .setPort(1 + random.nextInt(96))
+        .setStartedAt(1 + random.nextInt(966))
+        .build())
+      .build();
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusNodeCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/EsStatusNodeCheckTest.java
new file mode 100644 (file)
index 0000000..bf27294
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.server.common.health.EsStatusNodeCheck;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.health.Health;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class EsStatusNodeCheckTest {
+
+  private EsClient esClient = mock(EsClient.class, Mockito.RETURNS_DEEP_STUBS);
+  private EsStatusNodeCheck underTest = new EsStatusNodeCheck(esClient);
+
+  @Test
+  public void check_ignores_NodeHealth_arg_and_returns_RED_with_cause_if_an_exception_occurs_checking_ES_cluster_status() {
+    EsClient esClient = mock(EsClient.class);
+    when(esClient.clusterHealth(any())).thenThrow(new RuntimeException("Faking an exception occurring while using the EsClient"));
+
+    Health health = new EsStatusNodeCheck(esClient).check();
+
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("Elasticsearch status is RED (unavailable)");
+  }
+
+  @Test
+  public void check_returns_GREEN_without_cause_if_ES_cluster_status_is_GREEN() {
+    when(esClient.clusterHealth(any()).getStatus()).thenReturn(ClusterHealthStatus.GREEN);
+
+    Health health = underTest.check();
+
+    assertThat(health).isEqualTo(Health.GREEN);
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/HealthAssert.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/HealthAssert.java
new file mode 100644 (file)
index 0000000..8d8c6b3
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.assertj.core.api.AbstractAssert;
+import org.sonar.process.cluster.health.NodeHealth;
+import org.sonar.server.health.Health;
+
+final class HealthAssert extends AbstractAssert<HealthAssert, Health> {
+  private Set<NodeHealth> nodeHealths;
+
+  private HealthAssert(Health actual) {
+    super(actual, HealthAssert.class);
+  }
+
+  public static HealthAssert assertThat(Health actual) {
+    return new HealthAssert(actual);
+  }
+
+  public HealthAssert forInput(Set<NodeHealth> nodeHealths) {
+    this.nodeHealths = nodeHealths;
+
+    return this;
+  }
+
+  public HealthAssert hasStatus(Health.Status expected) {
+    isNotNull();
+
+    if (actual.getStatus() != expected) {
+      failWithMessage(
+        "Expected Status of Health to be <%s> but was <%s> for NodeHealth \n%s",
+        expected,
+        actual.getStatus(),
+        printStatusesAndTypes(this.nodeHealths));
+    }
+
+    return this;
+  }
+
+  public HealthAssert andCauses(String... causes) {
+    isNotNull();
+
+    if (!checkCauses(causes)) {
+      failWithMessage(
+        "Expected causes of Health to contain only \n%s\n but was \n%s\n for NodeHealth \n%s",
+        Arrays.asList(causes),
+        actual.getCauses(),
+        printStatusesAndTypes(this.nodeHealths));
+    }
+
+    return this;
+  }
+
+  private String printStatusesAndTypes(@Nullable Set<NodeHealth> nodeHealths) {
+    if (nodeHealths == null) {
+      return "<null>";
+    }
+    return nodeHealths.stream()
+      // sort by type then status for debugging convenience
+      .sorted(Comparator.<NodeHealth>comparingInt(s1 -> s1.getDetails().getType().ordinal())
+        .thenComparingInt(s -> s.getStatus().ordinal()))
+      .map(s -> ImmutableList.of(s.getDetails().getType().name(), s.getStatus().name()))
+      .map(String::valueOf)
+      .collect(Collectors.joining(","));
+  }
+
+  private boolean checkCauses(String... causes) {
+    if (causes.length != this.actual.getCauses().size()) {
+      return false;
+    }
+    return Objects.equals(new HashSet<>(Arrays.asList(causes)), this.actual.getCauses());
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerSafemodeNodeCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerSafemodeNodeCheckTest.java
new file mode 100644 (file)
index 0000000..fafe5a1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import org.junit.Test;
+import org.sonar.server.common.health.WebServerSafemodeNodeCheck;
+import org.sonar.server.health.Health;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebServerSafemodeNodeCheckTest {
+  private WebServerSafemodeNodeCheck underTest = new WebServerSafemodeNodeCheck();
+
+  @Test
+  public void always_returns_RED_status_with_cause() {
+    Health health = underTest.check();
+
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("SonarQube webserver is not up");
+
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerStatusNodeCheckTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/health/WebServerStatusNodeCheckTest.java
new file mode 100644 (file)
index 0000000..680178b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.health;
+
+import java.util.Arrays;
+import java.util.Random;
+import org.junit.Test;
+import org.sonar.server.app.RestartFlagHolder;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
+import org.sonar.server.health.Health;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.platform.db.migration.DatabaseMigrationState;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WebServerStatusNodeCheckTest {
+  private final DatabaseMigrationState migrationState = mock(DatabaseMigrationState.class);
+  private final Platform platform = mock(Platform.class);
+  private final RestartFlagHolder restartFlagHolder = mock(RestartFlagHolder.class);
+
+  private final Random random = new Random();
+
+  private WebServerStatusNodeCheck underTest = new WebServerStatusNodeCheck(migrationState, platform, restartFlagHolder);
+
+  @Test
+  public void returns_RED_status_with_cause_if_platform_status_is_not_UP() {
+    Platform.Status[] statusesButUp = Arrays.stream(Platform.Status.values())
+      .filter(s -> s != Platform.Status.UP)
+      .toArray(Platform.Status[]::new);
+    Platform.Status randomStatusButUp = statusesButUp[random.nextInt(statusesButUp.length)];
+    when(platform.status()).thenReturn(randomStatusButUp);
+
+    Health health = underTest.check();
+
+    verifyRedHealthWithCause(health);
+  }
+
+  @Test
+  public void returns_RED_status_with_cause_if_platform_status_is_UP_but_migrationStatus_is_neither_NONE_nor_SUCCEED() {
+    when(platform.status()).thenReturn(Platform.Status.UP);
+    DatabaseMigrationState.Status[] statusesButValidOnes = Arrays.stream(DatabaseMigrationState.Status.values())
+      .filter(s -> s != DatabaseMigrationState.Status.NONE)
+      .filter(s -> s != DatabaseMigrationState.Status.SUCCEEDED)
+      .toArray(DatabaseMigrationState.Status[]::new);
+    DatabaseMigrationState.Status randomInvalidStatus = statusesButValidOnes[random.nextInt(statusesButValidOnes.length)];
+    when(migrationState.getStatus()).thenReturn(randomInvalidStatus);
+
+    Health health = underTest.check();
+
+    verifyRedHealthWithCause(health);
+  }
+
+  @Test
+  public void returns_RED_with_cause_if_platform_status_is_UP_migration_status_is_valid_but_SQ_is_restarting() {
+    when(platform.status()).thenReturn(Platform.Status.UP);
+    when(migrationState.getStatus()).thenReturn(random.nextBoolean() ? DatabaseMigrationState.Status.NONE : DatabaseMigrationState.Status.SUCCEEDED);
+    when(restartFlagHolder.isRestarting()).thenReturn(true);
+
+    Health health = underTest.check();
+
+    verifyRedHealthWithCause(health);
+  }
+
+  @Test
+  public void returns_GREEN_without_cause_if_platform_status_is_UP_migration_status_is_valid_and_SQ_is_not_restarting() {
+    when(platform.status()).thenReturn(Platform.Status.UP);
+    when(migrationState.getStatus()).thenReturn(random.nextBoolean() ? DatabaseMigrationState.Status.NONE : DatabaseMigrationState.Status.SUCCEEDED);
+    when(restartFlagHolder.isRestarting()).thenReturn(false);
+
+    Health health = underTest.check();
+
+    assertThat(health).isEqualTo(Health.GREEN);
+  }
+
+  private void verifyRedHealthWithCause(Health health) {
+    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
+    assertThat(health.getCauses()).containsOnly("SonarQube webserver is not up");
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/LivenessCheckerImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/LivenessCheckerImplTest.java
new file mode 100644 (file)
index 0000000..8465cb1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.platform;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
+import org.sonar.server.health.Health;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class LivenessCheckerImplTest {
+
+  public static final Health RED = Health.builder().setStatus(Health.Status.RED).build();
+
+  private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class);
+  private final WebServerStatusNodeCheck webServerStatusNodeCheck = mock(WebServerStatusNodeCheck.class);
+  private final CeStatusNodeCheck ceStatusNodeCheck = mock(CeStatusNodeCheck.class);
+  private final EsStatusNodeCheck esStatusNodeCheck = mock(EsStatusNodeCheck.class);
+
+  LivenessCheckerImpl underTest = new LivenessCheckerImpl(dbConnectionNodeCheck, webServerStatusNodeCheck, ceStatusNodeCheck, esStatusNodeCheck);
+  LivenessCheckerImpl underTestDCE = new LivenessCheckerImpl(dbConnectionNodeCheck, webServerStatusNodeCheck, ceStatusNodeCheck, null);
+
+  @Test
+  public void fail_when_db_connection_check_fail() {
+    when(dbConnectionNodeCheck.check()).thenReturn(RED);
+
+    Assertions.assertThat(underTest.liveness()).isFalse();
+  }
+
+  @Test
+  public void fail_when_web_check_fail() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+    when(webServerStatusNodeCheck.check()).thenReturn(RED);
+
+    Assertions.assertThat(underTest.liveness()).isFalse();
+  }
+
+  @Test
+  public void fail_when_ce_check_fail() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(ceStatusNodeCheck.check()).thenReturn(RED);
+
+    Assertions.assertThat(underTest.liveness()).isFalse();
+  }
+
+  @Test
+  public void fail_when_es_check_fail() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(esStatusNodeCheck.check()).thenReturn(RED);
+
+    Assertions.assertThat(underTest.liveness()).isFalse();
+  }
+
+  @Test
+  public void success_when_db_web_ce_es_succeed() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(esStatusNodeCheck.check()).thenReturn(Health.GREEN);
+
+    Assertions.assertThat(underTest.liveness()).isTrue();
+  }
+
+  @Test
+  public void success_when_db_web_ce_succeed() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
+    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
+
+    Assertions.assertThat(underTestDCE.liveness()).isTrue();
+  }
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/platform/SafeModeLivenessCheckerImplTest.java
new file mode 100644 (file)
index 0000000..8dbe81c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.common.platform;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.platform.SafeModeLivenessCheckerImpl;
+import org.sonar.server.health.Health;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SafeModeLivenessCheckerImplTest {
+
+  public static final Health RED = Health.builder().setStatus(Health.Status.RED).build();
+  private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class);
+  private final SafeModeLivenessCheckerImpl underTest = new SafeModeLivenessCheckerImpl(dbConnectionNodeCheck);
+
+  @Test
+  public void fail_when_db_connection_check_fail() {
+    when(dbConnectionNodeCheck.check()).thenReturn(RED);
+
+    Assertions.assertThat(underTest.liveness()).isFalse();
+  }
+
+  @Test
+  public void succeed_when_db_connection_check_success() {
+    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
+
+    Assertions.assertThat(underTest.liveness()).isTrue();
+  }
+
+}
index 04d0150bf86c88006ae5b7d0425e70fc3ec7b7ae..183c5598094f5416742969ed42c385e46a106482 100644 (file)
@@ -14,9 +14,8 @@ dependencies {
 
     api project(':server:sonar-db-dao')
     api project(':server:sonar-webserver-common')
-    // We are not supposed to have a v1 dependency. The ideal would be to have another common module between webapi and webapi-v2 but that needs a lot of refactoring.
-    api project(':server:sonar-webserver-webapi')
 
+    testImplementation 'javax.servlet:javax.servlet-api'
     testImplementation 'org.mockito:mockito-core'
     testImplementation 'org.springframework:spring-test'
     testImplementation 'org.skyscreamer:jsonassert:1.5.1'
index da32653b58a418a7424b09614bb9c012e670be60..2747a7b9f1af843743eba3aff63776ca9bd534e8 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.server.v2.api.system.controller;
 
 import javax.annotation.Nullable;
+import org.sonar.server.common.platform.LivenessChecker;
 import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.platform.ws.LivenessChecker;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.user.UserSession;
 
index 74b5c8a6fdf4c07c777ab2b558d781ef628a0419..9231cc10c6b5481a54ef41bc3d4f0fba982a9ac9 100644 (file)
 package org.sonar.server.v2.config;
 
 import javax.annotation.Nullable;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
+import org.sonar.server.common.platform.LivenessChecker;
+import org.sonar.server.common.platform.LivenessCheckerImpl;
 import org.sonar.server.common.user.service.UserService;
-import org.sonar.server.health.CeStatusNodeCheck;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
 import org.sonar.server.health.HealthChecker;
-import org.sonar.server.health.WebServerStatusNodeCheck;
 import org.sonar.server.platform.NodeInformation;
-import org.sonar.server.platform.ws.LivenessChecker;
-import org.sonar.server.platform.ws.LivenessCheckerImpl;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
index 5c2671d016b9442a3a94510355af3200f899ab2f..9cc8266ddb66ef88055343ca8970fbf727ba5d3e 100644 (file)
  */
 package org.sonar.server.v2.config;
 
-import org.sonar.server.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.platform.LivenessChecker;
+import org.sonar.server.common.platform.SafeModeLivenessCheckerImpl;
 import org.sonar.server.health.HealthChecker;
-import org.sonar.server.platform.ws.LivenessChecker;
-import org.sonar.server.platform.ws.SafeModeLivenessCheckerImpl;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
 import org.sonar.server.v2.api.system.controller.HealthController;
index 6cd65d5d67b030fa4bad2d514379b902090e01d8..0daa5a994c2ca17029356be2f6ac447d0ebddb39 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.v2.api.system.controller;
 
 import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.server.platform.ws.LivenessChecker;
+import org.sonar.server.common.platform.LivenessChecker;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.v2.api.ControllerTester;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java
deleted file mode 100644 (file)
index 565dbc8..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.sonar.process.cluster.health.NodeDetails;
-import org.sonar.process.cluster.health.NodeHealth;
-
-import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
-import static org.sonar.process.cluster.health.NodeHealth.Status.RED;
-import static org.sonar.process.cluster.health.NodeHealth.Status.YELLOW;
-
-public class AppNodeClusterCheck implements ClusterHealthCheck {
-
-  @Override
-  public Health check(Set<NodeHealth> nodeHealths) {
-    Set<NodeHealth> appNodes = nodeHealths.stream()
-      .filter(s -> s.getDetails().getType() == NodeDetails.Type.APPLICATION)
-      .collect(Collectors.toSet());
-
-    return Arrays.stream(AppNodeClusterHealthSubChecks.values())
-      .map(s -> s.check(appNodes))
-      .reduce(Health.GREEN, HealthReducer::merge);
-  }
-
-  private enum AppNodeClusterHealthSubChecks implements ClusterHealthSubCheck {
-    NO_APPLICATION_NODE() {
-      @Override
-      public Health check(Set<NodeHealth> appNodes) {
-        int appNodeCount = appNodes.size();
-        if (appNodeCount == 0) {
-          return Health.builder()
-            .setStatus(Health.Status.RED)
-            .addCause("No application node")
-            .build();
-        }
-        return Health.GREEN;
-      }
-    },
-    MIN_APPLICATION_NODE_COUNT() {
-      @Override
-      public Health check(Set<NodeHealth> appNodes) {
-        int appNodeCount = appNodes.size();
-        if (appNodeCount == 1) {
-          return Health.builder()
-            .setStatus(Health.Status.YELLOW)
-            .addCause("There should be at least two application nodes")
-            .build();
-        }
-        return Health.GREEN;
-      }
-    },
-    REPORT_RED_OR_YELLOW_NODES() {
-      @Override
-      public Health check(Set<NodeHealth> appNodes) {
-        int appNodeCount = appNodes.size();
-        if (appNodeCount == 0) {
-          // skipping this check
-          return Health.GREEN;
-        }
-
-        long redNodesCount = withStatus(appNodes, RED).count();
-        long yellowNodesCount = withStatus(appNodes, YELLOW).count();
-        if (redNodesCount == 0 && yellowNodesCount == 0) {
-          return Health.GREEN;
-        }
-
-        Health.Builder builder = Health.builder();
-        if (redNodesCount == appNodeCount) {
-          return builder
-            .setStatus(Health.Status.RED)
-            .addCause("Status of all application nodes is RED")
-            .build();
-        } else if (redNodesCount > 0) {
-          builder.addCause("At least one application node is RED");
-        }
-        if (yellowNodesCount == appNodeCount) {
-          return builder
-            .setStatus(Health.Status.YELLOW)
-            .addCause("Status of all application nodes is YELLOW")
-            .build();
-        } else if (yellowNodesCount > 0) {
-          builder.addCause("At least one application node is YELLOW");
-        }
-        long greenNodesCount = withStatus(appNodes, GREEN).count();
-        builder.setStatus(greenNodesCount > 0 || yellowNodesCount > 0 ? Health.Status.YELLOW : Health.Status.RED);
-
-        return builder.build();
-      }
-    }
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/CeStatusNodeCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/CeStatusNodeCheck.java
deleted file mode 100644 (file)
index 2af7789..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.sonar.server.app.ProcessCommandWrapper;
-
-public class CeStatusNodeCheck implements NodeHealthCheck {
-  private static final Health RED_HEALTH = Health.builder()
-    .setStatus(Health.Status.RED)
-    .addCause("Compute Engine is not operational")
-    .build();
-
-  private final ProcessCommandWrapper processCommandWrapper;
-
-  public CeStatusNodeCheck(ProcessCommandWrapper processCommandWrapper) {
-    this.processCommandWrapper = processCommandWrapper;
-  }
-
-  @Override
-  public Health check() {
-    if (processCommandWrapper.isCeOperational()) {
-      return Health.GREEN;
-    }
-
-    return RED_HEALTH;
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthCheck.java
deleted file mode 100644 (file)
index 6dca76d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.Set;
-import org.sonar.process.cluster.health.NodeHealth;
-
-public interface ClusterHealthCheck {
-  Health check(Set<NodeHealth> nodeHealths);
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthSubCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/ClusterHealthSubCheck.java
deleted file mode 100644 (file)
index 3ecb1b3..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.Set;
-import java.util.stream.Stream;
-import org.sonar.process.cluster.health.NodeHealth;
-
-interface ClusterHealthSubCheck extends ClusterHealthCheck {
-
-  default Stream<NodeHealth> withStatus(Set<NodeHealth> searchNodes, NodeHealth.Status... statuses) {
-    return searchNodes.stream()
-        .filter(t -> {
-          for (NodeHealth.Status status : statuses) {
-            if (status == t.getStatus()) {
-              return true;
-            }
-          }
-          return false;
-        });
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/DbConnectionNodeCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/DbConnectionNodeCheck.java
deleted file mode 100644 (file)
index 01cd017..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.IsAliveMapper;
-
-/**
- * Checks Web Server can connect to the Database.
- */
-public class DbConnectionNodeCheck implements NodeHealthCheck {
-  private static final Logger LOGGER = LoggerFactory.getLogger(DbConnectionNodeCheck.class);
-  private static final Health RED_HEALTH = Health.builder().setStatus(Health.Status.RED).addCause("Can't connect to DB").build();
-
-  private final DbClient dbClient;
-
-  public DbConnectionNodeCheck(DbClient dbClient) {
-    this.dbClient = dbClient;
-  }
-
-  @Override
-  public Health check() {
-    if (isConnectedToDB()) {
-      return Health.GREEN;
-    }
-    return RED_HEALTH;
-  }
-
-  private boolean isConnectedToDB() {
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      return dbSession.getMapper(IsAliveMapper.class).isAlive() == IsAliveMapper.IS_ALIVE_RETURNED_VALUE;
-    } catch (RuntimeException e) {
-      LOGGER.trace("DB connection is down: {}", e.getMessage(), e);
-      return false;
-    }
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusCheck.java
deleted file mode 100644 (file)
index e228781..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.server.es.EsClient;
-
-abstract class EsStatusCheck {
-  private static final Logger LOG = LoggerFactory.getLogger(EsStatusCheck.class);
-
-  private static final Health YELLOW_HEALTH = Health.builder()
-    .setStatus(Health.Status.YELLOW)
-    .addCause("Elasticsearch status is YELLOW")
-    .build();
-  private static final Health RED_HEALTH = Health.builder()
-    .setStatus(Health.Status.RED)
-    .addCause("Elasticsearch status is RED")
-    .build();
-  protected static final Health RED_HEALTH_UNAVAILABLE = Health.builder()
-    .setStatus(Health.Status.RED)
-    .addCause("Elasticsearch status is RED (unavailable)")
-    .build();
-
-  private final EsClient esClient;
-
-  EsStatusCheck(EsClient esClient) {
-    this.esClient = esClient;
-  }
-
-  protected ClusterHealthResponse getEsClusterHealth() {
-    try {
-      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);
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusClusterCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusClusterCheck.java
deleted file mode 100644 (file)
index 5964ddd..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-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) {
-    ClusterHealthResponse esClusterHealth = this.getEsClusterHealth();
-    if (esClusterHealth != null) {
-      Health minimumNodes = checkMinimumNodes(esClusterHealth);
-      Health clusterStatus = extractStatusHealth(esClusterHealth);
-      return HealthReducer.merge(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.builder()
-        .setStatus(Health.Status.YELLOW)
-        .addCause(MINIMUM_NODE_MESSAGE)
-        .build();
-    }
-    return Health.GREEN;
-  }
-
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusNodeCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/EsStatusNodeCheck.java
deleted file mode 100644 (file)
index 373f536..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
-import org.sonar.server.es.EsClient;
-
-/**
- * Checks the ElasticSearch cluster status.
- */
-public class EsStatusNodeCheck extends EsStatusCheck implements NodeHealthCheck {
-
-  public EsStatusNodeCheck(EsClient esClient) {
-    super(esClient);
-  }
-
-  @Override
-  public Health check() {
-    ClusterHealthResponse healthResponse = getEsClusterHealth();
-    return healthResponse != null ? extractStatusHealth(healthResponse) : RED_HEALTH_UNAVAILABLE;
-  }
-}
index 0d551d284e71589ccdc8ddd91ae474d7c101a0e3..15841346c438283811a4ad9d93fc8401e255cf68 100644 (file)
@@ -25,6 +25,9 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.process.cluster.health.NodeHealth;
 import org.sonar.process.cluster.health.SharedHealthState;
+import org.sonar.server.common.health.ClusterHealthCheck;
+import org.sonar.server.common.health.HealthReducer;
+import org.sonar.server.common.health.NodeHealthCheck;
 import org.sonar.server.platform.NodeInformation;
 import org.springframework.beans.factory.annotation.Autowired;
 
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/HealthReducer.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/HealthReducer.java
deleted file mode 100644 (file)
index 984bc2a..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-
-public class HealthReducer {
-
-  private HealthReducer() {
-    // no public constructor
-  }
-
-  public static Health merge(Health left, Health right) {
-    Health.Builder builder = Health.builder()
-      .setStatus(worseOf(left.getStatus(), right.getStatus()));
-    left.getCauses().forEach(builder::addCause);
-    right.getCauses().forEach(builder::addCause);
-    return builder.build();
-  }
-
-  private static Health.Status worseOf(Health.Status left, Health.Status right) {
-    if (left == right) {
-      return left;
-    }
-    if (left.ordinal() > right.ordinal()) {
-      return left;
-    }
-    return right;
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/NodeHealthCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/NodeHealthCheck.java
deleted file mode 100644 (file)
index 814db0d..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-public interface NodeHealthCheck {
-  Health check();
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerSafemodeNodeCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerSafemodeNodeCheck.java
deleted file mode 100644 (file)
index 762055d..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-/**
- * Checks the running status of the WebServer when the WebServer is in safemode.
- * Obviously, it statically returns a red health status.
- */
-public class WebServerSafemodeNodeCheck implements NodeHealthCheck {
-
-  private static final Health RED_HEALTH = Health.builder()
-    .setStatus(Health.Status.RED)
-    .addCause("SonarQube webserver is not up")
-    .build();
-
-  @Override
-  public Health check() {
-    return RED_HEALTH;
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerStatusNodeCheck.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/health/WebServerStatusNodeCheck.java
deleted file mode 100644 (file)
index 490f41e..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.EnumSet;
-import org.sonar.server.app.RestartFlagHolder;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.DatabaseMigrationState;
-
-/**
- * Checks the running status of the WebServer when it is not anymore in safemode.
- */
-public class WebServerStatusNodeCheck implements NodeHealthCheck {
-  private static final EnumSet<DatabaseMigrationState.Status> VALID_DATABASEMIGRATION_STATUSES = EnumSet.of(
-    DatabaseMigrationState.Status.NONE, DatabaseMigrationState.Status.SUCCEEDED);
-
-  private final DatabaseMigrationState migrationState;
-  private final Platform platform;
-  private final RestartFlagHolder restartFlagHolder;
-
-  public WebServerStatusNodeCheck(DatabaseMigrationState migrationState, Platform platform, RestartFlagHolder restartFlagHolder) {
-    this.migrationState = migrationState;
-    this.platform = platform;
-    this.restartFlagHolder = restartFlagHolder;
-  }
-
-  @Override
-  public Health check() {
-    Platform.Status platformStatus = platform.status();
-    if (platformStatus == Platform.Status.UP
-      && VALID_DATABASEMIGRATION_STATUSES.contains(migrationState.getStatus())
-      && !restartFlagHolder.isRestarting()) {
-      return Health.GREEN;
-    }
-    return Health.builder()
-      .setStatus(Health.Status.RED)
-      .addCause("SonarQube webserver is not up")
-      .build();
-  }
-}
index 8aa53d337c561d701f9a30f7435a517c1ae89c1f..fe87252937430bfe877ce21afe6ad78a69dbf9ed 100644 (file)
 package org.sonar.server.platform.ws;
 
 import org.sonar.core.platform.Module;
-import org.sonar.server.health.AppNodeClusterCheck;
-import org.sonar.server.health.CeStatusNodeCheck;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusClusterCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.AppNodeClusterCheck;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusClusterCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
 import org.sonar.server.health.HealthCheckerImpl;
-import org.sonar.server.health.WebServerStatusNodeCheck;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
 import org.sonar.server.platform.NodeInformation;
 
 public class HealthCheckerModule extends Module {
index 31cbed8d6c94df920477a50dc013504c7d34ebf7..278ccdb495cc01f0acf4e74efc805b8e7153e012 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.ws;
 
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.server.common.platform.LivenessChecker;
 
 public class LivenessActionSupport {
 
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java
deleted file mode 100644 (file)
index 372114b..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.platform.ws;
-
-public interface LivenessChecker {
-  boolean liveness();
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java
deleted file mode 100644 (file)
index 415b489..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.platform.ws;
-
-import javax.annotation.Nullable;
-import org.sonar.server.health.CeStatusNodeCheck;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
-import org.sonar.server.health.Health;
-import org.sonar.server.health.WebServerStatusNodeCheck;
-
-public class LivenessCheckerImpl implements LivenessChecker {
-
-  private final DbConnectionNodeCheck dbConnectionNodeCheck;
-  private final CeStatusNodeCheck ceStatusNodeCheck;
-  @Nullable
-  private final EsStatusNodeCheck esStatusNodeCheck;
-  private final WebServerStatusNodeCheck webServerStatusNodeCheck;
-
-  public LivenessCheckerImpl(DbConnectionNodeCheck dbConnectionNodeCheck,
-    WebServerStatusNodeCheck webServerStatusNodeCheck, CeStatusNodeCheck ceStatusNodeCheck, @Nullable EsStatusNodeCheck esStatusNodeCheck) {
-    this.dbConnectionNodeCheck = dbConnectionNodeCheck;
-    this.webServerStatusNodeCheck = webServerStatusNodeCheck;
-    this.ceStatusNodeCheck = ceStatusNodeCheck;
-    this.esStatusNodeCheck = esStatusNodeCheck;
-  }
-
-  public boolean liveness() {
-
-    if (!Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus())) {
-      return false;
-    }
-
-    if (!Health.Status.GREEN.equals(webServerStatusNodeCheck.check().getStatus())) {
-      return false;
-    }
-
-    if (!Health.Status.GREEN.equals(ceStatusNodeCheck.check().getStatus())) {
-      return false;
-    }
-
-    if (esStatusNodeCheck != null && Health.Status.RED.equals(esStatusNodeCheck.check().getStatus())) {
-      return false;
-    }
-
-    return true;
-  }
-}
index d9a37ba5680d08c04fa3eb5a964cd818de4e7164..894d7fa3daf5284837217574e36b6357cd469a7a 100644 (file)
 package org.sonar.server.platform.ws;
 
 import org.sonar.core.platform.Module;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
 import org.sonar.server.health.HealthCheckerImpl;
-import org.sonar.server.health.WebServerSafemodeNodeCheck;
+import org.sonar.server.common.health.WebServerSafemodeNodeCheck;
 
 public class SafeModeHealthCheckerModule extends Module {
   @Override
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java
deleted file mode 100644 (file)
index 508045f..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.platform.ws;
-
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.Health;
-
-public class SafeModeLivenessCheckerImpl implements LivenessChecker {
-
-  private final DbConnectionNodeCheck dbConnectionNodeCheck;
-
-  public SafeModeLivenessCheckerImpl(DbConnectionNodeCheck dbConnectionNodeCheck) {
-    this.dbConnectionNodeCheck = dbConnectionNodeCheck;
-  }
-
-  public boolean liveness() {
-    return Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus());
-  }
-}
index 93dcd16ac39b089e846cbf2eda4a707370b549d8..b5dbb0c79404d1c7cb57aa00c2fac7b47e02fb8a 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.platform.ws;
 
 import org.sonar.core.platform.Module;
+import org.sonar.server.common.platform.SafeModeLivenessCheckerImpl;
 import org.sonar.server.monitoring.MonitoringWs;
 import org.sonar.server.user.BearerPasscode;
 
index 641d4f63430becf906f9c808bdd07783d2605db5..b1a19ee8660d6dcfd303411703d0f88f4fc8b2e7 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.platform.ws;
 
 import org.sonar.core.platform.Module;
+import org.sonar.server.common.platform.LivenessCheckerImpl;
 
 public class SystemWsModule extends Module {
 
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java
deleted file mode 100644 (file)
index 8320c96..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.Arrays;
-import java.util.Random;
-import java.util.Set;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
-import org.junit.Test;
-import org.sonar.process.cluster.health.NodeDetails;
-import org.sonar.process.cluster.health.NodeHealth;
-
-import static java.util.stream.Collectors.toSet;
-import static java.util.stream.Stream.of;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
-import static org.sonar.process.cluster.health.NodeHealth.Status.RED;
-import static org.sonar.process.cluster.health.NodeHealth.Status.YELLOW;
-import static org.sonar.server.health.HealthAssert.assertThat;
-
-public class AppNodeClusterCheckTest {
-  private final Random random = new Random();
-
-  private AppNodeClusterCheck underTest = new AppNodeClusterCheck();
-
-  @Test
-  public void status_RED_when_no_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths().collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.RED)
-      .andCauses("No application node");
-  }
-
-  @Test
-  public void status_RED_when_single_RED_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(RED).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.RED)
-      .andCauses("Status of all application nodes is RED",
-        "There should be at least two application nodes");
-  }
-
-  @Test
-  public void status_YELLOW_when_single_YELLOW_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses(
-        "Status of all application nodes is YELLOW",
-        "There should be at least two application nodes");
-  }
-
-  @Test
-  public void status_YELLOW_when_single_GREEN_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(GREEN).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("There should be at least two application nodes");
-  }
-
-  @Test
-  public void status_RED_when_two_RED_application_nodes() {
-    Set<NodeHealth> nodeHealths = nodeHealths(RED, RED).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.RED)
-      .andCauses("Status of all application nodes is RED");
-  }
-
-  @Test
-  public void status_YELLOW_when_two_YELLOW_application_nodes() {
-    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, YELLOW).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("Status of all application nodes is YELLOW");
-  }
-
-  @Test
-  public void status_YELLOW_when_one_RED_node_and_one_YELLOW_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(RED, YELLOW).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses(
-        "At least one application node is RED",
-        "At least one application node is YELLOW");
-  }
-
-  @Test
-  public void status_YELLOW_when_one_RED_node_and_one_GREEN_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(RED, GREEN).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("At least one application node is RED");
-  }
-
-  @Test
-  public void status_YELLOW_when_one_YELLOW_node_and_one_GREEN_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, GREEN).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("At least one application node is YELLOW");
-  }
-
-  @Test
-  public void status_GREEN_when_two_GREEN_application_node() {
-    Set<NodeHealth> nodeHealths = nodeHealths(GREEN, GREEN).collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.GREEN)
-      .andCauses();
-  }
-
-  @Test
-  public void status_GREEN_when_two_GREEN_application_node_and_any_number_of_other_is_GREEN() {
-    Set<NodeHealth> nodeHealths = of(
-      // at least 1 extra GREEN
-      of(appNodeHealth(GREEN)),
-      // 0 to 10 GREEN
-      randomNumberOfAppNodeHealthOfAnyStatus(GREEN),
-      // 2 GREEN
-      nodeHealths(GREEN, GREEN))
-        .flatMap(s -> s)
-        .collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.GREEN)
-      .andCauses();
-  }
-
-  @Test
-  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_YELLOW_or_GREEN() {
-    Set<NodeHealth> nodeHealths = of(
-      // at least 1 YELLOW
-      of(appNodeHealth(YELLOW)),
-      // 0 to 10 YELLOW/GREEN
-      randomNumberOfAppNodeHealthOfAnyStatus(GREEN, YELLOW),
-      // 2 GREEN
-      nodeHealths(GREEN, GREEN))
-        .flatMap(s -> s)
-        .collect(toSet());
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("At least one application node is YELLOW");
-  }
-
-  @Test
-  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_RED_or_GREEN() {
-    Set<NodeHealth> nodeHealths = of(
-      // at least 1 RED
-      of(appNodeHealth(RED)),
-      // 0 to 10 RED/GREEN
-      randomNumberOfAppNodeHealthOfAnyStatus(GREEN, RED),
-      // 2 GREEN
-      nodeHealths(GREEN, GREEN))
-        .flatMap(s -> s)
-        .collect(toSet());
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses("At least one application node is RED");
-  }
-
-  @Test
-  public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_either_RED_or_YELLOW() {
-    Set<NodeHealth> nodeHealths = of(
-      // at least 1 RED
-      of(appNodeHealth(RED)),
-      // at least 1 YELLOW
-      of(appNodeHealth(YELLOW)),
-      // 0 to 10 RED/YELLOW/GREEN
-      randomNumberOfAppNodeHealthOfAnyStatus(RED, YELLOW, GREEN),
-      // 2 GREEN
-      nodeHealths(GREEN, GREEN))
-        .flatMap(s -> s)
-        .collect(toSet());
-
-    Health check = underTest.check(nodeHealths);
-
-    assertThat(check)
-      .forInput(nodeHealths)
-      .hasStatus(Health.Status.YELLOW)
-      .andCauses(
-        "At least one application node is YELLOW",
-        "At least one application node is RED");
-  }
-
-  /**
-   * Between 0 and 10 NodeHealth of Application node with any of the specified statuses.
-   */
-  private Stream<NodeHealth> randomNumberOfAppNodeHealthOfAnyStatus(NodeHealth.Status... randomStatuses) {
-    return IntStream.range(0, random.nextInt(10))
-      .mapToObj(i -> appNodeHealth(randomStatuses[random.nextInt(randomStatuses.length)]));
-  }
-
-  private Stream<NodeHealth> nodeHealths(NodeHealth.Status... appNodeStatuses) {
-    return of(
-      // random number of Search nodes with random status
-      IntStream.range(0, random.nextInt(3))
-        .mapToObj(i -> appNodeHealth(NodeDetails.Type.SEARCH, NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)])),
-      Arrays.stream(appNodeStatuses).map(this::appNodeHealth))
-        .flatMap(s -> s);
-  }
-
-  private NodeHealth appNodeHealth(NodeHealth.Status status) {
-    return appNodeHealth(NodeDetails.Type.APPLICATION, status);
-  }
-
-  private NodeHealth appNodeHealth(NodeDetails.Type type, NodeHealth.Status status) {
-    return NodeHealth.newNodeHealthBuilder()
-      .setStatus(status)
-      .setDetails(NodeDetails.newNodeDetailsBuilder()
-        .setType(type)
-        .setHost(randomAlphanumeric(32))
-        .setName(randomAlphanumeric(32))
-        .setPort(1 + random.nextInt(88))
-        .setStartedAt(1 + random.nextInt(54))
-        .build())
-      .build();
-  }
-
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/CeStatusNodeCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/CeStatusNodeCheckTest.java
deleted file mode 100644 (file)
index 9482bff..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.junit.Test;
-import org.sonar.server.app.ProcessCommandWrapper;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class CeStatusNodeCheckTest {
-  private ProcessCommandWrapper processCommandWrapper = mock(ProcessCommandWrapper.class);
-  private CeStatusNodeCheck underTest = new CeStatusNodeCheck(processCommandWrapper);
-
-  @Test
-  public void check_returns_GREEN_status_without_cause_if_ce_is_operational() {
-    when(processCommandWrapper.isCeOperational()).thenReturn(true);
-
-    Health health = underTest.check();
-
-    assertThat(health).isEqualTo(Health.GREEN);
-  }
-
-  @Test
-  public void check_returns_RED_status_with_cause_if_ce_is_not_operational() {
-    when(processCommandWrapper.isCeOperational()).thenReturn(false);
-
-    Health health = underTest.check();
-
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("Compute Engine is not operational");
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/DbConnectionNodeCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/DbConnectionNodeCheckTest.java
deleted file mode 100644 (file)
index 7a414bb..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.IsAliveMapper;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DbConnectionNodeCheckTest {
-  private DbClient dbClient = mock(DbClient.class);
-  private DbSession dbSession = mock(DbSession.class);
-  private IsAliveMapper isAliveMapper = mock(IsAliveMapper.class);
-
-  private DbConnectionNodeCheck underTest = new DbConnectionNodeCheck(dbClient);
-
-  @Before
-  public void wireMocks() {
-    when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
-    when(dbSession.getMapper(IsAliveMapper.class)).thenReturn(isAliveMapper);
-  }
-
-  @Test
-  public void status_is_GREEN_without_cause_if_isAlive_returns_1() {
-    when(isAliveMapper.isAlive()).thenReturn(1);
-
-    Health health = underTest.check();
-
-    assertThat(health).isEqualTo(Health.GREEN);
-  }
-
-  @Test
-  public void status_is_RED_with_single_cause_if_any_error_occurs_when_checking_DB() {
-    when(isAliveMapper.isAlive()).thenThrow(new RuntimeException("simulated runtime exception when querying DB"));
-
-    Health health = underTest.check();
-
-    verifyRedStatus(health);
-  }
-
-  /**
-   * By contract {@link IsAliveMapper#isAlive()} can not return anything but 1. Still we write this test as a
-   * protection against change in this contract.
-   */
-  @Test
-  public void status_is_RED_with_single_cause_if_isAlive_does_not_return_1() {
-    when(isAliveMapper.isAlive()).thenReturn(12);
-
-    Health health = underTest.check();
-
-    verifyRedStatus(health);
-  }
-
-  private void verifyRedStatus(Health health) {
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("Can't connect to DB");
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusClusterCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusClusterCheckTest.java
deleted file mode 100644 (file)
index 0d8c4d5..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Random;
-import java.util.Set;
-import org.elasticsearch.cluster.health.ClusterHealthStatus;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.sonar.process.cluster.health.NodeDetails;
-import org.sonar.process.cluster.health.NodeHealth;
-import org.sonar.server.es.EsClient;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-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 {
-
-  private EsClient esClient = Mockito.mock(EsClient.class, RETURNS_DEEP_STUBS);
-  private Random random = new Random();
-  private EsStatusClusterCheck underTest = new EsStatusClusterCheck(esClient);
-
-  @Test
-  public void check_ignores_NodeHealth_arg_and_returns_RED_with_cause_if_an_exception_occurs_checking_ES_cluster_status() {
-    Set<NodeHealth> nodeHealths = ImmutableSet.of(newNodeHealth(NodeHealth.Status.GREEN));
-    when(esClient.clusterHealth(any())).thenThrow(new RuntimeException("Faking an exception occurring while using the EsClient"));
-
-    Health health = new EsStatusClusterCheck(esClient).check(nodeHealths);
-
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("Elasticsearch status is RED (unavailable)");
-  }
-
-  @Test
-  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)
-      .setDetails(NodeDetails.newNodeDetailsBuilder()
-        .setType(random.nextBoolean() ? NodeDetails.Type.APPLICATION : NodeDetails.Type.SEARCH)
-        .setName(randomAlphanumeric(23))
-        .setHost(randomAlphanumeric(23))
-        .setPort(1 + random.nextInt(96))
-        .setStartedAt(1 + random.nextInt(966))
-        .build())
-      .build();
-  }
-
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusNodeCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/EsStatusNodeCheckTest.java
deleted file mode 100644 (file)
index 8eb1c98..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.elasticsearch.cluster.health.ClusterHealthStatus;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.sonar.server.es.EsClient;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class EsStatusNodeCheckTest {
-
-  private EsClient esClient = mock(EsClient.class, Mockito.RETURNS_DEEP_STUBS);
-  private EsStatusNodeCheck underTest = new EsStatusNodeCheck(esClient);
-
-  @Test
-  public void check_ignores_NodeHealth_arg_and_returns_RED_with_cause_if_an_exception_occurs_checking_ES_cluster_status() {
-    EsClient esClient = mock(EsClient.class);
-    when(esClient.clusterHealth(any())).thenThrow(new RuntimeException("Faking an exception occurring while using the EsClient"));
-
-    Health health = new EsStatusNodeCheck(esClient).check();
-
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("Elasticsearch status is RED (unavailable)");
-  }
-
-  @Test
-  public void check_returns_GREEN_without_cause_if_ES_cluster_status_is_GREEN() {
-    when(esClient.clusterHealth(any()).getStatus()).thenReturn(ClusterHealthStatus.GREEN);
-
-    Health health = underTest.check();
-
-    assertThat(health).isEqualTo(Health.GREEN);
-  }
-
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/HealthAssert.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/HealthAssert.java
deleted file mode 100644 (file)
index d46c309..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import com.google.common.collect.ImmutableList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-import org.assertj.core.api.AbstractAssert;
-import org.sonar.process.cluster.health.NodeHealth;
-
-final class HealthAssert extends AbstractAssert<HealthAssert, Health> {
-  private Set<NodeHealth> nodeHealths;
-
-  private HealthAssert(Health actual) {
-    super(actual, HealthAssert.class);
-  }
-
-  public static HealthAssert assertThat(Health actual) {
-    return new HealthAssert(actual);
-  }
-
-  public HealthAssert forInput(Set<NodeHealth> nodeHealths) {
-    this.nodeHealths = nodeHealths;
-
-    return this;
-  }
-
-  public HealthAssert hasStatus(Health.Status expected) {
-    isNotNull();
-
-    if (actual.getStatus() != expected) {
-      failWithMessage(
-        "Expected Status of Health to be <%s> but was <%s> for NodeHealth \n%s",
-        expected,
-        actual.getStatus(),
-        printStatusesAndTypes(this.nodeHealths));
-    }
-
-    return this;
-  }
-
-  public HealthAssert andCauses(String... causes) {
-    isNotNull();
-
-    if (!checkCauses(causes)) {
-      failWithMessage(
-        "Expected causes of Health to contain only \n%s\n but was \n%s\n for NodeHealth \n%s",
-        Arrays.asList(causes),
-        actual.getCauses(),
-        printStatusesAndTypes(this.nodeHealths));
-    }
-
-    return this;
-  }
-
-  private String printStatusesAndTypes(@Nullable Set<NodeHealth> nodeHealths) {
-    if (nodeHealths == null) {
-      return "<null>";
-    }
-    return nodeHealths.stream()
-      // sort by type then status for debugging convenience
-      .sorted(Comparator.<NodeHealth>comparingInt(s1 -> s1.getDetails().getType().ordinal())
-        .thenComparingInt(s -> s.getStatus().ordinal()))
-      .map(s -> ImmutableList.of(s.getDetails().getType().name(), s.getStatus().name()))
-      .map(String::valueOf)
-      .collect(Collectors.joining(","));
-  }
-
-  private boolean checkCauses(String... causes) {
-    if (causes.length != this.actual.getCauses().size()) {
-      return false;
-    }
-    return Objects.equals(new HashSet<>(Arrays.asList(causes)), this.actual.getCauses());
-  }
-}
index 1b266b5b0ac7490ea856ccbb52c73356e42dc0e5..f3a801041ab9c0fbe6904cde44b59fcd779a0dc3 100644 (file)
@@ -32,6 +32,8 @@ import org.junit.Test;
 import org.sonar.process.cluster.health.NodeDetails;
 import org.sonar.process.cluster.health.NodeHealth;
 import org.sonar.process.cluster.health.SharedHealthState;
+import org.sonar.server.common.health.ClusterHealthCheck;
+import org.sonar.server.common.health.NodeHealthCheck;
 import org.sonar.server.platform.NodeInformation;
 
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerSafemodeNodeCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerSafemodeNodeCheckTest.java
deleted file mode 100644 (file)
index 6d221cf..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class WebServerSafemodeNodeCheckTest {
-  private WebServerSafemodeNodeCheck underTest = new WebServerSafemodeNodeCheck();
-
-  @Test
-  public void always_returns_RED_status_with_cause() {
-    Health health = underTest.check();
-
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("SonarQube webserver is not up");
-
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerStatusNodeCheckTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/health/WebServerStatusNodeCheckTest.java
deleted file mode 100644 (file)
index a5eb21e..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.health;
-
-import java.util.Arrays;
-import java.util.Random;
-import org.junit.Test;
-import org.sonar.server.app.RestartFlagHolder;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.DatabaseMigrationState;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class WebServerStatusNodeCheckTest {
-  private final DatabaseMigrationState migrationState = mock(DatabaseMigrationState.class);
-  private final Platform platform = mock(Platform.class);
-  private final RestartFlagHolder restartFlagHolder = mock(RestartFlagHolder.class);
-
-  private final Random random = new Random();
-
-  private WebServerStatusNodeCheck underTest = new WebServerStatusNodeCheck(migrationState, platform, restartFlagHolder);
-
-  @Test
-  public void returns_RED_status_with_cause_if_platform_status_is_not_UP() {
-    Platform.Status[] statusesButUp = Arrays.stream(Platform.Status.values())
-      .filter(s -> s != Platform.Status.UP)
-      .toArray(Platform.Status[]::new);
-    Platform.Status randomStatusButUp = statusesButUp[random.nextInt(statusesButUp.length)];
-    when(platform.status()).thenReturn(randomStatusButUp);
-
-    Health health = underTest.check();
-
-    verifyRedHealthWithCause(health);
-  }
-
-  @Test
-  public void returns_RED_status_with_cause_if_platform_status_is_UP_but_migrationStatus_is_neither_NONE_nor_SUCCEED() {
-    when(platform.status()).thenReturn(Platform.Status.UP);
-    DatabaseMigrationState.Status[] statusesButValidOnes = Arrays.stream(DatabaseMigrationState.Status.values())
-      .filter(s -> s != DatabaseMigrationState.Status.NONE)
-      .filter(s -> s != DatabaseMigrationState.Status.SUCCEEDED)
-      .toArray(DatabaseMigrationState.Status[]::new);
-    DatabaseMigrationState.Status randomInvalidStatus = statusesButValidOnes[random.nextInt(statusesButValidOnes.length)];
-    when(migrationState.getStatus()).thenReturn(randomInvalidStatus);
-
-    Health health = underTest.check();
-
-    verifyRedHealthWithCause(health);
-  }
-
-  @Test
-  public void returns_RED_with_cause_if_platform_status_is_UP_migration_status_is_valid_but_SQ_is_restarting() {
-    when(platform.status()).thenReturn(Platform.Status.UP);
-    when(migrationState.getStatus()).thenReturn(random.nextBoolean() ? DatabaseMigrationState.Status.NONE : DatabaseMigrationState.Status.SUCCEEDED);
-    when(restartFlagHolder.isRestarting()).thenReturn(true);
-
-    Health health = underTest.check();
-
-    verifyRedHealthWithCause(health);
-  }
-
-  @Test
-  public void returns_GREEN_without_cause_if_platform_status_is_UP_migration_status_is_valid_and_SQ_is_not_restarting() {
-    when(platform.status()).thenReturn(Platform.Status.UP);
-    when(migrationState.getStatus()).thenReturn(random.nextBoolean() ? DatabaseMigrationState.Status.NONE : DatabaseMigrationState.Status.SUCCEEDED);
-    when(restartFlagHolder.isRestarting()).thenReturn(false);
-
-    Health health = underTest.check();
-
-    assertThat(health).isEqualTo(Health.GREEN);
-  }
-
-  private void verifyRedHealthWithCause(Health health) {
-    assertThat(health.getStatus()).isEqualTo(Health.Status.RED);
-    assertThat(health.getCauses()).containsOnly("SonarQube webserver is not up");
-  }
-}
index dcf45b6565df2d308772ae1a6f629b65cf9507a4..5c886f2d115681f944942bcc2721239292dfde97 100644 (file)
@@ -24,15 +24,15 @@ import java.util.Random;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.sonar.core.platform.ListContainer;
-import org.sonar.server.health.AppNodeClusterCheck;
-import org.sonar.server.health.CeStatusNodeCheck;
-import org.sonar.server.health.ClusterHealthCheck;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusClusterCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.AppNodeClusterCheck;
+import org.sonar.server.common.health.CeStatusNodeCheck;
+import org.sonar.server.common.health.ClusterHealthCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusClusterCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
 import org.sonar.server.health.HealthCheckerImpl;
-import org.sonar.server.health.NodeHealthCheck;
-import org.sonar.server.health.WebServerStatusNodeCheck;
+import org.sonar.server.common.health.NodeHealthCheck;
+import org.sonar.server.common.health.WebServerStatusNodeCheck;
 import org.sonar.server.platform.NodeInformation;
 
 import static org.assertj.core.api.Assertions.assertThat;
index 1576563b4067f82f565d81b977218e59678c8351..cf6b474efd2ea24c62d3b6c37d6836ac2f444f4c 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.platform.ws;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.server.common.platform.LivenessChecker;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.user.SystemPasscode;
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java
deleted file mode 100644 (file)
index 4a22cad..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.platform.ws;
-
-import org.assertj.core.api.Assertions;
-import org.junit.Test;
-import org.sonar.server.health.CeStatusNodeCheck;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
-import org.sonar.server.health.Health;
-import org.sonar.server.health.WebServerStatusNodeCheck;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class LivenessCheckerImplTest {
-
-  public static final Health RED = Health.builder().setStatus(Health.Status.RED).build();
-
-  private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class);
-  private final WebServerStatusNodeCheck webServerStatusNodeCheck = mock(WebServerStatusNodeCheck.class);
-  private final CeStatusNodeCheck ceStatusNodeCheck = mock(CeStatusNodeCheck.class);
-  private final EsStatusNodeCheck esStatusNodeCheck = mock(EsStatusNodeCheck.class);
-
-  LivenessCheckerImpl underTest = new LivenessCheckerImpl(dbConnectionNodeCheck, webServerStatusNodeCheck, ceStatusNodeCheck, esStatusNodeCheck);
-  LivenessCheckerImpl underTestDCE = new LivenessCheckerImpl(dbConnectionNodeCheck, webServerStatusNodeCheck, ceStatusNodeCheck, null);
-
-  @Test
-  public void fail_when_db_connection_check_fail() {
-    when(dbConnectionNodeCheck.check()).thenReturn(RED);
-
-    Assertions.assertThat(underTest.liveness()).isFalse();
-  }
-
-  @Test
-  public void fail_when_web_check_fail() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-    when(webServerStatusNodeCheck.check()).thenReturn(RED);
-
-    Assertions.assertThat(underTest.liveness()).isFalse();
-  }
-
-  @Test
-  public void fail_when_ce_check_fail() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(ceStatusNodeCheck.check()).thenReturn(RED);
-
-    Assertions.assertThat(underTest.liveness()).isFalse();
-  }
-
-  @Test
-  public void fail_when_es_check_fail() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(esStatusNodeCheck.check()).thenReturn(RED);
-
-    Assertions.assertThat(underTest.liveness()).isFalse();
-  }
-
-  @Test
-  public void success_when_db_web_ce_es_succeed() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(esStatusNodeCheck.check()).thenReturn(Health.GREEN);
-
-    Assertions.assertThat(underTest.liveness()).isTrue();
-  }
-
-  @Test
-  public void success_when_db_web_ce_succeed() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-    when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN);
-    when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN);
-
-    Assertions.assertThat(underTestDCE.liveness()).isTrue();
-  }
-}
index 0d13606641e107746e57b99b0d0a96872d5c309e..1c786d8e35e5aa46f248b55c57cf05f83bdef0a4 100644 (file)
@@ -23,11 +23,11 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.sonar.core.platform.ListContainer;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.EsStatusNodeCheck;
+import org.sonar.server.common.health.DbConnectionNodeCheck;
+import org.sonar.server.common.health.EsStatusNodeCheck;
 import org.sonar.server.health.HealthCheckerImpl;
-import org.sonar.server.health.NodeHealthCheck;
-import org.sonar.server.health.WebServerSafemodeNodeCheck;
+import org.sonar.server.common.health.NodeHealthCheck;
+import org.sonar.server.common.health.WebServerSafemodeNodeCheck;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
index 56a4011d19745ace059af26bb29259ca206331ef..ca11d326bd7632938b93ff41978c38991a593f42 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.ws;
 
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.server.common.platform.LivenessChecker;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.ws.TestRequest;
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java
deleted file mode 100644 (file)
index e0bfc60..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.platform.ws;
-
-import org.assertj.core.api.Assertions;
-import org.junit.Test;
-import org.sonar.server.health.DbConnectionNodeCheck;
-import org.sonar.server.health.Health;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class SafeModeLivenessCheckerImplTest {
-
-  public static final Health RED = Health.builder().setStatus(Health.Status.RED).build();
-  private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class);
-  private final SafeModeLivenessCheckerImpl underTest = new SafeModeLivenessCheckerImpl(dbConnectionNodeCheck);
-
-  @Test
-  public void fail_when_db_connection_check_fail() {
-    when(dbConnectionNodeCheck.check()).thenReturn(RED);
-
-    Assertions.assertThat(underTest.liveness()).isFalse();
-  }
-
-  @Test
-  public void succeed_when_db_connection_check_success() {
-    when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN);
-
-    Assertions.assertThat(underTest.liveness()).isTrue();
-  }
-
-}