Merge branch 'stable-3.12' into stable-3.13 * stable-3.12: Add --remote switch to replication start command Change-Id: Ifa94395155956f48dae777e0e41222a79554afbd
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/OnStartStop.java b/src/main/java/com/googlesource/gerrit/plugins/replication/OnStartStop.java index fc80781..c779857 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/OnStartStop.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/OnStartStop.java
@@ -22,6 +22,7 @@ import com.google.inject.Inject; import com.googlesource.gerrit.plugins.replication.PushResultProcessing.GitUpdateProcessing; import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -53,7 +54,7 @@ ReplicationState state = new ReplicationState(new GitUpdateProcessing(eventDispatcher.get())); pushAllFuture.set( pushAll - .create(null, ReplicationFilter.all(), state, false) + .create(null, Set.of(), ReplicationFilter.all(), state, false) .schedule(30, TimeUnit.SECONDS)); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java index 4f60319..8958a2a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java
@@ -20,6 +20,7 @@ import com.google.gerrit.server.project.ProjectCache; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -27,13 +28,19 @@ private final ReplicationStateListener stateLog; public interface Factory { - PushAll create(String urlMatch, ReplicationFilter filter, ReplicationState state, boolean now); + PushAll create( + String urlMatch, + Set<String> remotesToConsider, + ReplicationFilter filter, + ReplicationState state, + boolean now); } private final WorkQueue workQueue; private final ProjectCache projectCache; private final ReplicationQueue replication; private final String urlMatch; + private final Set<String> remotesToConsider; private final ReplicationFilter filter; private final ReplicationState state; private final boolean now; @@ -45,6 +52,7 @@ ReplicationQueue rq, ReplicationStateListeners stateLog, @Assisted @Nullable String urlMatch, + @Assisted Set<String> remotesToConsider, @Assisted ReplicationFilter filter, @Assisted ReplicationState state, @Assisted boolean now) { @@ -53,6 +61,7 @@ this.replication = rq; this.stateLog = stateLog; this.urlMatch = urlMatch; + this.remotesToConsider = remotesToConsider; this.filter = filter; this.state = state; this.now = now; @@ -67,7 +76,7 @@ try { for (Project.NameKey nameKey : projectCache.all()) { if (filter.matches(nameKey)) { - replication.scheduleFullSync(nameKey, urlMatch, state, now); + replication.scheduleFullSync(nameKey, urlMatch, remotesToConsider, state, now); } } } catch (Exception e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java index 6a36add..7b6079a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -132,9 +132,19 @@ public void scheduleFullSync( Project.NameKey project, String urlMatch, ReplicationState state, boolean now) { + scheduleFullSync(project, urlMatch, Set.of(), state, now); + } + + public void scheduleFullSync( + Project.NameKey project, + String urlMatch, + Set<String> remotesToConsider, + ReplicationState state, + boolean now) { fire( project, urlMatch, + remotesToConsider, Set.of(new GitReferenceUpdated.UpdatedRef(PushOne.ALL_REFS, null, null, null)), state, now); @@ -157,6 +167,16 @@ Set<UpdatedRef> updatedRefs, ReplicationState state, boolean now) { + fire(project, urlMatch, Set.of(), updatedRefs, state, now); + } + + private void fire( + Project.NameKey project, + String urlMatch, + Set<String> remotesToConsider, + Set<UpdatedRef> updatedRefs, + ReplicationState state, + boolean now) { if (!running) { stateLog.warn( "Replication plugin did not finish startup before event, event replication is postponed", @@ -166,13 +186,15 @@ } for (Destination cfg : destinations.get().getAll(FilterType.ALL)) { - pushReferences( - cfg, - project, - urlMatch, - updatedRefs.stream().map(UpdatedRef::getRefName).collect(Collectors.toSet()), - state, - now); + if (remotesToConsider.isEmpty() || remotesToConsider.contains(cfg.getRemoteConfigName())) { + pushReferences( + cfg, + project, + urlMatch, + updatedRefs.stream().map(UpdatedRef::getRefName).collect(Collectors.toSet()), + state, + now); + } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java index f63df98..507774f 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java
@@ -20,7 +20,9 @@ import com.google.inject.Inject; import com.googlesource.gerrit.plugins.replication.PushResultProcessing.CommandProcessing; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -40,6 +42,9 @@ @Option(name = "--url", metaVar = "PATTERN", usage = "pattern to match URL on") private String urlMatch; + @Option(name = "--remote", metaVar = "REMOTE", usage = "name of remote to replicate to") + private Set<String> remotesToConsider = new HashSet<>(); + @Option(name = "--wait", usage = "wait for replication to finish before exiting") private boolean wait; @@ -70,7 +75,9 @@ } Future<?> future = - pushFactory.create(urlMatch, projectFilter, state, now).schedule(0, TimeUnit.SECONDS); + pushFactory + .create(urlMatch, remotesToConsider, projectFilter, state, now) + .schedule(0, TimeUnit.SECONDS); if (wait) { if (future != null) {
diff --git a/src/main/resources/Documentation/cmd-start.md b/src/main/resources/Documentation/cmd-start.md index e1b7341..e6d9fbd 100644 --- a/src/main/resources/Documentation/cmd-start.md +++ b/src/main/resources/Documentation/cmd-start.md
@@ -12,6 +12,7 @@ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ start [--now] [--wait] + [--remote <REMOTE> ...] {--url <PATTERN> | [--url <PATTERN>] --all | [--url <PATTERN>] <PROJECT PATTERN> ...} ``` @@ -102,6 +103,12 @@ URL contains `PATTERN`. This can be useful to replicate only to a previously down node, which has been brought back online. +`--remote <REMOTE>` +: Replicate only to the destination whose remote name exactly matches +`REMOTE`. May be specified multiple times to target several remotes. +Remote names correspond to the `[remote "name"]` sections in +`replication.config`. + EXAMPLES -------- Replicate every project, to every configured remote: @@ -142,6 +149,18 @@ $ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ start --url https://example.com/tools/gerrit.git ``` +Replicate all projects to a specific named remote: + +```console + $ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ start --remote replica1 --all +``` + +Replicate all projects to two named remotes: + +```console + $ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ start --remote replica1 --remote replica2 --all +``` + SEE ALSO --------
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java index 45cf5a7..5013014 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
@@ -34,6 +34,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -281,7 +282,8 @@ plugin .getSysInjector() .getInstance(PushAll.Factory.class) - .create(null, new ReplicationFilter(Arrays.asList(project.get())), state, false) + .create( + null, Set.of(), new ReplicationFilter(Arrays.asList(project.get())), state, false) .schedule(0, TimeUnit.SECONDS); future.get(); @@ -301,7 +303,8 @@ plugin .getSysInjector() .getInstance(PushAll.Factory.class) - .create(null, new ReplicationFilter(Arrays.asList(project.get())), state, false) + .create( + null, Set.of(), new ReplicationFilter(Arrays.asList(project.get())), state, false) .schedule(0, TimeUnit.SECONDS); CountDownLatch latch = new CountDownLatch(1); @@ -492,6 +495,51 @@ } } + @Test + public void shouldReplicateToMatchingRemote() throws Exception { + Project.NameKey targetProject = createTestProject(project + "replica"); + + setReplicationDestination("foo", "replica", ALL_PROJECTS); + reloadConfig(); + + String newRef = "refs/heads/newForTest"; + ObjectId newRefTip = createNewBranchWithoutPush("refs/heads/master", newRef); + + plugin + .getSysInjector() + .getInstance(ReplicationQueue.class) + .scheduleFullSync(project, null, Set.of("foo"), new ReplicationState(NO_OP), true); + + try (Repository repo = repoManager.openRepository(targetProject)) { + waitUntil(() -> checkedGetRef(repo, newRef) != null); + + Ref targetBranchRef = getRef(repo, newRef); + assertThat(targetBranchRef).isNotNull(); + assertThat(targetBranchRef.getObjectId()).isEqualTo(newRefTip); + } + } + + @Test + public void shouldNotReplicateToNonMatchingRemote() throws Exception { + Project.NameKey targetProject = createTestProject(project + "replica"); + + setReplicationDestination("foo", "replica", ALL_PROJECTS); + reloadConfig(); + + String newRef = "refs/heads/newForTest"; + createNewBranchWithoutPush("refs/heads/master", newRef); + + plugin + .getSysInjector() + .getInstance(ReplicationQueue.class) + .scheduleFullSync(project, null, Set.of("bar"), new ReplicationState(NO_OP), true); + + try (Repository repo = repoManager.openRepository(targetProject)) { + assertThrows( + InterruptedException.class, () -> waitUntil(() -> checkedGetRef(repo, newRef) != null)); + } + } + private void waitUntil(Supplier<Boolean> waitCondition) throws InterruptedException { WaitUtil.waitUntil(waitCondition, TEST_TIMEOUT); }