Allow replication to match only some hosts

By supporting substring matches on URLs during "gerrit replicate" an
administrator can reforce replication to a single remote system after
network connectivity with that system is known to be working.

Bug: GERRIT-110
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java b/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java
index e13df07..0df522c 100644
--- a/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java
+++ b/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java
@@ -29,13 +29,23 @@
   private static final Logger log =
       LoggerFactory.getLogger(PushAllProjectsOp.class);
 
+  private final String urlMatch;
+
+  public PushAllProjectsOp() {
+    this(null);
+  }
+
+  public PushAllProjectsOp(final String urlMatch) {
+    this.urlMatch = urlMatch;
+  }
+
   public void run() {
     final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
     try {
       final ReviewDb db = Common.getSchemaFactory().open();
       try {
         for (final Project project : db.projects().all()) {
-          PushQueue.scheduleFullSync(project.getNameKey());
+          PushQueue.scheduleFullSync(project.getNameKey(), urlMatch);
         }
       } finally {
         db.close();
@@ -47,6 +57,10 @@
 
   @Override
   public String toString() {
-    return "Replicate All Projects";
+    String s = "Replicate All Projects";
+    if (urlMatch != null) {
+      s = s + " to " + urlMatch;
+    }
+    return s;
   }
 }
diff --git a/src/main/java/com/google/gerrit/git/PushQueue.java b/src/main/java/com/google/gerrit/git/PushQueue.java
index 6426da2..f52f91e 100644
--- a/src/main/java/com/google/gerrit/git/PushQueue.java
+++ b/src/main/java/com/google/gerrit/git/PushQueue.java
@@ -75,10 +75,12 @@
    * removed from the local repository.
    * 
    * @param project identity of the project to replicate.
+   * @param urlMatch substring that must appear in a URI to support replication.
    */
-  public static void scheduleFullSync(final Project.NameKey project) {
+  public static void scheduleFullSync(final Project.NameKey project,
+      final String urlMatch) {
     for (final ReplicationConfig cfg : allConfigs()) {
-      for (final URIish uri : cfg.getURIs(project)) {
+      for (final URIish uri : cfg.getURIs(project, urlMatch)) {
         scheduleImp(project, PushOp.MIRROR_ALL, cfg, uri);
       }
     }
@@ -98,7 +100,7 @@
       final String ref) {
     for (final ReplicationConfig cfg : allConfigs()) {
       if (cfg.wouldPushRef(ref)) {
-        for (final URIish uri : cfg.getURIs(project)) {
+        for (final URIish uri : cfg.getURIs(project, null)) {
           scheduleImp(project, ref, cfg, uri);
         }
       }
@@ -192,8 +194,8 @@
       delay = posInt(rc, cfg, "replicationdelay", 15);
     }
 
-    private static int posInt(final RemoteConfig rc, final RepositoryConfig cfg,
-        final String name, final int defValue) {
+    private static int posInt(final RemoteConfig rc,
+        final RepositoryConfig cfg, final String name, final int defValue) {
       return Math.max(0, cfg.getInt("remote", rc.getName(), name, defValue));
     }
 
@@ -206,13 +208,22 @@
       return false;
     }
 
-    List<URIish> getURIs(final Project.NameKey project) {
+    List<URIish> getURIs(final Project.NameKey project, final String urlMatch) {
       final List<URIish> r = new ArrayList<URIish>(remote.getURIs().size());
       for (URIish uri : remote.getURIs()) {
-        uri = uri.setPath(replace(uri.getPath(), "name", project.get()));
-        r.add(uri);
+        if (matches(uri, urlMatch)) {
+          uri = uri.setPath(replace(uri.getPath(), "name", project.get()));
+          r.add(uri);
+        }
       }
       return r;
     }
+
+    private static boolean matches(URIish uri, final String urlMatch) {
+      if (urlMatch == null || urlMatch.equals("") || urlMatch.equals("*")) {
+        return true;
+      }
+      return uri.toString().contains(urlMatch);
+    }
   }
 }
diff --git a/src/main/java/com/google/gerrit/server/ssh/AdminReplicate.java b/src/main/java/com/google/gerrit/server/ssh/AdminReplicate.java
index 8df7a17..31998f7 100644
--- a/src/main/java/com/google/gerrit/server/ssh/AdminReplicate.java
+++ b/src/main/java/com/google/gerrit/server/ssh/AdminReplicate.java
@@ -31,6 +31,9 @@
   @Option(name = "--all", usage = "push all known projects")
   private boolean all;
 
+  @Option(name = "--url", metaVar = "PATTERN", usage = "pattern to match URL on")
+  private String urlMatch;
+
   @Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project name")
   private List<String> projectNames;
 
@@ -47,13 +50,13 @@
     }
 
     if (all) {
-      WorkQueue.schedule(new PushAllProjectsOp(), 0, TimeUnit.SECONDS);
+      WorkQueue.schedule(new PushAllProjectsOp(urlMatch), 0, TimeUnit.SECONDS);
 
     } else {
       for (final String name : projectNames) {
         final Project.NameKey key = new Project.NameKey(name);
         if (Common.getProjectCache().get(key) != null) {
-          PushQueue.scheduleFullSync(key);
+          PushQueue.scheduleFullSync(key, urlMatch);
         } else {
           throw new Failure(1, "error: '" + name + "': not a Gerrit project");
         }