Read heathcheck settings from plugin configuration
Some settings are specific to the external healthcheck; the list of
repos and the periodOfTime. These settings are defined in the plugin's
configuration file.
Read custom settings from the plugin's config file. "Core" settings
like the `enabled` flag and the timeout are still defined in the
healthcheck plugin's config file.
Bug: Issue 312895374
Change-Id: I8454b19770e4cba986b1e33bddf15f00bc7a8f70
diff --git a/BUILD b/BUILD
index 9a37098..664b668 100644
--- a/BUILD
+++ b/BUILD
@@ -38,6 +38,7 @@
":pull_replication_util",
"//plugins/delete-project",
"//plugins/events-broker",
+ "//plugins/healthcheck",
"//plugins/replication",
],
)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheck.java
index ea6fdb5..6a4eb9a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheck.java
@@ -14,24 +14,60 @@
package com.googlesource.gerrit.plugins.replication.pull.health;
+import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.config.ConfigUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
import com.googlesource.gerrit.plugins.healthcheck.check.AbstractHealthCheck;
+import com.googlesource.gerrit.plugins.replication.MergedConfigResource;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.Config;
@Singleton
public class PullReplicationTasksHealthCheck extends AbstractHealthCheck {
+ private static final long DEFAULT_PERIOD_OF_TIME_SECS = 10L;
+ public static final String HEALTHCHECK_NAME_SUFFIX = "-outstanding-tasks";
+ public static final String PROJECTS_FILTER_FIELD = "projects";
+ public static final String PERIOD_OF_TIME_FIELD = "periodOfTime";
+ private final Set<String> projects;
+ private final long periodOfTimeSec;
@Inject
public PullReplicationTasksHealthCheck(
ListeningExecutorService executor,
- HealthCheckConfig config,
+ HealthCheckConfig healthCheckConfig,
+ MergedConfigResource configResource,
@PluginName String name,
MetricMaker metricMaker) {
- super(executor, config, name + "-tasks", metricMaker);
+ super(executor, healthCheckConfig, name + HEALTHCHECK_NAME_SUFFIX, metricMaker);
+ String healthCheckName = name + HEALTHCHECK_NAME_SUFFIX;
+
+ Config replicationConfig = configResource.getConfig();
+ this.projects =
+ Set.of(
+ replicationConfig.getStringList(
+ HealthCheckConfig.HEALTHCHECK, healthCheckName, PROJECTS_FILTER_FIELD));
+ this.periodOfTimeSec =
+ ConfigUtil.getTimeUnit(
+ replicationConfig,
+ HealthCheckConfig.HEALTHCHECK,
+ healthCheckName,
+ PERIOD_OF_TIME_FIELD,
+ DEFAULT_PERIOD_OF_TIME_SECS,
+ TimeUnit.SECONDS);
+ }
+
+ public long getPeriodOfTimeSec() {
+ return periodOfTimeSec;
+ }
+
+ public Set<String> getProjects() {
+ return ImmutableSet.copyOf(projects);
}
@Override
diff --git a/src/main/resources/Documentation/healthcheck.md b/src/main/resources/Documentation/healthcheck.md
new file mode 100644
index 0000000..aab68bd
--- /dev/null
+++ b/src/main/resources/Documentation/healthcheck.md
@@ -0,0 +1,41 @@
+@PLUGIN@ health checks
+==============
+
+The @PLUGIN@ plugin registers the `pull-replication-outstanding-tasks`
+healthcheck.
+
+Health check configuration
+--------------------------
+
+The configuration of the health check is split across two files.
+- The "standard" properties commonly available to all other checks
+of the `healthcheck` plugin. These are set in the `healthcheck` plugin's
+[config file](https://gerrit.googlesource.com/plugins/healthcheck/+/refs/heads/master/src/main/resources/Documentation/config.md#settings).
+- Settings specific to the check are set in the plugin's [config file](./config.md#file-pluginconfig).
+
+
+The health check can be configured as follows:
+- `healthcheck.@PLUGIN@-outstanding-tasks.projects`: The repo(s) that
+the health check will track pending replication tasks against. Multiple
+entries are supported.
+- `healthcheck.@PLUGIN@-outstanding-tasks.periodOfTime`: The time for
+which the check needs to be successful, in order for the instance to be
+marked healthy. If the time unit is omitted it defaults to seconds.
+Values should use common unit suffixes to express their setting:
+
+* ms, milliseconds
+* s, sec, second, seconds
+* m, min, minute, minutes
+* h, hr, hour, hours
+
+
+Useful information
+------------------
+
+- The health check is registered only when the [healthcheck](https://gerrit.googlesource.com/plugins/healthcheck) plugin
+is installed. If the `healthcheck` plugin is not installed, then the
+check registration is skipped during load of the pull-replication
+plugin.
+- Because the pull-replication healthcheck depends on the `healthcheck` plugin, renaming/removing the `healthcheck`
+jar file is not supported during runtime. Doing so can lead to unpredictable behaviour of your gerrit instance.
+
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheckTest.java
new file mode 100644
index 0000000..97eda7c
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/health/PullReplicationTasksHealthCheckTest.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2024 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.replication.pull.health;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.replication.pull.health.PullReplicationTasksHealthCheck.HEALTHCHECK_NAME_SUFFIX;
+import static com.googlesource.gerrit.plugins.replication.pull.health.PullReplicationTasksHealthCheck.PERIOD_OF_TIME_FIELD;
+import static com.googlesource.gerrit.plugins.replication.pull.health.PullReplicationTasksHealthCheck.PROJECTS_FILTER_FIELD;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckExtensionApiModule;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import com.googlesource.gerrit.plugins.replication.ConfigResource;
+import com.googlesource.gerrit.plugins.replication.MergedConfigResource;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class PullReplicationTasksHealthCheckTest {
+ private static final String PLUGIN_NAME = "pull-replication";
+ private static final String SECTION_NAME = PLUGIN_NAME + HEALTHCHECK_NAME_SUFFIX;
+
+ @Test
+ public void shouldAlwaysPass() {
+ List<String> projectsToCheck = List.of("foo", "bar/baz");
+ int periodOfCheckSec = 10;
+ Injector injector = testInjector(new TestModule(projectsToCheck, 10 + " sec"));
+
+ PullReplicationTasksHealthCheck check =
+ injector.getInstance(PullReplicationTasksHealthCheck.class);
+
+ assertThat(check.run().result).isEqualTo(HealthCheck.Result.PASSED);
+ assertThat(check.getProjects()).containsExactlyElementsIn(projectsToCheck);
+ assertThat(check.getPeriodOfTimeSec()).isEqualTo(periodOfCheckSec);
+ }
+
+ private Injector testInjector(AbstractModule testModule) {
+ return Guice.createInjector(new HealthCheckExtensionApiModule(), testModule);
+ }
+
+ private class TestModule extends AbstractModule {
+ Config config;
+ MergedConfigResource configResource;
+ private final HealthCheckConfig healthCheckConfig = HealthCheckConfig.DEFAULT_CONFIG;
+
+ public TestModule(List<String> projects, String periodOfTime) {
+ this.config = new Config();
+ config.setStringList(
+ HealthCheckConfig.HEALTHCHECK, SECTION_NAME, PROJECTS_FILTER_FIELD, projects);
+ config.setString(
+ HealthCheckConfig.HEALTHCHECK, SECTION_NAME, PERIOD_OF_TIME_FIELD, periodOfTime);
+ configResource =
+ MergedConfigResource.withBaseOnly(
+ new ConfigResource() {
+ @Override
+ public Config getConfig() {
+ return config;
+ }
+
+ @Override
+ public String getVersion() {
+ return "";
+ }
+ });
+ }
+
+ @Override
+ protected void configure() {
+ bind(Config.class).toInstance(config);
+ bind(MergedConfigResource.class).toInstance(configResource);
+ bind(MetricMaker.class).toInstance(new DisabledMetricMaker());
+ bind(HealthCheckConfig.class).toInstance(healthCheckConfig);
+ bind(String.class).annotatedWith(PluginName.class).toInstance(PLUGIN_NAME);
+ }
+ }
+}