aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java112
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/health/HealthCheckerImpl.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/health/HealthReducer.java57
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ws/HealthActionModule.java15
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java353
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/platform/ws/HealthActionModuleTest.java32
6 files changed, 562 insertions, 41 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java b/server/sonar-server/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java
new file mode 100644
index 00000000000..f91b51ab5f4
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/health/AppNodeClusterCheck.java
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 org.sonar.cluster.health.NodeDetails;
+import org.sonar.cluster.health.NodeHealth;
+
+import static org.sonar.cluster.health.NodeHealth.Status.GREEN;
+import static org.sonar.cluster.health.NodeHealth.Status.RED;
+import static org.sonar.cluster.health.NodeHealth.Status.YELLOW;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+import static org.sonar.server.health.Health.newHealthCheckBuilder;
+
+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(toSet());
+
+ return Arrays.stream(AppNodeClusterHealthChecks.values())
+ .map(s -> s.check(appNodes))
+ .reduce(Health.GREEN, HealthReducer.INSTANCE);
+ }
+
+ private enum AppNodeClusterHealthChecks implements ClusterHealthCheck {
+ NO_APPLICATION_NODE() {
+ @Override
+ public Health check(Set<NodeHealth> appNodes) {
+ int appNodeCount = appNodes.size();
+ if (appNodeCount == 0) {
+ return newHealthCheckBuilder()
+ .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 newHealthCheckBuilder()
+ .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 = appNodes.stream().filter(s -> s.getStatus() == RED).count();
+ long yellowNodesCount = appNodes.stream().filter(s -> s.getStatus() == YELLOW).count();
+ if (redNodesCount == 0 && yellowNodesCount == 0) {
+ return Health.GREEN;
+ }
+
+ Health.Builder builder = newHealthCheckBuilder();
+ 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 = appNodes.stream().filter(s -> s.getStatus() == GREEN).count();
+ builder.setStatus(greenNodesCount > 0 || yellowNodesCount > 0 ? Health.Status.YELLOW : Health.Status.RED);
+
+ return builder.build();
+ }
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/health/HealthCheckerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/health/HealthCheckerImpl.java
index fbaabfe4f18..6fea8ab8e45 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/health/HealthCheckerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/health/HealthCheckerImpl.java
@@ -21,8 +21,6 @@ package org.sonar.server.health;
import java.util.List;
import java.util.Set;
-import java.util.function.BinaryOperator;
-import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.cluster.health.NodeHealth;
@@ -31,7 +29,6 @@ import org.sonar.server.platform.WebServer;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.copyOf;
-import static org.sonar.server.health.Health.newHealthCheckBuilder;
/**
* Implementation of {@link HealthChecker} based on {@link NodeHealthCheck} and {@link ClusterHealthCheck} instances
@@ -81,35 +78,4 @@ public class HealthCheckerImpl implements HealthChecker {
return new ClusterHealth(health, nodeHealths);
}
- private enum HealthReducer implements BinaryOperator<Health> {
- INSTANCE;
-
- /**
- * According to Javadoc, {@link BinaryOperator} used in method
- * {@link java.util.stream.Stream#reduce(Object, BinaryOperator)} is supposed to be stateless.
- *
- * But as we are sure this {@link BinaryOperator} won't be used on a Stream with {@link Stream#parallel()}
- * feature on, we allow ourselves this optimisation.
- */
- private final Health.Builder builder = newHealthCheckBuilder();
-
- @Override
- public Health apply(Health left, Health right) {
- builder.clear();
- 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-server/src/main/java/org/sonar/server/health/HealthReducer.java b/server/sonar-server/src/main/java/org/sonar/server/health/HealthReducer.java
new file mode 100644
index 00000000000..3767d180d14
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/health/HealthReducer.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.function.BinaryOperator;
+import java.util.stream.Stream;
+
+import static org.sonar.server.health.Health.newHealthCheckBuilder;
+
+public enum HealthReducer implements BinaryOperator<Health> {
+ INSTANCE;
+
+ /**
+ * According to Javadoc, {@link BinaryOperator} used in method
+ * {@link java.util.stream.Stream#reduce(Object, BinaryOperator)} is supposed to be stateless.
+ *
+ * But as we are sure this {@link BinaryOperator} won't be used on a Stream with {@link Stream#parallel()}
+ * feature on, we allow ourselves this optimisation.
+ */
+ private final Health.Builder builder = newHealthCheckBuilder();
+
+ @Override
+ public Health apply(Health left, Health right) {
+ builder.clear();
+ 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-server/src/main/java/org/sonar/server/platform/ws/HealthActionModule.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/HealthActionModule.java
index f73abfe4591..0985a9dda9c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/HealthActionModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/HealthActionModule.java
@@ -20,14 +20,21 @@
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.health.HealthCheckerImpl;
import org.sonar.server.health.WebServerStatusNodeCheck;
+import org.sonar.server.platform.WebServer;
public class HealthActionModule extends Module {
+ private final WebServer webServer;
+
+ public HealthActionModule(WebServer webServer) {
+ this.webServer = webServer;
+ }
@Override
protected void configureModule() {
@@ -36,9 +43,11 @@ public class HealthActionModule extends Module {
DbConnectionNodeCheck.class,
EsStatusNodeCheck.class,
CeStatusNodeCheck.class);
-
- // ClusterHealthCheck implementations
- add(EsStatusClusterCheck.class);
+ if (!webServer.isStandalone()) {
+ // ClusterHealthCheck implementations
+ add(EsStatusClusterCheck.class,
+ AppNodeClusterCheck.class);
+ }
add(HealthCheckerImpl.class,
HealthActionSupport.class,
diff --git a/server/sonar-server/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java b/server/sonar-server/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java
new file mode 100644
index 00000000000..02b627f59e9
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/health/AppNodeClusterCheckTest.java
@@ -0,0 +1,353 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.HashSet;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.assertj.core.api.AbstractAssert;
+import org.junit.Test;
+import org.sonar.cluster.health.NodeDetails;
+import org.sonar.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.cluster.health.NodeHealth.Status.GREEN;
+import static org.sonar.cluster.health.NodeHealth.Status.RED;
+import static org.sonar.cluster.health.NodeHealth.Status.YELLOW;
+
+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))
+ .setStarted(1 + random.nextInt(54))
+ .build())
+ .setDate(1 + random.nextInt(2323))
+ .build();
+ }
+
+ public static HealthAssert assertThat(Health actual) {
+ return new HealthAssert(actual);
+ }
+
+ private static final class HealthAssert extends AbstractAssert<HealthAssert, Health> {
+ private Set<NodeHealth> nodeHealths;
+
+ protected HealthAssert(Health actual) {
+ super(actual, HealthAssert.class);
+ }
+
+ 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()
+ .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-server/src/test/java/org/sonar/server/platform/ws/HealthActionModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/HealthActionModuleTest.java
index e3090b0afef..e4ac3a8448e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/HealthActionModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/HealthActionModuleTest.java
@@ -21,10 +21,12 @@ package org.sonar.server.platform.ws;
import java.util.Collection;
import java.util.List;
+import java.util.Random;
import java.util.stream.Collectors;
import org.junit.Test;
import org.picocontainer.ComponentAdapter;
import org.sonar.core.platform.ComponentContainer;
+import org.sonar.server.health.AppNodeClusterCheck;
import org.sonar.server.health.CeStatusNodeCheck;
import org.sonar.server.health.ClusterHealthCheck;
import org.sonar.server.health.DbConnectionNodeCheck;
@@ -33,19 +35,26 @@ import org.sonar.server.health.EsStatusNodeCheck;
import org.sonar.server.health.HealthCheckerImpl;
import org.sonar.server.health.NodeHealthCheck;
import org.sonar.server.health.WebServerStatusNodeCheck;
+import org.sonar.server.platform.WebServer;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class HealthActionModuleTest {
- private HealthActionModule underTest = new HealthActionModule();
+ private WebServer webServer = mock(WebServer.class);
+ private HealthActionModule underTest = new HealthActionModule(webServer);
@Test
public void verify_action_and_HealthChecker() {
+ boolean standalone = new Random().nextBoolean();
+ when(webServer.isStandalone()).thenReturn(standalone);
ComponentContainer container = new ComponentContainer();
underTest.configure(container);
assertThat(classesAddedToContainer(container))
+ .describedAs("Verifying action and HealthChecker with standalone=%s", standalone)
.contains(HealthCheckerImpl.class)
.contains(HealthActionSupport.class)
.contains(HealthAction.class)
@@ -54,6 +63,8 @@ public class HealthActionModuleTest {
@Test
public void verify_installed_NodeHealthChecks_implementations() {
+ boolean standalone = new Random().nextBoolean();
+ when(webServer.isStandalone()).thenReturn(standalone);
ComponentContainer container = new ComponentContainer();
underTest.configure(container);
@@ -68,15 +79,28 @@ public class HealthActionModuleTest {
}
@Test
- public void verify_installed_ClusterHealthChecks_implementations() {
+ public void verify_installed_ClusterHealthChecks_implementations_in_standalone() {
+ when(webServer.isStandalone()).thenReturn(true);
+ ComponentContainer container = new ComponentContainer();
+
+ underTest.configure(container);
+
+ List<Class<?>> checks = classesAddedToContainer(container).stream().filter(ClusterHealthCheck.class::isAssignableFrom).collect(Collectors.toList());
+ assertThat(checks).isEmpty();
+ }
+
+ @Test
+ public void verify_installed_ClusterHealthChecks_implementations_in_clustering() {
+ when(webServer.isStandalone()).thenReturn(false);
ComponentContainer container = new ComponentContainer();
underTest.configure(container);
List<Class<?>> checks = classesAddedToContainer(container).stream().filter(ClusterHealthCheck.class::isAssignableFrom).collect(Collectors.toList());
assertThat(checks)
- .hasSize(1)
- .contains(EsStatusClusterCheck.class);
+ .hasSize(2)
+ .contains(EsStatusClusterCheck.class)
+ .contains(AppNodeClusterCheck.class);
}
private List<Class<?>> classesAddedToContainer(ComponentContainer container) {