Introduce the changes index health check stub

This patch introduces the change index healtch check stub that:
* reports `disabled` when `changesindex` healthcheck is disabled
* reports `disabled` when different than `lucene` index is used as ATM
  there are no means to verify other indexes
* ALWAYS returns `passed` (no real check yet)

Notes:
* changes (as the most important) is examined only ATM
* documentation was updated to contain the check in question.

Bug: Issue 40015289
Change-Id: Ibbf77115e801e9fc01ec264b01a656b2a2b29a23
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
index aea007a..d88c445 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -19,6 +19,7 @@
 import com.googlesource.gerrit.plugins.healthcheck.check.ActiveWorkersCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.AuthHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.BlockedThreadsCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.ChangesIndexHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.DeadlockCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
@@ -36,6 +37,7 @@
     bindChecker(ActiveWorkersCheck.class);
     bindChecker(DeadlockCheck.class);
     bindChecker(BlockedThreadsCheck.class);
+    bindChecker(ChangesIndexHealthCheck.class);
 
     install(BlockedThreadsCheck.SUB_CHECKS);
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java
new file mode 100644
index 0000000..584787a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 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.check;
+
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.CHANGES_INDEX;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.index.IndexType;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class ChangesIndexHealthCheck extends AbstractHealthCheck {
+  private static final Logger log = LoggerFactory.getLogger(QueryChangesHealthCheck.class);
+
+  private final boolean isLuceneIndex;
+
+  @Inject
+  ChangesIndexHealthCheck(
+      @GerritServerConfig Config cfg,
+      ListeningExecutorService executor,
+      HealthCheckConfig config,
+      MetricMaker metricMaker) {
+    super(executor, config, CHANGES_INDEX, metricMaker);
+    this.isLuceneIndex = isIndexTypeLucene(cfg);
+  }
+
+  @Override
+  protected Result doCheck() throws Exception {
+    return isLuceneIndex ? Result.PASSED : Result.DISABLED;
+  }
+
+  private static boolean isIndexTypeLucene(Config cfg) {
+    IndexType indexType = new IndexType(cfg.getString("index", null, "type"));
+    boolean isLucene = indexType.isLucene();
+    if (!isLucene) {
+      log.warn(
+          "Configured index type [{}] is not supported for index health check therefore it is disabled.",
+          indexType);
+    }
+    return isLucene;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
index 4771942..5a4947f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
@@ -23,4 +23,5 @@
   String DEADLOCK = "deadlock";
   String BLOCKEDTHREADS = "blockedthreads";
   String GLOBAL = "global";
+  String CHANGES_INDEX = "changesindex";
 }
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 0b4f05f..7f7d549 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -56,6 +56,8 @@
 - `activeworkers`: check the number of active worker threads and the ability to create a new one
 - `deadlock` : check if Java deadlocks are reported by the JVM
 - `blockedthreads` : check the number of blocked threads
+- `changesindex` : check if the lucene based changes indexes (open and closed) are operable
+   (examines index lock files)
 
 Each check name can be disabled by setting the `enabled` parameter to **false**,
 by default this parameter is set to **true**
diff --git a/src/main/resources/Documentation/metrics.md b/src/main/resources/Documentation/metrics.md
index 76e6eb5..f250d1b 100644
--- a/src/main/resources/Documentation/metrics.md
+++ b/src/main/resources/Documentation/metrics.md
@@ -52,6 +52,10 @@
 # TYPE plugins_healthcheck_blockedthreads_latest_measured_latency gauge
 plugins_healthcheck_blockedthreads_latest_measured_latency 6.0
 
+# HELP plugins_healthcheck_changesindex_latest_measured_latency Generated from Dropwizard metric import (metric=plugins/healthcheck/changesindex/latency, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl0$1)
+# TYPE plugins_healthcheck_changesindex_latest_measured_latency gauge
+plugins_healthcheck_changesindex_latest_measured_latency 3.0
+
 # HELP plugins_healthcheck_jgit_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/jgit/failure, type=com.codahale.metrics.Meter)
 # TYPE plugins_healthcheck_jgit_failure_total counter
 plugins_healthcheck_jgit_failure_total 3.0
@@ -63,6 +67,10 @@
 # HELP plugins_healthcheck_blockedthreads_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/blockedthreads/failure, type=com.codahale.metrics.Meter)
 # TYPE plugins_healthcheck_blockedthreads_failure_total counter
 plugins_healthcheck_blockedthreads_failure_total 1.0
+
+# HELP plugins_healthcheck_changesindex_failure_total Generated from Dropwizard metric import (metric=plugins/healthcheck/changesindex/failure, type=com.codahale.metrics.Meter)
+# TYPE plugins_healthcheck_changesindex_failure_total counter
+plugins_healthcheck_changesindex_failure_total 1.0
 ```
 
 Note that additionally to the default `blockedthreads` metrics pair failures counter will reported for
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 17f1e10..1aa698c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.ACTIVEWORKERS;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.AUTH;
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.CHANGES_INDEX;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.JGIT;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.QUERYCHANGES;
 
@@ -224,6 +225,32 @@
     assertThat(respBody.get("reason").getAsString()).isEqualTo("Fail Flag File exists");
   }
 
+  @Test
+  public void shouldReturnChangesIndexCheck() throws Exception {
+    RestResponse resp = getHealthCheckStatus();
+    resp.assertOK();
+
+    assertCheckResult(getResponseJson(resp), CHANGES_INDEX, "passed");
+  }
+
+  @Test
+  public void shouldReturnChangesIndexCheckAsDisabled() throws Exception {
+    disableCheck(CHANGES_INDEX);
+    RestResponse resp = getHealthCheckStatus();
+
+    resp.assertOK();
+    assertCheckResult(getResponseJson(resp), CHANGES_INDEX, "disabled");
+  }
+
+  @Test
+  @GerritConfig(name = "index.type", value = "fake")
+  public void shouldReturnChangesIndexCheckAsDisabledWhenIndexIsNotLucene() throws Exception {
+    RestResponse resp = getHealthCheckStatus();
+
+    resp.assertOK();
+    assertCheckResult(getResponseJson(resp), CHANGES_INDEX, "disabled");
+  }
+
   private void createFailFileFlag(String path) throws IOException {
     File file = new File(path);
     file.createNewFile();