Merge branch 'stable-3.11' into stable-3.12 * stable-3.11: 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: I0b5d42ee5e0f6cc342ef2947d209f89edbaf2c76
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() {