Merge branch 'stable-3.9' into stable-3.10
* stable-3.9:
Add missing Singleton to ActiveWokersCheck and DeadlockCheck
Disable changesindex check for gerrit replica
Add active http workers check
Change-Id: I19def17a8eee2fb4617256330389862c3bd71368
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java
index 9025af8..d866586 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckConfig.java
@@ -24,7 +24,6 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.Project;
-import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritIsReplica;
@@ -40,6 +39,7 @@
@Singleton
public class HealthCheckConfig {
+ public static final String HEALTHCHECK_PLUGIN_NAME = "healthcheck";
public static final String HEALTHCHECK = "healthcheck";
public static final HealthCheckConfig DEFAULT_CONFIG = new HealthCheckConfig(null);
private static final long HEALTHCHECK_TIMEOUT_DEFAULT = 500L;
@@ -62,11 +62,10 @@
@Inject
public HealthCheckConfig(
PluginConfigFactory configFactory,
- @PluginName String pluginName,
AllProjectsName allProjectsName,
AllUsersName allUsersName,
@GerritIsReplica boolean isReplica) {
- config = configFactory.getGlobalPluginConfig(pluginName);
+ config = configFactory.getGlobalPluginConfig(HEALTHCHECK_PLUGIN_NAME);
this.allProjectsName = allProjectsName;
this.allUsersName = allUsersName;
this.isReplica = isReplica;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
index fa5f14a..4e4cef7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/GlobalHealthCheck.java
@@ -22,9 +22,11 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -33,6 +35,31 @@
private final DynamicSet<HealthCheck> healthChecks;
+ public static class MemoizedStatusSummary implements Supplier<StatusSummary> {
+ private final AtomicReference<StatusSummary> result = new AtomicReference<>();
+ private final HealthCheck check;
+
+ MemoizedStatusSummary(HealthCheck check) {
+ this.check = check;
+ }
+
+ @Override
+ public StatusSummary get() {
+ if (result.get() == null) {
+ result.set(check.run());
+ }
+ return result.get();
+ }
+
+ public StatusSummary getIfCompleted() {
+ StatusSummary completedResult = result.get();
+ return completedResult == null
+ ? new StatusSummary(
+ Result.NOT_RUN, System.currentTimeMillis(), 0L, Collections.emptyMap())
+ : completedResult;
+ }
+ }
+
@Inject
public GlobalHealthCheck(
DynamicSet<HealthCheck> healthChecks,
@@ -47,17 +74,16 @@
public HealthCheck.StatusSummary run() {
Iterable<HealthCheck> iterable = () -> healthChecks.iterator();
long ts = System.currentTimeMillis();
- Map<String, Object> checkToResults =
+ Map<String, MemoizedStatusSummary> checkToResults =
StreamSupport.stream(iterable.spliterator(), false)
- .map(check -> Arrays.asList(check.name(), check.run()))
- .collect(Collectors.toMap(k -> (String) k.get(0), v -> v.get(1)));
+ .collect(Collectors.toMap(HealthCheck::name, MemoizedStatusSummary::new));
long elapsed = System.currentTimeMillis() - ts;
+ Result checkResult = hasAnyFailureOnResults(checkToResults) ? Result.FAILED : Result.PASSED;
+ Map<String, Object> reportedResults =
+ checkToResults.entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().getIfCompleted()));
StatusSummary globalStatus =
- new HealthCheck.StatusSummary(
- hasAnyFailureOnResults(checkToResults) ? Result.FAILED : Result.PASSED,
- ts,
- elapsed,
- checkToResults);
+ new HealthCheck.StatusSummary(checkResult, ts, elapsed, reportedResults);
if (globalStatus.isFailure()) {
failureCounterMetric.increment();
}
@@ -70,13 +96,7 @@
return run().result;
}
- public static boolean hasAnyFailureOnResults(Map<String, Object> results) {
- return results.values().stream()
- .filter(
- res ->
- res instanceof HealthCheck.StatusSummary
- && ((HealthCheck.StatusSummary) res).isFailure())
- .findAny()
- .isPresent();
+ public static boolean hasAnyFailureOnResults(Map<String, MemoizedStatusSummary> results) {
+ return results.values().stream().parallel().anyMatch(res -> res.get().isFailure());
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
index 7c4f8a1..db07320 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheck.java
@@ -30,6 +30,8 @@
FAILED,
@SerializedName("timeout")
TIMEOUT,
+ @SerializedName("not_run")
+ NOT_RUN,
@SerializedName("disabled")
DISABLED;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilter.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilter.java
index 75e48fd..a6f33ea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilter.java
@@ -72,8 +72,7 @@
private void doStatusCheck(HttpServletResponse httpResponse) throws ServletException {
try {
- Response<Map<String, Object>> healthStatus =
- (Response<Map<String, Object>>) statusEndpoint.apply(new ConfigResource());
+ Response<Map<String, Object>> healthStatus = statusEndpoint.apply(new ConfigResource());
String healthStatusJson = gson.toJson(healthStatus.value());
if (healthStatus.statusCode() == HttpServletResponse.SC_OK) {
PrintWriter writer = httpResponse.getWriter();
diff --git a/src/main/resources/Documentation/extensions.md b/src/main/resources/Documentation/extensions.md
index ccc07c7..6477b39 100644
--- a/src/main/resources/Documentation/extensions.md
+++ b/src/main/resources/Documentation/extensions.md
@@ -54,6 +54,11 @@
}
```
+Finally, you will need to register your plugin's healthcheck in the plugin's `Module` class:
+```java
+DynamicSet.bind(binder(), HealthCheck.class).to(FooHealthCheck.class);
+```
+
To build the plugin in the gerrit-CI, as
[documented](https://gerrit-review.googlesource.com/Documentation/dev-plugins.html#_cross_plugin_communication)
by gerrit, you should be configuring your build job as follows:
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java
index ff924e6..1b1cd70 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java
@@ -16,13 +16,20 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.metrics.MetricMaker;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
import com.google.inject.Key;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -34,6 +41,33 @@
protected void configure() {
install(new HealthCheckExtensionApiModule());
install(new Module());
+ DynamicSet.bind(binder(), HealthCheck.class).to(FakeHealthCheck.class);
+ }
+ }
+
+ @Singleton
+ public static class FakeHealthCheck extends AbstractHealthCheck {
+ private long sleep = 0L;
+ private Result result = Result.PASSED;
+
+ @Inject
+ public FakeHealthCheck(
+ ListeningExecutorService executor, HealthCheckConfig config, MetricMaker metricMaker) {
+ super(executor, config, "fake-check", metricMaker);
+ }
+
+ public void setSleep(long sleep) {
+ this.sleep = sleep;
+ }
+
+ public void setResult(Result result) {
+ this.result = result;
+ }
+
+ @Override
+ protected Result doCheck() throws Exception {
+ Thread.sleep(sleep);
+ return result;
}
}
@@ -60,11 +94,11 @@
plugin.getSysInjector().getInstance(Key.get(String.class, PluginName.class)));
}
- protected RestResponse getHealthCheckStatus() throws IOException {
+ protected RestResponse getHealthCheckStatus() throws Exception {
return adminRestSession.get(healthCheckUriPath);
}
- protected RestResponse getHealthCheckStatusAnonymously() throws IOException {
+ protected RestResponse getHealthCheckStatusAnonymously() throws Exception {
return anonymousRestSession.get(healthCheckUriPath);
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
index 86ce328..621ba17 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
@@ -26,9 +26,15 @@
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
import java.io.File;
import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.junit.Before;
@@ -114,6 +120,25 @@
}
@Test
+ public void shouldNotRunChecksWhenOneFails() throws Exception {
+ FakeHealthCheck fakeHealthCheck = plugin.getSysInjector().getInstance(FakeHealthCheck.class);
+ fakeHealthCheck.setResult(HealthCheck.Result.FAILED);
+
+ RestResponse resp = getHealthCheckStatus();
+ resp.assertStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ JsonObject respJson = getResponseJson(resp);
+ Set<Map.Entry<String, JsonElement>> checkResults = respJson.entrySet();
+ List<String> results =
+ checkResults.stream()
+ .map(Map.Entry::getValue)
+ .filter(JsonElement::isJsonObject)
+ .map(j -> j.getAsJsonObject().get("result"))
+ .map(JsonElement::getAsString)
+ .collect(Collectors.toList());
+ assertThat(results).contains("not_run");
+ }
+
+ @Test
public void shouldReturnAuthCheckAsDisabled() throws Exception {
disableCheck(AUTH);
RestResponse resp = getHealthCheckStatus();