Merge branch 'stable-3.12'
* stable-3.12:
Make excludedRefsPattern() to return an empty list by default
Do not push deletes for refs configured to be skipped
Add a remote config to exclude replicating desired refs
Auto-format source code using gjf
Change-Id: I57e2f34aa6b3012d2aaca69ac13899ab2bc392c2
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
index 3d7ae36..d535934 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -85,6 +85,7 @@
import java.util.concurrent.locks.ReadWriteLock;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
@@ -890,6 +891,10 @@
return config.replicateNoteDbMetaRefs();
}
+ ImmutableList<Pattern> excludedRefsPattern() {
+ return config.excludedRefsPattern();
+ }
+
private static boolean matches(URIish uri, String urlMatch) {
if (urlMatch == null || urlMatch.equals("") || urlMatch.equals("*")) {
return true;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
index 4d72ff0..41ab4c4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
@@ -20,8 +20,12 @@
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.config.ConfigUtil;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.transport.RemoteConfig;
@@ -51,6 +55,7 @@
private final int maxRetries;
private final int slowLatencyThreshold;
private final Supplier<Integer> pushBatchSize;
+ private final ImmutableList<Pattern> excludedRefsPattern;
protected DestinationConfiguration(RemoteConfig remoteConfig, Config cfg) {
this.remoteConfig = remoteConfig;
@@ -116,6 +121,7 @@
}
return 0;
});
+ excludedRefsPattern = getExcludedRefsPattern(cfg, name);
}
@Override
@@ -215,4 +221,21 @@
public int getPushBatchSize() {
return pushBatchSize.get();
}
+
+ @Override
+ public ImmutableList<Pattern> excludedRefsPattern() {
+ return excludedRefsPattern;
+ }
+
+ private ImmutableList<Pattern> getExcludedRefsPattern(Config cfg, String name) {
+ List<Pattern> patterns = new ArrayList<>();
+ for (String regex : cfg.getStringList("remote", name, "excludedRefsPattern")) {
+ try {
+ patterns.add(Pattern.compile(regex));
+ } catch (PatternSyntaxException e) {
+ repLog.atWarning().log("Invalid excludedRefsPattern '%s' is ignored", regex);
+ }
+ }
+ return ImmutableList.copyOf(patterns);
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
index c2c2ed8..030ba1c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
@@ -739,8 +739,12 @@
srcRef = git.exactRef(src);
}
- if (srcRef != null && canPushRef(src, noPerms)) {
- push(git, cmds, spec, srcRef);
+ if (srcRef != null) {
+ if (canPushRef(src, noPerms)) {
+ push(git, cmds, spec, srcRef);
+ } else {
+ repLog.atFine().log("Skipping push of ref %s", srcRef.getName());
+ }
} else if (config.isMirror()) {
delete(git, cmds, spec);
}
@@ -752,7 +756,8 @@
private boolean canPushRef(String ref, boolean noPerms) {
return !(noPerms && RefNames.REFS_CONFIG.equals(ref))
&& !ref.startsWith(RefNames.REFS_CACHE_AUTOMERGE)
- && !(!pool.replicateNoteDbMetaRefs() && RefNames.isNoteDbMetaRef(ref));
+ && !(!pool.replicateNoteDbMetaRefs() && RefNames.isNoteDbMetaRef(ref))
+ && pool.excludedRefsPattern().stream().noneMatch(p -> p.matcher(ref).matches());
}
private Map<String, Ref> listRemote(Transport tn)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RemoteConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RemoteConfiguration.java
index e39afd6..67d23b0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/RemoteConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RemoteConfiguration.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.replication;
import com.google.common.collect.ImmutableList;
+import java.util.regex.Pattern;
import org.eclipse.jgit.transport.RemoteConfig;
/** Remote configuration for a replication endpoint */
@@ -142,4 +143,13 @@
}
return ret;
}
+
+ /**
+ * List of patterns that will be used to exclude refs from being replicated
+ *
+ * @return list of successfully compiled patterns
+ */
+ default ImmutableList<Pattern> excludedRefsPattern() {
+ return ImmutableList.of();
+ }
}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index bb4be9a..67c201c 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -604,6 +604,13 @@
By default, true.
+remote.NAME.excludedRefsPattern
+: Refs that match the pattern provided using this config will not be replicated.
+ This option is useful when admins want to skip replicating certain refs, for
+ example refs created by plugins. Multiple excludedRefsPattern keys can be
+ supplied, to specify multiple patterns to match against.
+
+ Do not exclude any refs pattern by default.
Directory `replication`
--------------------
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/PushOneTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/PushOneTest.java
index 47470df..339123a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/PushOneTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/PushOneTest.java
@@ -53,6 +53,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.errors.TransportException;
@@ -75,6 +76,7 @@
import org.eclipse.jgit.util.FS;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -294,6 +296,40 @@
}
@Test
+ public void skipPushingExcludedRefs() throws InterruptedException, IOException {
+ when(destinationMock.excludedRefsPattern())
+ .thenReturn(
+ ImmutableList.of(Pattern.compile("refs/foo/.*"), Pattern.compile("refs/bar/.*")));
+ PushOne pushOne = Mockito.spy(createPushOne(null));
+
+ Ref ref1 =
+ new ObjectIdRef.Unpeeled(
+ NEW,
+ "refs/heads/master",
+ ObjectId.fromString("0000000000000000000000000000000000000002"));
+ Ref ref2 =
+ new ObjectIdRef.Unpeeled(
+ NEW, "refs/foo/test", ObjectId.fromString("0000000000000000000000000000000000000003"));
+ Ref ref3 =
+ new ObjectIdRef.Unpeeled(
+ NEW, "refs/bar/test", ObjectId.fromString("0000000000000000000000000000000000000004"));
+
+ localRefs.add(ref1);
+ localRefs.add(ref2);
+ localRefs.add(ref3);
+
+ pushOne.addRefBatch(ImmutableSet.of(ref1.getName(), ref2.getName(), ref3.getName()));
+ pushOne.run();
+
+ isCallFinished.await(10, TimeUnit.SECONDS);
+
+ ArgumentCaptor<Ref> refCaptor = ArgumentCaptor.forClass(Ref.class);
+ verify(transportMock, atLeastOnce()).push(any(), any());
+ verify(pushOne, times(1)).push(any(), any(), any(), refCaptor.capture());
+ assertThat(refCaptor.getValue().getName()).isEqualTo("refs/heads/master");
+ }
+
+ @Test
public void shouldNotAttemptDuplicateRemoteRefUpdate() throws InterruptedException, IOException {
PushOne pushOne = Mockito.spy(createPushOne(null));
@@ -459,6 +495,7 @@
private void setupDestinationMock() {
destinationMock = mock(Destination.class);
when(destinationMock.requestRunway(any())).thenReturn(RunwayStatus.allowed());
+ when(destinationMock.excludedRefsPattern()).thenReturn(ImmutableList.of());
}
private void setupPermissionBackedMock() {