Add Gerrit multi-primary utilities
Change-Id: I99d58dd6a1bf14edb8456b8454f287b71287e0a4
diff --git a/README.md b/README.md
index ec4eb15..f52eb95 100644
--- a/README.md
+++ b/README.md
@@ -20,3 +20,6 @@
[Administration Scripts](/admin/)
------------------------
+
+[Gerrit multi-primary Scripts](/multi-primary/)
+------------------------
diff --git a/multi-primary/README.md b/multi-primary/README.md
new file mode 100644
index 0000000..cb7d092
--- /dev/null
+++ b/multi-primary/README.md
@@ -0,0 +1,11 @@
+Gerrit multi-primary utilities
+==============================
+
+Overview
+--------
+Set of utilities to manage the setup and administration of Gerrit multi-primary
+installation and BAU operations.
+
+Index
+-----
+* [globalrefdb](globalrefdb.md) - Checks the repository local-refdb vs. global-refdb
diff --git a/multi-primary/globalrefdb.groovy b/multi-primary/globalrefdb.groovy
new file mode 100644
index 0000000..b47cdb8
--- /dev/null
+++ b/multi-primary/globalrefdb.groovy
@@ -0,0 +1,108 @@
+// 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.
+
+import com.google.gerrit.common.data.*
+import com.google.gerrit.sshd.*
+import com.google.gerrit.extensions.annotations.*
+import com.google.gerrit.server.project.*
+import com.google.gerrit.server.account.*
+import com.google.gerrit.server.IdentifiedUser
+import com.google.inject.*
+import org.kohsuke.args4j.*
+import com.google.gerrit.server.git.*
+import com.google.gerrit.entities.*
+import org.eclipse.jgit.errors.*
+import com.gerritforge.gerrit.globalrefdb.*
+import com.google.gerrit.extensions.registration.*
+import org.eclipse.jgit.lib.*
+
+abstract class BaseSshCommand extends SshCommand {
+ void println(String msg) {
+ stdout.println msg
+ stdout.flush()
+ }
+
+ void error(String msg) {
+ stderr.println msg
+ stderr.flush()
+ }
+}
+
+@Export("check")
+@CommandMetaData(description = "Check local refs alignment against the global-refdb for a project")
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+class ProjectRefsCheck extends BaseSshCommand {
+
+ @Argument(index = 0, usage = "Project name", metaVar = "PROJECT", required = true)
+ String project
+
+ @Option(name = "--verbose", usage = "Display verbose check results for all refs")
+ boolean verbose = false
+
+ @Option(name = "--ref", usage = "Check only one ref")
+ String singleRef
+
+ @Inject
+ GitRepositoryManager repoMgr
+
+ @Inject
+ DynamicItem<GlobalRefDatabase> globalRefDb
+
+ public void run() {
+ try {
+ def projectName = Project.nameKey(project)
+
+ repoMgr.openRepository(projectName).with { repo ->
+ def upToDate = true
+
+ if (singleRef) {
+ println "Checking project $project:$singleRef ..."
+ def ref = repo.refDatabase.exactRef(singleRef)
+ if (!ref) {
+ error "Project $project does not have $singleRef"
+ return
+ }
+
+ upToDate = checkRef(projectName, repo, ref)
+ } else {
+ println "Checking project $project ..."
+ repo.refDatabase.refs.each { ref ->
+ checkRef(projectName, repo, ref)
+ }
+ }
+
+ println "Result: ${upToDate ? 'UP-TO-DATE':'OUTDATED'}"
+ }
+ } catch (RepositoryNotFoundException e) {
+ error "Project $project not found"
+ }
+ }
+
+ boolean checkRef(Project.NameKey projectName, Repository repo, Ref ref)
+ {
+ def isUpToDate = globalRefDb.get().isUpToDate(projectName, ref)
+
+ if (verbose && isUpToDate) {
+ println "[UP-TO-DATE] ${ref.name}"
+ }
+
+ if (!isUpToDate) {
+ def globalRef = globalRefDb.get().get(projectName, ref.name, String.class)
+ println "[OUTDATED] ${ref.name}:${ref.objectId.name} <> ${globalRef.orElse('MISSING')}"
+ }
+ }
+}
+
+commands = [ ProjectRefsCheck ]
+
diff --git a/multi-primary/globalrefdb.md b/multi-primary/globalrefdb.md
new file mode 100644
index 0000000..17f74f6
--- /dev/null
+++ b/multi-primary/globalrefdb.md
@@ -0,0 +1,30 @@
+Global-refdb Check utility
+==============================
+
+NAME
+----
+`globalrefdb check` utility compare the repository refs status against the global-refdb.
+
+SYNOPSIS
+--------
+> ssh -p <port> <host> globalrefdb check PROJECT [--ref <ref name>] [--verbose]
+
+DESCRIPTION
+-----------
+Verify if the local with the global refs for a project, reporting all differences
+between the two SHA1s. The operation can operate on the individual ref passed as
+a parameter.
+
+ACCESS
+------
+Any user who has been granted the 'Administrate Server' capability.
+
+SCRIPTING
+---------
+This command is intended to be used in scripts.
+
+EXAMPLES
+--------
+Check if all refs of the All-Users project are up-to-date with the global-refdb:
+
+> $ ssh -p 29418 review.example.com globalrefdb check All-Users