mirror of
https://github.com/SonarSource/sonarqube.git
synced 2024-07-29 08:17:28 +02:00
SONAR-9741 return nodes in response of api/system/health
This commit is contained in:
parent
fa0fe58b05
commit
9250ab169f
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.Objects;
|
||||
import java.util.Set;
|
||||
import org.sonar.cluster.health.NodeHealth;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class ClusterHealth {
|
||||
private final Health health;
|
||||
private final Set<NodeHealth> nodes;
|
||||
|
||||
public ClusterHealth(Health health, Set<NodeHealth> nodes) {
|
||||
this.health = requireNonNull(health, "health can't be null");
|
||||
this.nodes = copyOf(requireNonNull(nodes, "nodes can't be null"));
|
||||
}
|
||||
|
||||
public Health getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
public Set<NodeHealth> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ClusterHealth that = (ClusterHealth) o;
|
||||
return Objects.equals(health, that.health) &&
|
||||
Objects.equals(nodes, that.nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(health, nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClusterHealth{" +
|
||||
"health=" + health +
|
||||
", nodes=" + nodes +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ public class Health {
|
||||
/**
|
||||
* The GREEN status without any cause as a constant, for convenience and optimisation.
|
||||
*/
|
||||
static final Health GREEN = newHealthCheckBuilder()
|
||||
public static final Health GREEN = newHealthCheckBuilder()
|
||||
.setStatus(Status.GREEN)
|
||||
.build();
|
||||
|
||||
|
@ -31,5 +31,5 @@ public interface HealthChecker {
|
||||
*
|
||||
* @throws IllegalStateException if clustering is not enabled.
|
||||
*/
|
||||
Health checkCluster();
|
||||
ClusterHealth checkCluster();
|
||||
}
|
||||
|
@ -70,14 +70,15 @@ public class HealthCheckerImpl implements HealthChecker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Health checkCluster() {
|
||||
public ClusterHealth checkCluster() {
|
||||
checkState(!webServer.isStandalone(), "Clustering is not enabled");
|
||||
checkState(sharedHealthState != null, "HealthState instance can't be null when clustering is enabled");
|
||||
|
||||
Set<NodeHealth> nodeHealths = sharedHealthState.readAll();
|
||||
return clusterHealthChecks.stream()
|
||||
Health health = clusterHealthChecks.stream()
|
||||
.map(clusterHealthCheck -> clusterHealthCheck.check(nodeHealths))
|
||||
.reduce(Health.GREEN, HealthReducer.INSTANCE);
|
||||
return new ClusterHealth(health, nodeHealths);
|
||||
}
|
||||
|
||||
private enum HealthReducer implements BinaryOperator<Health> {
|
||||
|
@ -19,47 +19,33 @@
|
||||
*/
|
||||
package org.sonar.server.platform.ws;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import org.sonar.api.server.ws.Request;
|
||||
import org.sonar.api.server.ws.Response;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.server.health.Health;
|
||||
import org.sonar.server.health.HealthChecker;
|
||||
import org.sonar.server.platform.WebServer;
|
||||
import org.sonar.server.ws.WsUtils;
|
||||
import org.sonarqube.ws.WsSystem;
|
||||
|
||||
public class HealthAction implements SystemWsAction {
|
||||
private final HealthChecker healthChecker;
|
||||
private final WebServer webServer;
|
||||
private final HealthActionSupport support;
|
||||
|
||||
public HealthAction(HealthChecker healthChecker) {
|
||||
this.healthChecker = healthChecker;
|
||||
public HealthAction(WebServer webServer, HealthActionSupport support) {
|
||||
this.webServer = webServer;
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void define(WebService.NewController controller) {
|
||||
controller.createAction("health")
|
||||
.setDescription("Provide health status of the current SonarQube instance." +
|
||||
"<p>status: the health status" +
|
||||
" <ul>" +
|
||||
" <li>GREEN: SonarQube is fully operational</li>" +
|
||||
" <li>YELLOW: SonarQube is operational but something must be fixed to be fully operational</li>" +
|
||||
" <li>RED: SonarQube is not operational</li>" +
|
||||
" </ul>" +
|
||||
"</p>")
|
||||
.setSince("6.6")
|
||||
.setResponseExample(Resources.getResource(this.getClass(), "example-health.json"))
|
||||
.setHandler(this);
|
||||
support.define(controller, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Request request, Response response) throws Exception {
|
||||
Health check = healthChecker.checkNode();
|
||||
WsSystem.HealthResponse.Builder responseBuilder = WsSystem.HealthResponse.newBuilder()
|
||||
.setHealth(WsSystem.Health.valueOf(check.getStatus().name()));
|
||||
WsSystem.Cause.Builder causeBuilder = WsSystem.Cause.newBuilder();
|
||||
check.getCauses().forEach(str -> responseBuilder.addCauses(causeBuilder.clear().setMessage(str).build()));
|
||||
|
||||
WsUtils.writeProtobuf(responseBuilder.build(), request, response);
|
||||
if (webServer.isStandalone()) {
|
||||
WsUtils.writeProtobuf(support.checkNodeHealth(), request, response);
|
||||
} else {
|
||||
WsUtils.writeProtobuf(support.checkClusterHealth(), request, response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ public class HealthActionModule extends Module {
|
||||
add(EsStatusClusterCheck.class);
|
||||
|
||||
add(HealthCheckerImpl.class,
|
||||
HealthActionSupport.class,
|
||||
HealthAction.class);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.platform.ws;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.util.Comparator;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.cluster.health.NodeDetails;
|
||||
import org.sonar.cluster.health.NodeHealth;
|
||||
import org.sonar.server.health.ClusterHealth;
|
||||
import org.sonar.server.health.Health;
|
||||
import org.sonar.server.health.HealthChecker;
|
||||
import org.sonarqube.ws.WsSystem;
|
||||
|
||||
import static java.lang.String.valueOf;
|
||||
import static org.sonar.api.utils.DateUtils.formatDateTime;
|
||||
|
||||
public class HealthActionSupport {
|
||||
private static final Comparator<NodeHealth> NODE_HEALTH_COMPARATOR = Comparator.<NodeHealth>comparingInt(s -> s.getDetails().getType().ordinal())
|
||||
.thenComparing(a -> a.getDetails().getName())
|
||||
.thenComparing(a -> a.getDetails().getHost())
|
||||
.thenComparing(a -> a.getDetails().getPort());
|
||||
private final HealthChecker healthChecker;
|
||||
|
||||
public HealthActionSupport(HealthChecker healthChecker) {
|
||||
this.healthChecker = healthChecker;
|
||||
}
|
||||
|
||||
void define(WebService.NewController controller, SystemWsAction handler) {
|
||||
controller.createAction("health")
|
||||
.setDescription("Provide health status of SonarQube." +
|
||||
"<p>Require 'Administer System' permission or authentication with passcode</p>" +
|
||||
"<p> " +
|
||||
" <ul>" +
|
||||
" <li>GREEN: SonarQube is fully operational</li>" +
|
||||
" <li>YELLOW: SonarQube is usable, but it needs attention in order to be fully operational</li>" +
|
||||
" <li>RED: SonarQube is not operational</li>" +
|
||||
" </ul>" +
|
||||
"</p>")
|
||||
.setSince("6.6")
|
||||
.setResponseExample(Resources.getResource(this.getClass(), "example-health.json"))
|
||||
.setHandler(handler);
|
||||
}
|
||||
|
||||
WsSystem.HealthResponse checkNodeHealth() {
|
||||
Health check = healthChecker.checkNode();
|
||||
WsSystem.HealthResponse.Builder responseBuilder = WsSystem.HealthResponse.newBuilder()
|
||||
.setHealth(WsSystem.Health.valueOf(check.getStatus().name()));
|
||||
WsSystem.Cause.Builder causeBuilder = WsSystem.Cause.newBuilder();
|
||||
check.getCauses().forEach(str -> responseBuilder.addCauses(causeBuilder.clear().setMessage(str).build()));
|
||||
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
WsSystem.HealthResponse checkClusterHealth() {
|
||||
ClusterHealth check = healthChecker.checkCluster();
|
||||
return toResponse(check);
|
||||
}
|
||||
|
||||
private static WsSystem.HealthResponse toResponse(ClusterHealth check) {
|
||||
WsSystem.HealthResponse.Builder responseBuilder = WsSystem.HealthResponse.newBuilder();
|
||||
WsSystem.Node.Builder nodeBuilder = WsSystem.Node.newBuilder();
|
||||
WsSystem.Cause.Builder causeBuilder = WsSystem.Cause.newBuilder();
|
||||
|
||||
Health health = check.getHealth();
|
||||
responseBuilder.setHealth(WsSystem.Health.valueOf(health.getStatus().name()));
|
||||
health.getCauses().forEach(str -> responseBuilder.addCauses(toCause(str, causeBuilder)));
|
||||
|
||||
WsSystem.Nodes.Builder nodesBuilder = WsSystem.Nodes.newBuilder();
|
||||
check.getNodes().stream()
|
||||
.sorted(NODE_HEALTH_COMPARATOR)
|
||||
.map(node -> toNode(node, nodeBuilder, causeBuilder))
|
||||
.forEach(nodesBuilder::addNodes);
|
||||
responseBuilder.setNodes(nodesBuilder.build());
|
||||
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
private static WsSystem.Node toNode(NodeHealth nodeHealth, WsSystem.Node.Builder nodeBuilder, WsSystem.Cause.Builder causeBuilder) {
|
||||
nodeBuilder.clear();
|
||||
nodeBuilder.setHealth(WsSystem.Health.valueOf(nodeHealth.getStatus().name()));
|
||||
nodeHealth.getCauses().forEach(str -> nodeBuilder.addCauses(toCause(str, causeBuilder)));
|
||||
NodeDetails details = nodeHealth.getDetails();
|
||||
nodeBuilder
|
||||
.setType(WsSystem.NodeType.valueOf(details.getType().name()))
|
||||
.setName(details.getName())
|
||||
.setHost(details.getHost())
|
||||
.setPort(valueOf(details.getPort()))
|
||||
.setStarted(formatDateTime(details.getStarted()));
|
||||
return nodeBuilder.build();
|
||||
}
|
||||
|
||||
private static WsSystem.Cause toCause(String str, WsSystem.Cause.Builder causeBuilder) {
|
||||
return causeBuilder.clear().setMessage(str).build();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.platform.ws;
|
||||
|
||||
import org.sonar.api.server.ws.Request;
|
||||
import org.sonar.api.server.ws.Response;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.server.ws.WsUtils;
|
||||
|
||||
public class SafeModeHealthAction implements SystemWsAction {
|
||||
private final HealthActionSupport support;
|
||||
|
||||
public SafeModeHealthAction(HealthActionSupport support) {
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void define(WebService.NewController controller) {
|
||||
support.define(controller, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Request request, Response response) throws Exception {
|
||||
WsUtils.writeProtobuf(support.checkNodeHealth(), request, response);
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ public class SafeModeHealthActionModule extends Module {
|
||||
EsStatusNodeCheck.class,
|
||||
|
||||
HealthCheckerImpl.class,
|
||||
HealthAction.class);
|
||||
HealthActionSupport.class,
|
||||
SafeModeHealthAction.class);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,63 @@
|
||||
{
|
||||
"health": "YELLOW",
|
||||
"health": "RED",
|
||||
"causes": [
|
||||
{
|
||||
"message": "Elasticsearch status is YELLOW"
|
||||
"message": "Application node app-1 is RED"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"name": "app-1",
|
||||
"type": "APPLICATION",
|
||||
"host": "192.168.1.1",
|
||||
"port": "999",
|
||||
"started": "2015-08-13T23:34:59+0200",
|
||||
"health": "RED",
|
||||
"causes": [
|
||||
{
|
||||
"message": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "app-2",
|
||||
"type": "APPLICATION",
|
||||
"host": "192.168.1.2",
|
||||
"port": "999",
|
||||
"started": "2015-08-13T23:34:59+0200",
|
||||
"health": "YELLOW",
|
||||
"causes": [
|
||||
{
|
||||
"message": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "es-1",
|
||||
"type": "SEARCH",
|
||||
"host": "192.168.1.3",
|
||||
"port": "999",
|
||||
"started": "2015-08-13T23:34:59+0200",
|
||||
"health": "GREEN",
|
||||
"causes": []
|
||||
},
|
||||
{
|
||||
"name": "es-2",
|
||||
"type": "SEARCH",
|
||||
"host": "192.168.1.4",
|
||||
"port": "999",
|
||||
"started": "2015-08-13T23:34:59+0200",
|
||||
"health": "GREEN",
|
||||
"causes": []
|
||||
},
|
||||
{
|
||||
"name": "es-3",
|
||||
"type": "SEARCH",
|
||||
"host": "192.168.1.5",
|
||||
"port": "999",
|
||||
"started": "2015-08-13T23:34:59+0200",
|
||||
"health": "GREEN",
|
||||
"causes": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.sonar.cluster.health.NodeDetails;
|
||||
import org.sonar.cluster.health.NodeHealth;
|
||||
|
||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ClusterHealthTest {
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Test
|
||||
public void constructor_fails_with_NPE_if_Health_is_null() {
|
||||
expectedException.expect(NullPointerException.class);
|
||||
expectedException.expectMessage("health can't be null");
|
||||
|
||||
new ClusterHealth(null, Collections.emptySet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_fails_with_NPE_if_NodeHealth_is_null() {
|
||||
expectedException.expect(NullPointerException.class);
|
||||
expectedException.expectMessage("nodes can't be null");
|
||||
|
||||
new ClusterHealth(Health.GREEN, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify_getters() {
|
||||
Health health = randomHealth();
|
||||
Set<NodeHealth> nodeHealths = randomNodeHealths();
|
||||
ClusterHealth underTest = new ClusterHealth(health, nodeHealths);
|
||||
|
||||
assertThat(underTest.getHealth()).isSameAs(health);
|
||||
assertThat(underTest.getNodes()).isEqualTo(nodeHealths);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_is_based_on_content() {
|
||||
Health health = randomHealth();
|
||||
Set<NodeHealth> nodeHealths = randomNodeHealths();
|
||||
ClusterHealth underTest = new ClusterHealth(health, nodeHealths);
|
||||
|
||||
assertThat(underTest)
|
||||
.isEqualTo(underTest)
|
||||
.isEqualTo(new ClusterHealth(health, nodeHealths))
|
||||
.isNotEqualTo(new Object())
|
||||
.isNotEqualTo(null)
|
||||
.isNotEqualTo(new ClusterHealth(randomHealth(), randomNodeHealths()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashcode_is_based_on_content() {
|
||||
Health health = randomHealth();
|
||||
Set<NodeHealth> nodeHealths = randomNodeHealths();
|
||||
ClusterHealth underTest = new ClusterHealth(health, nodeHealths);
|
||||
|
||||
assertThat(underTest.hashCode())
|
||||
.isEqualTo(underTest.hashCode())
|
||||
.isNotEqualTo(new ClusterHealth(randomHealth(), randomNodeHealths()).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify_toString() {
|
||||
Health health = randomHealth();
|
||||
Set<NodeHealth> nodeHealths = randomNodeHealths();
|
||||
|
||||
ClusterHealth underTest = new ClusterHealth(health, nodeHealths);
|
||||
|
||||
assertThat(underTest.toString()).isEqualTo("ClusterHealth{health=" + health + ", nodes=" + nodeHealths + "}");
|
||||
}
|
||||
|
||||
private Health randomHealth() {
|
||||
Health.Builder healthBuilder = Health.newHealthCheckBuilder();
|
||||
healthBuilder.setStatus(Health.Status.values()[random.nextInt(Health.Status.values().length)]);
|
||||
IntStream.range(0, random.nextInt(3)).mapToObj(i -> randomAlphanumeric(3)).forEach(healthBuilder::addCause);
|
||||
return healthBuilder.build();
|
||||
}
|
||||
|
||||
private Set<NodeHealth> randomNodeHealths() {
|
||||
return IntStream.range(0, random.nextInt(4)).mapToObj(i -> NodeHealth.newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)])
|
||||
.setDate(1 + random.nextInt(951))
|
||||
.setDetails(
|
||||
NodeDetails.newNodeDetailsBuilder()
|
||||
.setType(random.nextBoolean() ? NodeDetails.Type.SEARCH : NodeDetails.Type.APPLICATION)
|
||||
.setName(randomAlphanumeric(3))
|
||||
.setHost(randomAlphanumeric(4))
|
||||
.setPort(1 + random.nextInt(344))
|
||||
.setStarted(1 + random.nextInt(999))
|
||||
.build())
|
||||
.build()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
@ -145,7 +145,7 @@ public class HealthCheckerImplTest {
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
HealthCheckerImpl underTest = new HealthCheckerImpl(webServer, new NodeHealthCheck[0], new ClusterHealthCheck[0], sharedHealthState);
|
||||
|
||||
assertThat(underTest.checkCluster()).isEqualTo(Health.GREEN);
|
||||
assertThat(underTest.checkCluster().getHealth()).isEqualTo(Health.GREEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -154,7 +154,7 @@ public class HealthCheckerImplTest {
|
||||
List<Health.Status> statuses = IntStream.range(1, 1 + random.nextInt(20)).mapToObj(i -> GREEN).collect(Collectors.toList());
|
||||
HealthCheckerImpl underTest = newClusterHealthCheckerImpl(statuses.stream());
|
||||
|
||||
assertThat(underTest.checkCluster().getStatus())
|
||||
assertThat(underTest.checkCluster().getHealth().getStatus())
|
||||
.describedAs("%s should have been computed from %s statuses", GREEN, statuses)
|
||||
.isEqualTo(GREEN);
|
||||
}
|
||||
@ -169,7 +169,7 @@ public class HealthCheckerImplTest {
|
||||
Collections.shuffle(statuses);
|
||||
HealthCheckerImpl underTest = newClusterHealthCheckerImpl(statuses.stream());
|
||||
|
||||
assertThat(underTest.checkCluster().getStatus())
|
||||
assertThat(underTest.checkCluster().getHealth().getStatus())
|
||||
.describedAs("%s should have been computed from %s statuses", YELLOW, statuses)
|
||||
.isEqualTo(YELLOW);
|
||||
}
|
||||
@ -187,7 +187,7 @@ public class HealthCheckerImplTest {
|
||||
Collections.shuffle(statuses);
|
||||
HealthCheckerImpl underTest = newClusterHealthCheckerImpl(statuses.stream());
|
||||
|
||||
assertThat(underTest.checkCluster().getStatus())
|
||||
assertThat(underTest.checkCluster().getHealth().getStatus())
|
||||
.describedAs("%s should have been computed from %s statuses", RED, statuses)
|
||||
.isEqualTo(RED);
|
||||
}
|
||||
@ -206,7 +206,7 @@ public class HealthCheckerImplTest {
|
||||
|
||||
HealthCheckerImpl underTest = new HealthCheckerImpl(webServer, new NodeHealthCheck[0], clusterHealthChecks, sharedHealthState);
|
||||
|
||||
assertThat(underTest.checkCluster().getCauses()).containsOnly(expectedCauses);
|
||||
assertThat(underTest.checkCluster().getHealth().getCauses()).containsOnly(expectedCauses);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -229,6 +229,18 @@ public class HealthCheckerImplTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkCluster_returns_NodeHealths_returned_by_HealthState() {
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
Set<NodeHealth> nodeHealths = IntStream.range(0, 1 + random.nextInt(4)).mapToObj(i -> randomNodeHealth()).collect(Collectors.toSet());
|
||||
when(sharedHealthState.readAll()).thenReturn(nodeHealths);
|
||||
|
||||
HealthCheckerImpl underTest = new HealthCheckerImpl(webServer, new NodeHealthCheck[0], new ClusterHealthCheck[0], sharedHealthState);
|
||||
|
||||
ClusterHealth clusterHealth = underTest.checkCluster();
|
||||
assertThat(clusterHealth.getNodes()).isEqualTo(nodeHealths);
|
||||
}
|
||||
|
||||
private NodeHealth randomNodeHealth() {
|
||||
return newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)])
|
||||
|
@ -47,7 +47,9 @@ public class HealthActionModuleTest {
|
||||
|
||||
assertThat(classesAddedToContainer(container))
|
||||
.contains(HealthCheckerImpl.class)
|
||||
.contains(HealthAction.class);
|
||||
.contains(HealthActionSupport.class)
|
||||
.contains(HealthAction.class)
|
||||
.doesNotContain(SafeModeHealthAction.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -19,26 +19,49 @@
|
||||
*/
|
||||
package org.sonar.server.platform.ws;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.junit.Test;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.cluster.health.NodeDetails;
|
||||
import org.sonar.cluster.health.NodeHealth;
|
||||
import org.sonar.server.health.ClusterHealth;
|
||||
import org.sonar.server.health.Health;
|
||||
import org.sonar.server.health.HealthChecker;
|
||||
import org.sonar.server.platform.WebServer;
|
||||
import org.sonar.server.ws.TestRequest;
|
||||
import org.sonar.server.ws.TestResponse;
|
||||
import org.sonar.server.ws.WsActionTester;
|
||||
import org.sonar.test.JsonAssert;
|
||||
import org.sonarqube.ws.WsSystem;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
|
||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.sonar.api.utils.DateUtils.formatDateTime;
|
||||
import static org.sonar.api.utils.DateUtils.parseDateTime;
|
||||
import static org.sonar.cluster.health.NodeDetails.newNodeDetailsBuilder;
|
||||
import static org.sonar.cluster.health.NodeHealth.newNodeHealthBuilder;
|
||||
import static org.sonar.server.health.Health.GREEN;
|
||||
import static org.sonar.server.health.Health.newHealthCheckBuilder;
|
||||
import static org.sonar.test.JsonAssert.assertJson;
|
||||
|
||||
public class HealthActionTest {
|
||||
private HealthChecker mockedHealthChecker = mock(HealthChecker.class);
|
||||
private WsActionTester underTest = new WsActionTester(new HealthAction(mockedHealthChecker));
|
||||
private final Random random = new Random();
|
||||
private HealthChecker healthChecker = mock(HealthChecker.class);
|
||||
private WebServer webServer = mock(WebServer.class);
|
||||
private WsActionTester underTest = new WsActionTester(new HealthAction(webServer, new HealthActionSupport(healthChecker)));
|
||||
|
||||
@Test
|
||||
public void verify_definition() {
|
||||
@ -54,26 +77,94 @@ public class HealthActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify_example() {
|
||||
when(mockedHealthChecker.checkNode()).thenReturn(
|
||||
newHealthCheckBuilder()
|
||||
.setStatus(Health.Status.YELLOW)
|
||||
.addCause("Elasticsearch status is YELLOW")
|
||||
.build());
|
||||
TestRequest request = underTest.newRequest();
|
||||
public void verify_response_example() {
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
long time = parseDateTime("2015-08-13T23:34:59+0200").getTime();
|
||||
when(healthChecker.checkCluster())
|
||||
.thenReturn(
|
||||
new ClusterHealth(newHealthCheckBuilder()
|
||||
.setStatus(Health.Status.RED)
|
||||
.addCause("Application node app-1 is RED")
|
||||
.build(),
|
||||
ImmutableSet.of(
|
||||
newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.RED)
|
||||
.addCause("foo")
|
||||
.setDetails(
|
||||
newNodeDetailsBuilder()
|
||||
.setName("app-1")
|
||||
.setType(NodeDetails.Type.APPLICATION)
|
||||
.setHost("192.168.1.1")
|
||||
.setPort(999)
|
||||
.setStarted(time)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(888))
|
||||
.build(),
|
||||
newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.YELLOW)
|
||||
.addCause("bar")
|
||||
.setDetails(
|
||||
newNodeDetailsBuilder()
|
||||
.setName("app-2")
|
||||
.setType(NodeDetails.Type.APPLICATION)
|
||||
.setHost("192.168.1.2")
|
||||
.setPort(999)
|
||||
.setStarted(time)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(888))
|
||||
.build(),
|
||||
newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.GREEN)
|
||||
.setDetails(
|
||||
newNodeDetailsBuilder()
|
||||
.setName("es-1")
|
||||
.setType(NodeDetails.Type.SEARCH)
|
||||
.setHost("192.168.1.3")
|
||||
.setPort(999)
|
||||
.setStarted(time)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(888))
|
||||
.build(),
|
||||
newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.GREEN)
|
||||
.setDetails(
|
||||
newNodeDetailsBuilder()
|
||||
.setName("es-2")
|
||||
.setType(NodeDetails.Type.SEARCH)
|
||||
.setHost("192.168.1.4")
|
||||
.setPort(999)
|
||||
.setStarted(time)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(888))
|
||||
.build(),
|
||||
newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.GREEN)
|
||||
.setDetails(
|
||||
newNodeDetailsBuilder()
|
||||
.setName("es-3")
|
||||
.setType(NodeDetails.Type.SEARCH)
|
||||
.setHost("192.168.1.5")
|
||||
.setPort(999)
|
||||
.setStarted(time)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(888))
|
||||
.build())));
|
||||
|
||||
JsonAssert.assertJson(request.execute().getInput())
|
||||
.isSimilarTo(underTest.getDef().responseExampleAsString());
|
||||
TestResponse response = underTest.newRequest().execute();
|
||||
|
||||
assertJson(response.getInput())
|
||||
.isSimilarTo(underTest.getDef().responseExampleAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void request_returns_status_and_causes_from_HealthChecker_checkNode_method() {
|
||||
public void request_returns_status_and_causes_from_HealthChecker_checkNode_method_when_standalone() {
|
||||
Health.Status randomStatus = Health.Status.values()[new Random().nextInt(Health.Status.values().length)];
|
||||
Health.Builder builder = newHealthCheckBuilder()
|
||||
.setStatus(randomStatus);
|
||||
.setStatus(randomStatus);
|
||||
IntStream.range(0, new Random().nextInt(5)).mapToObj(i -> RandomStringUtils.randomAlphanumeric(3)).forEach(builder::addCause);
|
||||
Health health = builder.build();
|
||||
when(mockedHealthChecker.checkNode()).thenReturn(health);
|
||||
when(healthChecker.checkNode()).thenReturn(health);
|
||||
when(webServer.isStandalone()).thenReturn(true);
|
||||
TestRequest request = underTest.newRequest();
|
||||
|
||||
WsSystem.HealthResponse healthResponse = request.executeProtobuf(WsSystem.HealthResponse.class);
|
||||
@ -81,4 +172,107 @@ public class HealthActionTest {
|
||||
assertThat(health.getCauses()).isEqualTo(health.getCauses());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void response_contains_status_and_causes_from_HealthChecker_checkCluster_when_standalone() {
|
||||
Health.Status randomStatus = Health.Status.values()[random.nextInt(Health.Status.values().length)];
|
||||
String[] causes = IntStream.range(0, random.nextInt(33)).mapToObj(i -> randomAlphanumeric(4)).toArray(String[]::new);
|
||||
Health.Builder healthBuilder = newHealthCheckBuilder()
|
||||
.setStatus(randomStatus);
|
||||
Arrays.stream(causes).forEach(healthBuilder::addCause);
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
when(healthChecker.checkCluster()).thenReturn(new ClusterHealth(healthBuilder.build(), emptySet()));
|
||||
|
||||
WsSystem.HealthResponse clusterHealthResponse = underTest.newRequest().executeProtobuf(WsSystem.HealthResponse.class);
|
||||
assertThat(clusterHealthResponse.getHealth().name()).isEqualTo(randomStatus.name());
|
||||
assertThat(clusterHealthResponse.getCausesList())
|
||||
.extracting(WsSystem.Cause::getMessage)
|
||||
.containsOnly(causes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void response_contains_information_of_nodes_when_clustered() {
|
||||
NodeHealth nodeHealth = randomNodeHealth();
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
when(healthChecker.checkCluster()).thenReturn(new ClusterHealth(GREEN, singleton(nodeHealth)));
|
||||
|
||||
WsSystem.HealthResponse response = underTest.newRequest().executeProtobuf(WsSystem.HealthResponse.class);
|
||||
|
||||
assertThat(response.getNodes().getNodesList())
|
||||
.hasSize(1);
|
||||
WsSystem.Node node = response.getNodes().getNodesList().iterator().next();
|
||||
assertThat(node.getHealth().name()).isEqualTo(nodeHealth.getStatus().name());
|
||||
assertThat(node.getCausesList())
|
||||
.extracting(WsSystem.Cause::getMessage)
|
||||
.containsOnly(nodeHealth.getCauses().stream().toArray(String[]::new));
|
||||
assertThat(node.getName()).isEqualTo(nodeHealth.getDetails().getName());
|
||||
assertThat(node.getHost()).isEqualTo(nodeHealth.getDetails().getHost());
|
||||
assertThat(node.getPort()).isEqualTo(String.valueOf(nodeHealth.getDetails().getPort()));
|
||||
assertThat(node.getStarted()).isEqualTo(formatDateTime(nodeHealth.getDetails().getStarted()));
|
||||
assertThat(node.getType().name()).isEqualTo(nodeHealth.getDetails().getType().name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void response_sort_nodes_by_type_name_host_then_port_when_clustered() {
|
||||
// using created field as a unique identifier. pseudo random value to ensure sorting is not based on created field
|
||||
List<NodeHealth> nodeHealths = new ArrayList<>(Arrays.asList(
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "1_name", "1_host", 1, 99),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "1_name", "2_host", 1, 85),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "1_name", "2_host", 2, 12),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "2_name", "1_host", 1, 6),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "2_name", "1_host", 2, 30),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "2_name", "2_host", 1, 75),
|
||||
randomNodeHealth(NodeDetails.Type.APPLICATION, "2_name", "2_host", 2, 258),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "1_name", "1_host", 1, 963),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "1_name", "1_host", 2, 1),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "1_name", "2_host", 1, 35),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "1_name", "2_host", 2, 45),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "2_name", "1_host", 1, 39),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "2_name", "1_host", 2, 28),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "2_name", "2_host", 1, 66),
|
||||
randomNodeHealth(NodeDetails.Type.SEARCH, "2_name", "2_host", 2, 77)));
|
||||
String[] expected = nodeHealths.stream().map(s -> formatDateTime(new Date(s.getDetails().getStarted()))).toArray(String[]::new);
|
||||
Collections.shuffle(nodeHealths);
|
||||
|
||||
when(webServer.isStandalone()).thenReturn(false);
|
||||
when(healthChecker.checkCluster()).thenReturn(new ClusterHealth(GREEN, new HashSet<>(nodeHealths)));
|
||||
|
||||
WsSystem.HealthResponse response = underTest.newRequest().executeProtobuf(WsSystem.HealthResponse.class);
|
||||
|
||||
assertThat(response.getNodes().getNodesList())
|
||||
.extracting(WsSystem.Node::getStarted)
|
||||
.containsExactly(expected);
|
||||
}
|
||||
|
||||
private NodeHealth randomNodeHealth() {
|
||||
NodeHealth.Builder builder = newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)]);
|
||||
IntStream.range(0, random.nextInt(4)).mapToObj(i -> randomAlphabetic(5)).forEach(builder::addCause);
|
||||
return builder.setDetails(
|
||||
NodeDetails.newNodeDetailsBuilder()
|
||||
.setType(random.nextBoolean() ? NodeDetails.Type.APPLICATION : NodeDetails.Type.SEARCH)
|
||||
.setName(randomAlphanumeric(3))
|
||||
.setHost(randomAlphanumeric(4))
|
||||
.setPort(1 + random.nextInt(3))
|
||||
.setStarted(1 + random.nextInt(23))
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(343))
|
||||
.build();
|
||||
}
|
||||
|
||||
private NodeHealth randomNodeHealth(NodeDetails.Type type, String name, String host, int port, long started) {
|
||||
NodeHealth.Builder builder = newNodeHealthBuilder()
|
||||
.setStatus(NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)]);
|
||||
IntStream.range(0, random.nextInt(4)).mapToObj(i -> randomAlphabetic(5)).forEach(builder::addCause);
|
||||
return builder.setDetails(
|
||||
NodeDetails.newNodeDetailsBuilder()
|
||||
.setType(type)
|
||||
.setName(name)
|
||||
.setHost(host)
|
||||
.setPort(port)
|
||||
.setStarted(started)
|
||||
.build())
|
||||
.setDate(1 + random.nextInt(23))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,7 +44,9 @@ public class SafeModeHealthActionModuleTest {
|
||||
|
||||
assertThat(classesAddedToContainer(container))
|
||||
.contains(HealthCheckerImpl.class)
|
||||
.contains(HealthAction.class);
|
||||
.contains(HealthActionSupport.class)
|
||||
.contains(SafeModeHealthAction.class)
|
||||
.doesNotContain(HealthAction.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.platform.ws;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.junit.Test;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.server.health.Health;
|
||||
import org.sonar.server.health.HealthChecker;
|
||||
import org.sonar.server.ws.TestRequest;
|
||||
import org.sonar.server.ws.TestResponse;
|
||||
import org.sonar.server.ws.WsActionTester;
|
||||
import org.sonarqube.ws.WsSystem;
|
||||
|
||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.sonar.server.health.Health.newHealthCheckBuilder;
|
||||
import static org.sonar.test.JsonAssert.assertJson;
|
||||
|
||||
public class SafeModeHealthActionTest {
|
||||
private final Random random = new Random();
|
||||
private HealthChecker healthChecker = mock(HealthChecker.class);
|
||||
private WsActionTester underTest = new WsActionTester(new SafeModeHealthAction(new HealthActionSupport(healthChecker)));
|
||||
|
||||
@Test
|
||||
public void verify_definition() {
|
||||
WebService.Action definition = underTest.getDef();
|
||||
|
||||
assertThat(definition.key()).isEqualTo("health");
|
||||
assertThat(definition.isPost()).isFalse();
|
||||
assertThat(definition.description()).isNotEmpty();
|
||||
assertThat(definition.since()).isEqualTo("6.6");
|
||||
assertThat(definition.isInternal()).isFalse();
|
||||
assertThat(definition.responseExample()).isNotNull();
|
||||
assertThat(definition.params()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify_response_example() {
|
||||
when(healthChecker.checkNode())
|
||||
.thenReturn(newHealthCheckBuilder()
|
||||
.setStatus(Health.Status.RED)
|
||||
.addCause("Application node app-1 is RED")
|
||||
.build());
|
||||
|
||||
TestResponse response = underTest.newRequest().execute();
|
||||
|
||||
assertJson(response.getInput())
|
||||
.ignoreFields("nodes")
|
||||
.isSimilarTo(underTest.getDef().responseExampleAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void request_returns_status_and_causes_from_HealthChecker_checkNode_method() {
|
||||
Health.Status randomStatus = Health.Status.values()[new Random().nextInt(Health.Status.values().length)];
|
||||
Health.Builder builder = newHealthCheckBuilder()
|
||||
.setStatus(randomStatus);
|
||||
IntStream.range(0, new Random().nextInt(5)).mapToObj(i -> RandomStringUtils.randomAlphanumeric(3)).forEach(builder::addCause);
|
||||
Health health = builder.build();
|
||||
when(healthChecker.checkNode()).thenReturn(health);
|
||||
TestRequest request = underTest.newRequest();
|
||||
|
||||
WsSystem.HealthResponse healthResponse = request.executeProtobuf(WsSystem.HealthResponse.class);
|
||||
assertThat(healthResponse.getHealth().name()).isEqualTo(randomStatus.name());
|
||||
assertThat(health.getCauses()).isEqualTo(health.getCauses());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void response_contains_status_and_causes_from_HealthChecker_checkCluster() {
|
||||
Health.Status randomStatus = Health.Status.values()[random.nextInt(Health.Status.values().length)];
|
||||
String[] causes = IntStream.range(0, random.nextInt(33)).mapToObj(i -> randomAlphanumeric(4)).toArray(String[]::new);
|
||||
Health.Builder healthBuilder = newHealthCheckBuilder()
|
||||
.setStatus(randomStatus);
|
||||
Arrays.stream(causes).forEach(healthBuilder::addCause);
|
||||
when(healthChecker.checkNode()).thenReturn(healthBuilder.build());
|
||||
|
||||
WsSystem.HealthResponse clusterHealthResponse = underTest.newRequest().executeProtobuf(WsSystem.HealthResponse.class);
|
||||
assertThat(clusterHealthResponse.getHealth().name()).isEqualTo(randomStatus.name());
|
||||
assertThat(clusterHealthResponse.getCausesList())
|
||||
.extracting(WsSystem.Cause::getMessage)
|
||||
.containsOnly(causes);
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,11 @@ option optimize_for = SPEED;
|
||||
message HealthResponse {
|
||||
optional Health health = 1;
|
||||
repeated Cause causes = 2;
|
||||
optional Nodes nodes = 3;
|
||||
}
|
||||
|
||||
message Nodes {
|
||||
repeated Node nodes = 1;
|
||||
}
|
||||
|
||||
// GET api/system/status
|
||||
@ -55,3 +60,18 @@ enum Status {
|
||||
DB_MIGRATION_NEEDED = 4;
|
||||
DB_MIGRATION_RUNNING = 5;
|
||||
}
|
||||
|
||||
message Node {
|
||||
optional string name = 1;
|
||||
optional NodeType type = 2;
|
||||
optional string host = 3;
|
||||
optional string port = 4;
|
||||
optional string started = 5;
|
||||
optional Health health = 6;
|
||||
repeated Cause causes = 7;
|
||||
}
|
||||
|
||||
enum NodeType {
|
||||
APPLICATION = 0;
|
||||
SEARCH = 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user