diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2017-09-01 11:39:27 +0200 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2017-09-13 15:50:52 +0200 |
commit | 3a3df92e328aead43dff0a96d43105094d032a63 (patch) | |
tree | 1bf182ff817345c8bd865029e4334485f5bbd8d5 /server/sonar-server | |
parent | 9250ab169f690687d92e54fe2b5142b0376258cd (diff) | |
download | sonarqube-3a3df92e328aead43dff0a96d43105094d032a63.tar.gz sonarqube-3a3df92e328aead43dff0a96d43105094d032a63.zip |
SONAR-9741 check app nodes status in api/system/health
Diffstat (limited to 'server/sonar-server')
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) { |