Skip to content

Commit

Permalink
SONAR-9741 check app nodes status in api/system/health
Browse files Browse the repository at this point in the history
  • Loading branch information
sns-seb committed Sep 13, 2017
1 parent 9250ab1 commit 3a3df92
Show file tree
Hide file tree
Showing 6 changed files with 562 additions and 41 deletions.
@@ -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();
}
}
}
}
Expand Up @@ -21,8 +21,6 @@

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;
Expand All @@ -31,7 +29,6 @@

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
Expand Down Expand Up @@ -81,35 +78,4 @@ public ClusterHealth checkCluster() {
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;
}
}
}
@@ -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;
}
}
Expand Up @@ -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() {
Expand All @@ -36,9 +43,11 @@ protected void configureModule() {
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,
Expand Down

0 comments on commit 3a3df92

Please sign in to comment.