Implement JGit health-check

Check that JGit is able to open and read objects
from All-Projects repository.

Change-Id: Ibc7dc325b2f3529636b6ed8297bf3ca13269b444
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 9e6ebc5..55bf18b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -17,12 +17,18 @@
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.AbstractModule;
 import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck;
+import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.ReviewDbHealthCheck;
 
 public class HealthCheckSubsystemsModule extends AbstractModule {
 
   @Override
   protected void configure() {
-    DynamicSet.bind(binder(), HealthCheck.class).to(ReviewDbHealthCheck.class);
+    bindChecker(ReviewDbHealthCheck.class);
+    bindChecker(JGitHealthCheck.class);
+  }
+
+  private void bindChecker(Class<? extends HealthCheck> healthCheckClass) {
+    DynamicSet.bind(binder(), HealthCheck.class).to(healthCheckClass);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java
new file mode 100644
index 0000000..f186092
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractHealthCheck.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2019 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractHealthCheck implements HealthCheck {
+  private static final Logger log = LoggerFactory.getLogger(AbstractHealthCheck.class);
+  private final String name;
+
+  protected AbstractHealthCheck(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String name() {
+    return name;
+  }
+
+  @Override
+  public Status run() {
+    Result healthy;
+    long ts = System.currentTimeMillis();
+    try {
+      healthy = doCheck();
+    } catch (Exception e) {
+      log.warn("Check {} failed", name, e);
+      healthy = Result.FAILED;
+    }
+    return new Status(healthy, ts, System.currentTimeMillis() - ts);
+  }
+
+  protected abstract Result doCheck() throws Exception;
+}
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 e33b755..1c88f0e 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
@@ -17,4 +17,5 @@
 public interface HealthCheckNames {
 
   String REVIEWDB = "reviewdb";
+  String JGIT = "jgit";
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java
new file mode 100644
index 0000000..be0a373
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/JGitHealthCheck.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2019 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.JGIT;
+
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+public class JGitHealthCheck extends AbstractHealthCheck {
+  private final GitRepositoryManager repositoryManager;
+  private final AllProjectsName allProjectsName;
+
+  @Inject
+  public JGitHealthCheck(GitRepositoryManager repositoryManager, AllProjectsName allProjectsName) {
+    super(JGIT);
+    this.repositoryManager = repositoryManager;
+    this.allProjectsName = allProjectsName;
+  }
+
+  @Override
+  protected Result doCheck() throws Exception {
+    try (Repository allProjects = repositoryManager.openRepository(allProjectsName)) {
+      ObjectId headObj = allProjects.resolve("refs/meta/config");
+      allProjects.open(headObj).getType();
+      return Result.PASSED;
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
index ff9b74b..10aad07 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ReviewDbHealthCheck.java
@@ -23,38 +23,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ReviewDbHealthCheck implements HealthCheck {
+public class ReviewDbHealthCheck extends AbstractHealthCheck {
   private static final Logger log = LoggerFactory.getLogger(ReviewDbHealthCheck.class);
 
   private final Provider<ReviewDb> reviewDb;
 
   @Inject
   public ReviewDbHealthCheck(Provider<ReviewDb> reviewDb) {
+    super(REVIEWDB);
     this.reviewDb = reviewDb;
   }
 
   @Override
-  public HealthCheck.Status run() {
-    HealthCheck.Result healthy;
-    long ts = System.currentTimeMillis();
-    try {
-      healthy = doCheck();
-    } catch (Exception e) {
-      log.warn("Check reviewdb has failed", e);
-      healthy = HealthCheck.Result.FAILED;
-    }
-    return new HealthCheck.Status(healthy, ts, System.currentTimeMillis() - ts);
-  }
-
-  @Override
-  public String name() {
-    return REVIEWDB;
-  }
-
-  private HealthCheck.Result doCheck() throws Exception {
+  protected Result doCheck() throws Exception {
     try (ReviewDb db = reviewDb.get()) {
       db.schemaVersion().get(new CurrentSchemaVersion.Key());
-      return HealthCheck.Result.PASSED;
+      return Result.PASSED;
     }
   }
 }
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 4471372..f5c6036 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckIT.java
@@ -16,17 +16,16 @@
 
 import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.JGIT;
 import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.REVIEWDB;
 
-import java.io.IOException;
-
-import org.junit.Test;
-
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
+import java.io.IOException;
+import org.junit.Test;
 
 @TestPlugin(name = "healthcheck", sysModule = "com.googlesource.gerrit.plugins.healthcheck.Module")
 public class HealthCheckIT extends LightweightPluginDaemonTest {
@@ -48,14 +47,28 @@
     resp.assertOK();
 
     JsonObject respPayload = gson.fromJson(resp.getReader(), JsonObject.class);
-    assertThat(respPayload.has(REVIEWDB)).isTrue();
 
-    JsonObject reviewDbStatus = respPayload.get(REVIEWDB).getAsJsonObject();
-    assertThat(reviewDbStatus.has("result")).isTrue();
-    assertThat(reviewDbStatus.get("result").getAsString()).isEqualTo("passed");
+    assertCheckResult(respPayload, REVIEWDB, "passed");
+  }
+
+  @Test
+  public void shouldReturnJGitCheck() throws Exception {
+    RestResponse resp = getHealthCheckStatus();
+    resp.assertOK();
+
+    JsonObject respPayload = gson.fromJson(resp.getReader(), JsonObject.class);
+
+    assertCheckResult(respPayload, JGIT, "passed");
   }
 
   private RestResponse getHealthCheckStatus() throws IOException {
     return adminRestSession.get("/config/server/healthcheck~status");
   }
+
+  private void assertCheckResult(JsonObject respPayload, String checkName, String result) {
+    assertThat(respPayload.has(checkName)).isTrue();
+    JsonObject reviewDbStatus = respPayload.get(checkName).getAsJsonObject();
+    assertThat(reviewDbStatus.has("result")).isTrue();
+    assertThat(reviewDbStatus.get("result").getAsString()).isEqualTo(result);
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
new file mode 100644
index 0000000..677d22a
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/JGitHealthCheckTest.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2019 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.google.common.truth.Truth.assertThat;
+import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.RepositoryCaseMismatchException;
+import com.google.gerrit.testutil.InMemoryRepositoryManager;
+import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result;
+import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.SortedSet;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JGitHealthCheckTest {
+  private AllProjectsName allProjectsName = new AllProjectsName("All-Projects");
+  private InMemoryRepositoryManager inMemoryRepositoryManager = new InMemoryRepositoryManager();
+  PersonIdent personIdent = new PersonIdent("Gerrit Rietveld", "gerrit@rietveld.nl");
+
+  @Before
+  public void setupAllProjects() throws Exception {
+    InMemoryRepositoryManager.Repo repo =
+        inMemoryRepositoryManager.createRepository(allProjectsName);
+    createCommit(repo, "refs/meta/config");
+  }
+
+  @Test
+  public void shouldBeHealthyWhenJGitIsWorking() {
+    JGitHealthCheck reviewDbCheck =
+        new JGitHealthCheck(getWorkingRepositoryManager(), allProjectsName);
+    assertThat(reviewDbCheck.run().result).isEqualTo(Result.PASSED);
+  }
+
+  @Test
+  public void shouldBeUnhealthyWhenJGitIsFailing() {
+    JGitHealthCheck jGitHealthCheck =
+        new JGitHealthCheck(getFailingGitRepositoryManager(), allProjectsName);
+    assertThat(jGitHealthCheck.run().result).isEqualTo(Result.FAILED);
+  }
+
+  private GitRepositoryManager getFailingGitRepositoryManager() {
+    return new GitRepositoryManager() {
+
+      @Override
+      public Repository openRepository(Project.NameKey name)
+          throws RepositoryNotFoundException, IOException {
+        throw new RepositoryNotFoundException("Can't fine repository " + name);
+      }
+
+      @Override
+      public Repository createRepository(Project.NameKey name)
+          throws RepositoryCaseMismatchException, RepositoryNotFoundException, IOException {
+        throw new IOException("Can't create repositories");
+      }
+
+      @Override
+      public SortedSet<Project.NameKey> list() {
+        return Collections.emptySortedSet();
+      }
+    };
+  }
+
+  private GitRepositoryManager getWorkingRepositoryManager() {
+    return inMemoryRepositoryManager;
+  }
+
+  private void createCommit(InMemoryRepositoryManager.Repo repo, String ref) throws IOException {
+    try (ObjectInserter oi = repo.newObjectInserter()) {
+      CommitBuilder cb = new CommitBuilder();
+      cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {}));
+      cb.setAuthor(personIdent);
+      cb.setCommitter(personIdent);
+      cb.setMessage("Test commit\n");
+      ObjectId id = oi.insert(cb);
+      oi.flush();
+      RefUpdate ru = repo.updateRef(ref);
+      ru.setNewObjectId(id);
+      assertThat(ru.update()).isEqualTo(NEW);
+    }
+  }
+}