Introduce globalrefdb update-ref command

Allow the Gerrit admin to update refs on the global-refdb
in the same way he can update refs on the local one, using
a command similar to 'git update-ref'.

This allows to resolve split-brains situations with a
simple command line, without having to connect directly
to the global-refdb implementation console backend.

Change-Id: Ice02d4faa08ac3c275eb241d3a8249e46bede516
diff --git a/multi-primary/globalrefdb.groovy b/multi-primary/globalrefdb.groovy
index 89d4412..33ee85d 100644
--- a/multi-primary/globalrefdb.groovy
+++ b/multi-primary/globalrefdb.groovy
@@ -34,7 +34,12 @@
   }
 
   void error(String msg) {
-    stderr.println msg
+    stderr.println "[ERROR] $msg"
+    stderr.flush()
+  }
+
+  void warning(String msg) {
+    stderr.println "[WARNING] $msg"
     stderr.flush()
   }
 }
@@ -120,5 +125,48 @@
   }
 }
 
-commands = [ ProjectRefsCheck ]
+@Export("update-ref")
+@CommandMetaData(description = "Update the global-refdb ref name/value for a project")
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+class ProjectRefsUpdate extends BaseSshCommand {
+
+  @Argument(index = 0, usage = "Project name", metaVar = "PROJECT", required = true)
+  String project
+
+  @Argument(index = 1, usage = "Ref name", metaVar = "REF", required = true)
+  String ref
+
+  @Argument(index = 2, usage = "New value", metaVar = "NEWVALUE", required = true)
+  String newValue
+
+  @Inject
+  GitRepositoryManager repoMgr
+
+  @Inject
+  DynamicItem<GlobalRefDatabase> globalRefDb
+
+  public void run() {
+    try {
+      def projectName = Project.nameKey(project)
+
+      repoMgr.openRepository(projectName).with { repo ->
+        if (!repo.refDatabase.exactRef(ref)) {
+          warning "Local project $project does not have $ref"
+        }
+        def currValue = globalRefDb.get().get(projectName, ref, String.class)
+        if (currValue.isEmpty()) {
+          error "Global-refdb for project $project does not have $ref"
+        } else {
+          println "Updating global-refdb ref for /$project/$ref ${currValue.get()} => $newValue ... "
+          def updateDone = globalRefDb.get().compareAndPut(projectName, ref, currValue.get(), newValue)
+          println "Result: /$project/$ref global-refdb update has ${updateDone ? 'SUCCEEDED':'FAILED'}"
+        }
+      }
+    } catch (RepositoryNotFoundException e) {
+      error "Project $project not found"
+    }
+  }
+}
+
+commands = [ ProjectRefsCheck, ProjectRefsUpdate ]