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); + } + } +}