Merge branch 'stable-3.13' into stable-3.14 * stable-3.13: Adapt tests to updated Gerrit API Change-Id: If498339806d346d042e283c14fe587dd4065ba7d
diff --git a/README.md b/README.md index bd49524..464eb52 100644 --- a/README.md +++ b/README.md
@@ -131,7 +131,7 @@ ``` [healtcheck] - failFileFlaPath="data/healthcheck/fail" + failFileFlagPath="data/healthcheck/fail" ``` This will make the healthcheck endpoint return 500 even if the node is otherwise
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckExceptionHook.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckExceptionHook.java new file mode 100644 index 0000000..b74b591 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckExceptionHook.java
@@ -0,0 +1,47 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.healthcheck; + +import static com.googlesource.gerrit.plugins.healthcheck.filter.HealthCheckStatusFilter.GSON; +import static com.googlesource.gerrit.plugins.healthcheck.filter.HealthCheckStatusFilter.JSON_MAGIC_STRING; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + +import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.server.ExceptionHook; +import com.google.inject.AbstractModule; +import java.util.Optional; + +public class HealthCheckExceptionHook implements ExceptionHook { + + @Override + public Optional<Status> getStatus(Throwable throwable) { + if (throwable instanceof HealthCheckFailedException exc) { + return Optional.of( + Status.create( + SC_INTERNAL_SERVER_ERROR, JSON_MAGIC_STRING + GSON.toJson(exc.getResult()))); + } + return Optional.empty(); + } + + static AbstractModule module() { + return new AbstractModule() { + + @Override + protected void configure() { + DynamicSet.bind(binder(), ExceptionHook.class).to(HealthCheckExceptionHook.class); + } + }; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckFailedException.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckFailedException.java new file mode 100644 index 0000000..2ea0c14 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckFailedException.java
@@ -0,0 +1,34 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.healthcheck; + +import java.util.Map; + +public class HealthCheckFailedException extends Exception { + private final Map<String, Object> result; + + public HealthCheckFailedException(Map<String, Object> result) { + this.result = result; + } + + Map<String, Object> getResult() { + return result; + } + + @Override + public String toString() { + return "Health check failed: " + result; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/Module.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/Module.java index 54cd5a0..d13108a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/Module.java
@@ -20,6 +20,7 @@ @Override protected void configure() { + install(HealthCheckExceptionHook.module()); install(new HealthCheckSubsystemsModule()); install(new HealthCheckApiModule()); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java index 58ab01b..fe73cc5 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/api/HealthCheckStatusEndpoint.java
@@ -19,6 +19,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckFailedException; import com.googlesource.gerrit.plugins.healthcheck.check.GlobalHealthCheck; import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck; import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result; @@ -45,21 +46,23 @@ public Response<Map<String, Object>> apply(ConfigResource resource) throws AuthException, BadRequestException, ResourceConflictException, Exception { if (failFlagFileExists()) { - return Response.withStatusCode( - HttpServletResponse.SC_INTERNAL_SERVER_ERROR, Map.of("reason", "Fail Flag File exists")); + throw new HealthCheckFailedException(Map.of("reason", "Fail Flag File exists")); } HealthCheck.StatusSummary globalHealthCheckStatus = healthChecks.run(); Map<String, Object> result = globalHealthCheckStatus.subChecks(); result.put("ts", globalHealthCheckStatus.ts()); result.put("elapsed", globalHealthCheckStatus.elapsed()); - return Response.withStatusCode(getHTTPResultCode(globalHealthCheckStatus), result); + return Response.withStatusCode(getHTTPResultCode(globalHealthCheckStatus, result), result); } - private int getHTTPResultCode(HealthCheck.StatusSummary checkStatus) { - return checkStatus.result() == Result.FAILED - ? HttpServletResponse.SC_INTERNAL_SERVER_ERROR - : HttpServletResponse.SC_OK; + private int getHTTPResultCode(HealthCheck.StatusSummary checkStatus, Map<String, Object> result) + throws HealthCheckFailedException { + if (checkStatus.result() == Result.FAILED) { + throw new HealthCheckFailedException(result); + } + + return HttpServletResponse.SC_OK; } private boolean failFlagFileExists() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java index ffb7d16..63fce89 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AuthHealthCheck.java
@@ -58,7 +58,7 @@ protected Result doCheck() throws Exception { AuthRequest authRequest = authRequestFactory.createForUser(username); authRequest.setPassword(password); - realm.authenticate(authRequest); + var unused = realm.authenticate(authRequest); Optional<AccountState> accountState = byIdCache.getByUsername(username); if (!accountState.isPresent()) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/BlockedThreadsConfigurator.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/BlockedThreadsConfigurator.java index 8bfe53c..e122066 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/BlockedThreadsConfigurator.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/BlockedThreadsConfigurator.java
@@ -98,7 +98,7 @@ if (specsClassified.size() == 1) { Map.Entry<Boolean, List<Threshold>> entry = specsClassified.entrySet().iterator().next(); - return Boolean.TRUE == entry.getKey() + return Boolean.TRUE.equals(entry.getKey()) ? deduplicatePrefixes(entry.getValue()) : deduplicateGlobal(entry.getValue()); }
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 1b52d4c..5415bc2 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
@@ -20,16 +20,20 @@ import com.google.gerrit.httpd.AllRequestFilter; import com.google.gerrit.httpd.restapi.RestApiServlet; import com.google.gerrit.json.OutputFormat; +import com.google.gerrit.server.ExceptionHook; import com.google.gerrit.server.config.ConfigResource; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gson.Gson; import com.google.inject.Inject; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckExceptionHook; import com.googlesource.gerrit.plugins.healthcheck.api.HealthCheckStatusEndpoint; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; @@ -39,22 +43,27 @@ import org.eclipse.jgit.lib.Config; public class HealthCheckStatusFilter extends AllRequestFilter { + public static final Gson GSON = OutputFormat.JSON.newGsonBuilder().create(); + public static final String JSON_MAGIC_STRING = + new String(RestApiServlet.JSON_MAGIC, StandardCharsets.UTF_8); + private final HealthCheckStatusEndpoint statusEndpoint; - private final Gson gson; private final String pluginName; private final Config cfg; private final String uriPattern; + private final HealthCheckExceptionHook exceptionHook; @Inject public HealthCheckStatusFilter( HealthCheckStatusEndpoint statusEndpoint, @PluginName String pluginName, - @GerritServerConfig Config cfg) { + @GerritServerConfig Config cfg, + HealthCheckExceptionHook exceptionHook) { this.statusEndpoint = statusEndpoint; - this.gson = OutputFormat.JSON.newGsonBuilder().create(); this.pluginName = pluginName; this.cfg = cfg; this.uriPattern = getUriPattern(); + this.exceptionHook = exceptionHook; } private static List<String> extractUriPrefixes(String[] listenUrls) { @@ -120,19 +129,25 @@ return httpServletRequest.getRequestURI().matches(uriPattern); } - private void doStatusCheck(HttpServletResponse httpResponse) throws ServletException { + private void doStatusCheck(HttpServletResponse httpResponse) + throws ServletException, IOException { try { Response<Map<String, Object>> healthStatus = statusEndpoint.apply(new ConfigResource()); - String healthStatusJson = gson.toJson(healthStatus.value()); + String healthStatusJson = GSON.toJson(healthStatus.value()); if (healthStatus.statusCode() == HttpServletResponse.SC_OK) { PrintWriter writer = httpResponse.getWriter(); - writer.print(new String(RestApiServlet.JSON_MAGIC)); + writer.print(JSON_MAGIC_STRING); writer.print(healthStatusJson); } else { httpResponse.sendError(healthStatus.statusCode(), healthStatusJson); } } catch (Exception e) { - throw new ServletException(e); + Optional<ExceptionHook.Status> status = exceptionHook.getStatus(e); + if (status.isPresent()) { + httpResponse.sendError(status.get().statusCode(), status.get().statusMessage()); + } else { + throw new ServletException(e); + } } } }
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 1b1cd70..649e83b 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/AbstractHealthCheckIntegrationTest.java
@@ -86,7 +86,7 @@ for (int i = 0; i < numChanges; i++) { createChange("refs/for/master"); } - accountCreator.create(config.getUsername(HealthCheckNames.AUTH)); + var unused = accountCreator.create(config.getUsername(HealthCheckNames.AUTH)); healthCheckUriPath = String.format(
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java index af09d07..4185a21 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckStatusEndpointTest.java
@@ -15,6 +15,7 @@ package com.googlesource.gerrit.plugins.healthcheck; import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG; import com.google.common.util.concurrent.ListeningExecutorService; @@ -102,7 +103,7 @@ } @Test - public void shouldReturnServerErrorWhenOneChecksTimesOut() throws Exception { + public void shouldThrowHealthCheckFailedWhenOneChecksTimesOut() throws Exception { Injector injector = testInjector( new AbstractModule() { @@ -124,13 +125,12 @@ HealthCheckStatusEndpoint healthCheckApi = injector.getInstance(HealthCheckStatusEndpoint.class); - Response<?> resp = healthCheckApi.apply(null); - assertThat(resp.statusCode()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + assertThrows(HealthCheckFailedException.class, () -> healthCheckApi.apply(null)); } @Test - public void shouldReturnServerErrorWhenAtLeastOneCheckIsFailing() throws Exception { + public void shouldThrowHealthCheckFailedWhenAtLeastOneCheckIsFailing() throws Exception { Injector injector = testInjector( new AbstractModule() { @@ -166,9 +166,8 @@ HealthCheckStatusEndpoint healthCheckApi = injector.getInstance(HealthCheckStatusEndpoint.class); - Response<?> resp = healthCheckApi.apply(null); - assertThat(resp.statusCode()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + assertThrows(HealthCheckFailedException.class, () -> healthCheckApi.apply(null)); } private Injector testInjector(AbstractModule testModule) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java index 60c233a..1bfc2eb 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java
@@ -118,7 +118,7 @@ healtchCheckConfig, injector.getInstance(ThreadSettingsConfig.class), injector.getInstance(MetricRegistry.class), - new TestMetricMaker()); + TestMetricMaker.getInstance()); } private Injector testInjector(AbstractModule testModule) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilterTest.java index 9d44f21..6dc606d 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilterTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/filter/HealthCheckStatusFilterTest.java
@@ -58,7 +58,7 @@ private HealthCheckStatusFilter createFilter(List<String> listenUrl) { Config cfg = new Config(); cfg.setStringList("httpd", null, "listenUrl", listenUrl); - return new HealthCheckStatusFilter(null, "healthcheck", cfg); + return new HealthCheckStatusFilter(null, "healthcheck", cfg, null); } private HttpServletRequest createRequest(String path) {