Merge branch 'stable-3.5' into stable-3.6
* stable-3.5:
Reformat file with gjf 1.7
Change-Id: I8fdcd95bf9f77106556f69d9c40620f70118bd1e
diff --git a/example-setup/broker/Dockerfile b/example-setup/broker/Dockerfile
index 08eaba9..b79470c 100644
--- a/example-setup/broker/Dockerfile
+++ b/example-setup/broker/Dockerfile
@@ -1,4 +1,4 @@
-FROM gerritcodereview/gerrit:3.5.5-almalinux8
+FROM gerritcodereview/gerrit:3.6.3-almalinux8
USER root
@@ -12,7 +12,7 @@
# hence rename it with a 'z-' prefix because the Gerrit plugin loader starts the
# plugins in filename alphabetical order.
COPY --chown=gerrit:gerrit events-kafka.jar /var/gerrit/plugins/z-events-kafka.jar
-COPY --chown=gerrit:gerrit libevents-broker.jar /var/gerrit/lib/libevents-broker.jar
+COPY --chown=gerrit:gerrit events-broker.jar /var/gerrit/lib/events-broker.jar
COPY --chown=gerrit:gerrit entrypoint.sh /tmp/
COPY --chown=gerrit:gerrit configs/replication.config.template /var/gerrit/etc/
diff --git a/example-setup/http/Dockerfile b/example-setup/http/Dockerfile
index e9f8239..77fed72 100644
--- a/example-setup/http/Dockerfile
+++ b/example-setup/http/Dockerfile
@@ -1,4 +1,4 @@
-FROM gerritcodereview/gerrit:3.5.5-almalinux8
+FROM gerritcodereview/gerrit:3.6.3-almalinux8
USER root
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
index 370f2fb..d02cb02 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
@@ -41,6 +41,7 @@
import com.googlesource.gerrit.plugins.replication.pull.fetch.RefUpdateState;
import java.io.IOException;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -336,23 +337,33 @@
try {
long startedAt = context.getStartTime();
long delay = NANOSECONDS.toMillis(startedAt - createdAt);
- metrics.record(config.getName(), delay, retryCount);
git = gitManager.openRepository(projectName);
- runImpl();
- long elapsed = NANOSECONDS.toMillis(context.stop());
- Optional<Long> elapsedEnd2End =
- apiRequestMetrics
- .flatMap(metrics -> metrics.stop(config.getName()))
- .map(NANOSECONDS::toMillis);
- repLog.info(
- "[{}] {} replication from {} completed in {}ms, {}ms delay, {} retries{}",
- taskIdHex,
- replicationType,
- uri,
- elapsed,
- delay,
- retryCount,
- elapsedEnd2End.map(el -> String.format(", E2E %dms", el)).orElse(""));
+ List<RefSpec> fetchRefSpecs = runImpl();
+
+ if (fetchRefSpecs.isEmpty()) {
+ repLog.info(
+ "[{}] {} replication from {} finished but no refs were replicated, {}ms delay, {} retries",
+ taskIdHex,
+ replicationType,
+ uri,
+ delay,
+ retryCount);
+ } else {
+ metrics.record(config.getName(), delay, retryCount);
+ long elapsed = NANOSECONDS.toMillis(context.stop());
+ Optional<Long> elapsedEnd2End =
+ apiRequestMetrics
+ .flatMap(metrics -> metrics.stop(config.getName()))
+ .map(NANOSECONDS::toMillis);
+ repLog.info(
+ "[{}] Replication from {} completed in {}ms, {}ms delay, {} retries{}",
+ taskIdHex,
+ uri,
+ elapsed,
+ delay,
+ retryCount,
+ elapsedEnd2End.map(el -> String.format(", E2E %dms", el)).orElse(""));
+ }
} catch (RepositoryNotFoundException e) {
stateLog.error(
"["
@@ -428,7 +439,7 @@
repLog.info("[{}] Cannot replicate from {}. It was canceled while running", taskIdHex, uri, e);
}
- private void runImpl() throws IOException {
+ private List<RefSpec> runImpl() throws IOException {
Fetch fetch = fetchFactory.create(taskIdHex, uri, git);
List<RefSpec> fetchRefSpecs = getFetchRefSpecs();
@@ -445,7 +456,7 @@
delta.remove(inexistentRef);
if (delta.isEmpty()) {
repLog.warn("[{}] Empty replication task, skipping.", taskIdHex);
- return;
+ return Collections.emptyList();
}
runImpl();
@@ -453,6 +464,7 @@
notifyRefReplicatedIOException();
throw e;
}
+ return fetchRefSpecs;
}
public List<RefSpec> getFetchRefSpecs() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/OnStartStop.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/OnStartStop.java
index d8c4a8d..6457f8a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/OnStartStop.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/OnStartStop.java
@@ -18,6 +18,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
+import com.google.gerrit.server.config.GerritIsReplica;
import com.google.gerrit.server.events.EventDispatcher;
import com.google.gerrit.server.git.WorkQueue;
import com.google.inject.Inject;
@@ -36,6 +37,7 @@
private final ReplicationState.Factory replicationStateFactory;
private final SourcesCollection sourcesCollection;
private final WorkQueue workQueue;
+ private boolean isReplica;
@Inject
protected OnStartStop(
@@ -45,7 +47,8 @@
DynamicItem<EventDispatcher> eventDispatcher,
ReplicationState.Factory replicationStateFactory,
SourcesCollection sourcesCollection,
- WorkQueue workQueue) {
+ WorkQueue workQueue,
+ @GerritIsReplica Boolean isReplica) {
this.srvInfo = srvInfo;
this.fetchAll = fetchAll;
this.config = config;
@@ -54,11 +57,13 @@
this.fetchAllFuture = Atomics.newReference();
this.sourcesCollection = sourcesCollection;
this.workQueue = workQueue;
+ this.isReplica = isReplica;
}
@Override
public void start() {
- if (srvInfo.getState() == ServerInformation.State.STARTUP
+ if (isReplica
+ && srvInfo.getState() == ServerInformation.State.STARTUP
&& config.isReplicateAllOnPluginStart()) {
ReplicationState state =
replicationStateFactory.create(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationModule.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationModule.java
index 72a6b74..005d383 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationModule.java
@@ -15,6 +15,7 @@
package com.googlesource.gerrit.plugins.replication.pull;
import static com.googlesource.gerrit.plugins.replication.StartReplicationCapability.START_REPLICATION;
+import static com.googlesource.gerrit.plugins.replication.pull.api.FetchApiCapability.CALL_FETCH_ACTION;
import com.google.common.eventbus.EventBus;
import com.google.gerrit.extensions.annotations.Exports;
@@ -44,8 +45,8 @@
import com.googlesource.gerrit.plugins.replication.ReplicationConfig;
import com.googlesource.gerrit.plugins.replication.ReplicationFileBasedConfig;
import com.googlesource.gerrit.plugins.replication.StartReplicationCapability;
+import com.googlesource.gerrit.plugins.replication.pull.api.FetchApiCapability;
import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob;
-import com.googlesource.gerrit.plugins.replication.pull.api.PullReplicationApiModule;
import com.googlesource.gerrit.plugins.replication.pull.auth.PullReplicationGroupModule;
import com.googlesource.gerrit.plugins.replication.pull.client.FetchApiClient;
import com.googlesource.gerrit.plugins.replication.pull.client.FetchRestApiClient;
@@ -82,13 +83,16 @@
.annotatedWith(Names.named(ReplicationQueueMetrics.REPLICATION_QUEUE_METRICS))
.toInstance(pluginMetricMaker);
+ bind(CapabilityDefinition.class)
+ .annotatedWith(Exports.named(CALL_FETCH_ACTION))
+ .to(FetchApiCapability.class);
+
install(new PullReplicationGroupModule());
bind(BearerTokenProvider.class).in(Scopes.SINGLETON);
bind(RevisionReader.class).in(Scopes.SINGLETON);
bind(ApplyObject.class);
install(new FactoryModuleBuilder().build(FetchJob.Factory.class));
install(new ApplyObjectCacheModule());
- install(new PullReplicationApiModule());
install(new FetchRefReplicatedEventModule());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java
index 6bafaa4..1ba47de 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java
@@ -181,12 +181,7 @@
event.getRefName(),
event.refUpdate.get().oldRev,
event.refUpdate.get().newRev);
- fire(
- event.refUpdate.get().project,
- ObjectId.fromString(event.refUpdate.get().newRev),
- event.getRefName(),
- event.eventCreatedOn,
- ZEROS_OBJECTID.equals(event.refUpdate.get().newRev));
+ fire(ReferenceUpdatedEvent.from(event));
}
}
}
@@ -215,42 +210,42 @@
return !refsFilter.match(refName);
}
- private void fire(
- String projectName,
- ObjectId objectId,
- String refName,
- long eventCreatedOn,
- boolean isDelete) {
+ private void fire(ReferenceUpdatedEvent event) {
ReplicationState state = new ReplicationState(new GitUpdateProcessing(dispatcher.get()));
- fire(Project.nameKey(projectName), objectId, refName, eventCreatedOn, isDelete, state);
+ fire(event, state);
state.markAllFetchTasksScheduled();
}
- private void fire(
- NameKey project,
- ObjectId objectId,
- String refName,
- long eventCreatedOn,
- boolean isDelete,
- ReplicationState state) {
+ private void fire(ReferenceUpdatedEvent event, ReplicationState state) {
if (!running) {
stateLog.warn(
"Replication plugin did not finish startup before event, event replication is postponed",
state);
- beforeStartupEventsQueue.add(
- ReferenceUpdatedEvent.create(project.get(), refName, objectId, eventCreatedOn, isDelete));
+ beforeStartupEventsQueue.add(event);
queueMetrics.incrementQueuedBeforStartup();
return;
}
ForkJoinPool fetchCallsPool = null;
try {
- fetchCallsPool = new ForkJoinPool(sources.get().getAll().size());
+ List<Source> allSources = sources.get().getAll();
+ int numSources = allSources.size();
+ if (numSources == 0) {
+ repLog.debug("No replication sources configured -> skipping fetch");
+ return;
+ }
+ fetchCallsPool = new ForkJoinPool(numSources);
final Consumer<Source> callFunction =
- callFunction(project, objectId, refName, eventCreatedOn, isDelete, state);
+ callFunction(
+ Project.nameKey(event.projectName()),
+ event.objectId(),
+ event.refName(),
+ event.eventCreatedOn(),
+ event.isDelete(),
+ state);
fetchCallsPool
- .submit(() -> sources.get().getAll().parallelStream().forEach(callFunction))
+ .submit(() -> allSources.parallelStream().forEach(callFunction))
.get(fetchCallsTimeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
stateLog.error(
@@ -539,12 +534,7 @@
String eventKey = String.format("%s:%s", event.projectName(), event.refName());
if (!eventsReplayed.contains(eventKey)) {
repLog.info("Firing pending task {}", event);
- fire(
- event.projectName(),
- event.objectId(),
- event.refName(),
- event.eventCreatedOn(),
- event.isDelete());
+ fire(event);
eventsReplayed.add(eventKey);
}
}
@@ -578,6 +568,15 @@
projectName, refName, objectId, eventCreatedOn, isDelete);
}
+ static ReferenceUpdatedEvent from(RefUpdatedEvent event) {
+ return ReferenceUpdatedEvent.create(
+ event.refUpdate.get().project,
+ event.getRefName(),
+ ObjectId.fromString(event.refUpdate.get().newRev),
+ event.eventCreatedOn,
+ ZEROS_OBJECTID.equals(event.refUpdate.get().newRev));
+ }
+
public abstract String projectName();
public abstract String refName();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/SourceConfigParser.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/SourceConfigParser.java
index a8799c2..d7ae063 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/SourceConfigParser.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/SourceConfigParser.java
@@ -17,6 +17,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.server.config.GerritIsReplica;
+import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.replication.ConfigParser;
import com.googlesource.gerrit.plugins.replication.RemoteConfiguration;
import java.net.URISyntaxException;
@@ -32,6 +34,13 @@
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private boolean isReplica;
+
+ @Inject
+ SourceConfigParser(@GerritIsReplica Boolean isReplica) {
+ this.isReplica = isReplica;
+ }
+
/* (non-Javadoc)
* @see com.googlesource.gerrit.plugins.replication.ConfigParser#parseRemotes(org.eclipse.jgit.lib.Config)
*/
@@ -45,7 +54,7 @@
ImmutableList.Builder<RemoteConfiguration> sourceConfigs = ImmutableList.builder();
for (RemoteConfig c : allFetchRemotes(config)) {
- if (c.getURIs().isEmpty()) {
+ if (isReplica && c.getURIs().isEmpty()) {
continue;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectAction.java
index 72f0266..d535ac9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ApplyObjectAction.java
@@ -26,6 +26,7 @@
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
+import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionInput;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.MissingParentObjectException;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.RefUpdateException;
@@ -33,6 +34,7 @@
import java.util.Objects;
import javax.servlet.http.HttpServletResponse;
+@Singleton
public class ApplyObjectAction implements RestModifyView<ProjectResource, RevisionInput> {
private final ApplyObjectCommand applyObjectCommand;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
index f897012..e49c8b6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
@@ -35,7 +35,6 @@
import com.googlesource.gerrit.plugins.replication.pull.ReplicationState;
import com.googlesource.gerrit.plugins.replication.pull.Source;
import com.googlesource.gerrit.plugins.replication.pull.SourcesCollection;
-import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject;
import com.googlesource.gerrit.plugins.replication.pull.fetch.RefUpdateState;
import java.io.IOException;
import java.util.Optional;
@@ -49,7 +48,6 @@
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final PullReplicationStateLogger fetchStateLog;
- private final ApplyObject applyObject;
private final DynamicItem<EventDispatcher> eventDispatcher;
private final ProjectCache projectCache;
private final SourcesCollection sourcesCollection;
@@ -61,13 +59,11 @@
PullReplicationStateLogger fetchStateLog,
ProjectCache projectCache,
SourcesCollection sourcesCollection,
- ApplyObject applyObject,
PermissionBackend permissionBackend,
DynamicItem<EventDispatcher> eventDispatcher,
LocalGitRepositoryManagerProvider gitManagerProvider) {
this.fetchStateLog = fetchStateLog;
this.projectCache = projectCache;
- this.applyObject = applyObject;
this.eventDispatcher = eventDispatcher;
this.sourcesCollection = sourcesCollection;
this.permissionBackend = permissionBackend;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
index 04797bd..9e69f8d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchAction.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.ioutil.HexFormat;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
+import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction.Input;
import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob.Factory;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.RemoteConfigurationMissingException;
@@ -38,6 +39,7 @@
import java.util.concurrent.TimeoutException;
import org.eclipse.jgit.errors.TransportException;
+@Singleton
public class FetchAction implements RestModifyView<ProjectResource, Input> {
private final FetchCommand command;
private final WorkQueue workQueue;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchApiCapability.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchApiCapability.java
index 73a4ac5..27afcfd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchApiCapability.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchApiCapability.java
@@ -17,7 +17,7 @@
import com.google.gerrit.extensions.config.CapabilityDefinition;
public class FetchApiCapability extends CapabilityDefinition {
- static final String CALL_FETCH_ACTION = "callFetchAction";
+ public static final String CALL_FETCH_ACTION = "callFetchAction";
@Override
public String getDescription() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
index b8249ae..5323425 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchCommand.java
@@ -97,7 +97,12 @@
if (fetchType == ReplicationType.ASYNC) {
state.markAllFetchTasksScheduled();
Future<?> future = source.get().schedule(name, refName, state, apiRequestMetrics);
- future.get(source.get().getTimeout(), TimeUnit.SECONDS);
+ int timeout = source.get().getTimeout();
+ if (timeout == 0) {
+ future.get();
+ } else {
+ future.get(timeout, TimeUnit.SECONDS);
+ }
} else {
Optional<FetchOne> maybeFetch =
source
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchPreconditions.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchPreconditions.java
index 77d0e0b..7ca8805 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchPreconditions.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/FetchPreconditions.java
@@ -26,8 +26,10 @@
import com.google.gerrit.server.permissions.RefPermission;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.UnauthorizedAuthException;
+@Singleton
public class FetchPreconditions {
private final String pluginName;
private final PermissionBackend permissionBackend;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/HttpModule.java
index 0f3e1e8..95082b8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/HttpModule.java
@@ -16,7 +16,6 @@
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.httpd.AllRequestFilter;
-import com.google.gerrit.server.config.GerritIsReplica;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import com.google.inject.name.Names;
@@ -24,12 +23,10 @@
import com.googlesource.gerrit.plugins.replication.pull.BearerTokenProvider;
public class HttpModule extends ServletModule {
- private boolean isReplica;
private final BearerTokenProvider bearerTokenProvider;
@Inject
- public HttpModule(@GerritIsReplica Boolean isReplica, BearerTokenProvider bearerTokenProvider) {
- this.isReplica = isReplica;
+ public HttpModule(BearerTokenProvider bearerTokenProvider) {
this.bearerTokenProvider = bearerTokenProvider;
}
@@ -49,12 +46,8 @@
.in(Scopes.SINGLETON);
});
- if (isReplica) {
- DynamicSet.bind(binder(), AllRequestFilter.class)
- .to(PullReplicationFilter.class)
- .in(Scopes.SINGLETON);
- } else {
- serveRegex("/init-project/.*$").with(ProjectInitializationAction.class);
- }
+ DynamicSet.bind(binder(), AllRequestFilter.class)
+ .to(PullReplicationFilter.class)
+ .in(Scopes.SINGLETON);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java
index 2e1c5d4..adb333c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java
@@ -27,11 +27,13 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.replication.LocalFS;
import com.googlesource.gerrit.plugins.replication.pull.GerritConfigOps;
import java.util.Optional;
import org.eclipse.jgit.transport.URIish;
+@Singleton
class ProjectDeletionAction
implements RestModifyView<ProjectResource, ProjectDeletionAction.DeleteInput> {
private static final PluginPermission DELETE_PROJECT =
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationApiModule.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationApiModule.java
deleted file mode 100644
index d1d28a6..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationApiModule.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.googlesource.gerrit.plugins.replication.pull.api;
-
-import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
-import static com.googlesource.gerrit.plugins.replication.pull.api.FetchApiCapability.CALL_FETCH_ACTION;
-
-import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.config.CapabilityDefinition;
-import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.inject.Scopes;
-
-public class PullReplicationApiModule extends RestApiModule {
- @Override
- protected void configure() {
- bind(FetchAction.class).in(Scopes.SINGLETON);
- bind(ApplyObjectAction.class).in(Scopes.SINGLETON);
- bind(ProjectDeletionAction.class).in(Scopes.SINGLETON);
- bind(UpdateHeadAction.class).in(Scopes.SINGLETON);
- post(PROJECT_KIND, "fetch").to(FetchAction.class);
- post(PROJECT_KIND, "apply-object").to(ApplyObjectAction.class);
- post(PROJECT_KIND, "apply-objects").to(ApplyObjectsAction.class);
- delete(PROJECT_KIND, "delete-project").to(ProjectDeletionAction.class);
- put(PROJECT_KIND, "HEAD").to(UpdateHeadAction.class);
-
- bind(FetchPreconditions.class).in(Scopes.SINGLETON);
- bind(CapabilityDefinition.class)
- .annotatedWith(Exports.named(CALL_FETCH_ACTION))
- .to(FetchApiCapability.class);
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
index e54d408..5a4393c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
@@ -16,7 +16,6 @@
import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_UNPROCESSABLE_ENTITY;
import static com.googlesource.gerrit.plugins.replication.pull.api.HttpServletOps.checkAcceptHeader;
-import static com.googlesource.gerrit.plugins.replication.pull.api.HttpServletOps.setResponse;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
@@ -26,6 +25,7 @@
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.api.projects.HeadInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -35,24 +35,26 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.httpd.AllRequestFilter;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.json.OutputFormat;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.restapi.project.ProjectsCollection;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.MalformedJsonException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction.Input;
import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionInput;
import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionsInput;
-import com.googlesource.gerrit.plugins.replication.pull.api.exception.InitProjectException;
import com.googlesource.gerrit.plugins.replication.pull.api.exception.UnauthorizedAuthException;
import java.io.BufferedReader;
import java.io.EOFException;
@@ -83,9 +85,10 @@
private ProjectInitializationAction projectInitializationAction;
private UpdateHeadAction updateHEADAction;
private ProjectDeletionAction projectDeletionAction;
- private ProjectsCollection projectsCollection;
+ private ProjectCache projectCache;
private Gson gson;
private String pluginName;
+ private final Provider<CurrentUser> currentUserProvider;
@Inject
public PullReplicationFilter(
@@ -95,17 +98,19 @@
ProjectInitializationAction projectInitializationAction,
UpdateHeadAction updateHEADAction,
ProjectDeletionAction projectDeletionAction,
- ProjectsCollection projectsCollection,
- @PluginName String pluginName) {
+ ProjectCache projectCache,
+ @PluginName String pluginName,
+ Provider<CurrentUser> currentUserProvider) {
this.fetchAction = fetchAction;
this.applyObjectAction = applyObjectAction;
this.applyObjectsAction = applyObjectsAction;
this.projectInitializationAction = projectInitializationAction;
this.updateHEADAction = updateHEADAction;
this.projectDeletionAction = projectDeletionAction;
- this.projectsCollection = projectsCollection;
+ this.projectCache = projectCache;
this.pluginName = pluginName;
this.gson = OutputFormat.JSON.newGsonBuilder().create();
+ this.currentUserProvider = currentUserProvider;
}
@Override
@@ -120,19 +125,25 @@
HttpServletRequest httpRequest = (HttpServletRequest) request;
try {
if (isFetchAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
writeResponse(httpResponse, doFetch(httpRequest));
} else if (isApplyObjectAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
writeResponse(httpResponse, doApplyObject(httpRequest));
} else if (isApplyObjectsAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
writeResponse(httpResponse, doApplyObjects(httpRequest));
} else if (isInitProjectAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
if (!checkAcceptHeader(httpRequest, httpResponse)) {
return;
}
doInitProject(httpRequest, httpResponse);
} else if (isUpdateHEADAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
writeResponse(httpResponse, doUpdateHEAD(httpRequest));
} else if (isDeleteProjectAction(httpRequest)) {
+ failIfcurrentUserIsAnonymous();
writeResponse(httpResponse, doDeleteProject(httpRequest));
} else {
chain.doFilter(request, response);
@@ -156,7 +167,7 @@
} catch (ResourceConflictException e) {
RestApiServlet.replyError(
httpRequest, httpResponse, SC_CONFLICT, e.getMessage(), e.caching(), e);
- } catch (InitProjectException | ResourceNotFoundException e) {
+ } catch (ResourceNotFoundException e) {
RestApiServlet.replyError(
httpRequest, httpResponse, SC_INTERNAL_SERVER_ERROR, e.getMessage(), e.caching(), e);
} catch (NoSuchElementException e) {
@@ -173,17 +184,16 @@
}
}
- private void doInitProject(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
- throws RestApiException, IOException, PermissionBackendException {
-
- IdString id = getInitProjectName(httpRequest).get();
- String projectName = id.get();
- if (projectInitializationAction.initProject(projectName)) {
- setResponse(
- httpResponse, HttpServletResponse.SC_CREATED, "Project " + projectName + " initialized");
- return;
+ private void failIfcurrentUserIsAnonymous() throws UnauthorizedAuthException {
+ CurrentUser currentUser = currentUserProvider.get();
+ if (currentUser instanceof AnonymousUser) {
+ throw new UnauthorizedAuthException();
}
- throw new InitProjectException(projectName);
+ }
+
+ private void doInitProject(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+ throws IOException, ServletException {
+ projectInitializationAction.service(httpRequest, httpResponse);
}
@SuppressWarnings("unchecked")
@@ -191,9 +201,8 @@
throws RestApiException, IOException, PermissionBackendException {
RevisionInput input = readJson(httpRequest, TypeLiteral.get(RevisionInput.class));
IdString id = getProjectName(httpRequest).get();
- ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
- return (Response<String>) applyObjectAction.apply(projectResource, input);
+ return (Response<String>) applyObjectAction.apply(parseProjectResource(id), input);
}
@SuppressWarnings("unchecked")
@@ -201,26 +210,24 @@
throws RestApiException, IOException, PermissionBackendException {
RevisionsInput input = readJson(httpRequest, TypeLiteral.get(RevisionsInput.class));
IdString id = getProjectName(httpRequest).get();
- ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
- return (Response<String>) applyObjectsAction.apply(projectResource, input);
+ return (Response<String>) applyObjectsAction.apply(parseProjectResource(id), input);
}
@SuppressWarnings("unchecked")
private Response<String> doUpdateHEAD(HttpServletRequest httpRequest) throws Exception {
HeadInput input = readJson(httpRequest, TypeLiteral.get(HeadInput.class));
IdString id = getProjectName(httpRequest).get();
- ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
- return (Response<String>) updateHEADAction.apply(projectResource, input);
+ return (Response<String>) updateHEADAction.apply(parseProjectResource(id), input);
}
@SuppressWarnings("unchecked")
private Response<String> doDeleteProject(HttpServletRequest httpRequest) throws Exception {
IdString id = getProjectName(httpRequest).get();
- ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<String>)
- projectDeletionAction.apply(projectResource, new ProjectDeletionAction.DeleteInput());
+ projectDeletionAction.apply(
+ parseProjectResource(id), new ProjectDeletionAction.DeleteInput());
}
@SuppressWarnings("unchecked")
@@ -228,9 +235,16 @@
throws IOException, RestApiException, PermissionBackendException {
Input input = readJson(httpRequest, TypeLiteral.get(Input.class));
IdString id = getProjectName(httpRequest).get();
- ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
- return (Response<Map<String, Object>>) fetchAction.apply(projectResource, input);
+ return (Response<Map<String, Object>>) fetchAction.apply(parseProjectResource(id), input);
+ }
+
+ private ProjectResource parseProjectResource(IdString id) throws ResourceNotFoundException {
+ Optional<ProjectState> project = projectCache.get(Project.nameKey(id.get()));
+ if (project.isEmpty()) {
+ throw new ResourceNotFoundException(id);
+ }
+ return new ProjectResource(project.get(), currentUserProvider.get());
}
private <T> void writeResponse(HttpServletResponse httpResponse, Response<T> response)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java
deleted file mode 100644
index 85a7729..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.googlesource.gerrit.plugins.replication.pull.api.exception;
-
-import com.google.gerrit.extensions.restapi.RestApiException;
-
-public class InitProjectException extends RestApiException {
- private static final long serialVersionUID = 1L;
-
- public InitProjectException(String projectName) {
- super("Cannot create project " + projectName);
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetch.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetch.java
index 24898e6..d3e45da 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetch.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetch.java
@@ -28,6 +28,8 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
@@ -101,7 +103,9 @@
if (credentialsProvider.supports(user, pass)
&& credentialsProvider.get(uri, user, pass)
&& uri.getScheme() != null
- && !"ssh".equalsIgnoreCase(uri.getScheme())) {
+ && !"ssh".equalsIgnoreCase(uri.getScheme())
+ && StringUtils.isNotEmpty(user.getValue())
+ && ArrayUtils.isNotEmpty(pass.getValue())) {
return uri.setUser(user.getValue()).setPass(String.valueOf(pass.getValue()));
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetchValidator.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetchValidator.java
index 9a10898..434c0f0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetchValidator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/CGitFetchValidator.java
@@ -35,7 +35,7 @@
@Override
public Void visit(AssistedInjectBinding<? extends FetchFactory> binding) {
- TypeLiteral<CGitFetch> nativeGitFetchType = new TypeLiteral<CGitFetch>() {};
+ TypeLiteral<CGitFetch> nativeGitFetchType = new TypeLiteral<>() {};
for (AssistedMethod method : binding.getAssistedMethods()) {
if (method.getImplementationType().equals(nativeGitFetchType)) {
String[] command = new String[] {"git", "--version"};
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/PermanentTransportException.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/PermanentTransportException.java
index acb68cf..0fa89b5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/PermanentTransportException.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/fetch/PermanentTransportException.java
@@ -14,7 +14,7 @@
package com.googlesource.gerrit.plugins.replication.pull.fetch;
-import com.jcraft.jsch.JSchException;
+import org.apache.sshd.common.SshException;
import org.eclipse.jgit.errors.TransportException;
public class PermanentTransportException extends TransportException {
@@ -26,7 +26,8 @@
public static TransportException wrapIfPermanentTransportException(TransportException e) {
Throwable cause = e.getCause();
- if (cause instanceof JSchException && cause.getMessage().startsWith("UnknownHostKey:")) {
+ if (cause instanceof SshException
+ && cause.getMessage().startsWith("Failed (UnsupportedCredentialItem) to execute:")) {
return new PermanentTransportException("Terminal fetch failure", e);
}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 73c5c05..22b52e7 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -16,10 +16,6 @@
group that is granted the 'Pull Replication' capability (provided
by this plugin) or the 'Administrate Server' capability.
-When replicating hidden projects, the pull replication user needs to have
-the 'Administrate Server' capability or being added as the owner of each
-individual project that is supposed to be replicated.
-
Change Indexing
--------
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/FetchOneTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/FetchOneTest.java
index ee286bb..dd9f98e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/FetchOneTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/FetchOneTest.java
@@ -18,9 +18,9 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
+import com.google.gerrit.acceptance.TestMetricMaker;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.PerThreadRequestScope;
import com.google.gerrit.server.util.IdGenerator;
@@ -54,6 +54,7 @@
private final Project.NameKey PROJECT_NAME = Project.NameKey.parse(TEST_PROJECT_NAME);
private final String TEST_REF = "refs/heads/refForReplicationTask";
private final String URI_PATTERN = "http://test.com/" + TEST_PROJECT_NAME + ".git";
+ private final TestMetricMaker testMetricMaker = new TestMetricMaker();
@Mock private GitRepositoryManager grm;
@Mock private Repository repository;
@@ -72,8 +73,9 @@
@Before
public void setup() throws Exception {
+ testMetricMaker.reset();
FetchReplicationMetrics fetchReplicationMetrics =
- new FetchReplicationMetrics("pull-replication", new DisabledMetricMaker());
+ new FetchReplicationMetrics("pull-replication", testMetricMaker);
urIish = new URIish(URI_PATTERN);
grm = mock(GitRepositoryManager.class);
@@ -704,6 +706,51 @@
TEST_PROJECT_NAME, TEST_REF, urIish, ReplicationState.RefFetchResult.FAILED, null);
}
+ @Test
+ public void shouldNotRecordReplicationLatencyMetricIfAllRefsAreExcluded() throws Exception {
+ setupMocks(true);
+ String filteredRef = "refs/heads/filteredRef";
+ Set<String> refSpecs = Set.of(TEST_REF, filteredRef);
+ createTestStates(TEST_REF, 1);
+ createTestStates(filteredRef, 1);
+ setupFetchFactoryMock(
+ List.of(new FetchFactoryEntry.Builder().refSpecNameWithDefaults(TEST_REF).build()),
+ Optional.of(List.of(TEST_REF)));
+ objectUnderTest.addRefs(refSpecs);
+ objectUnderTest.setReplicationFetchFilter(replicationFilter);
+ ReplicationFetchFilter mockFilter = mock(ReplicationFetchFilter.class);
+ when(replicationFilter.get()).thenReturn(mockFilter);
+ when(mockFilter.filter(TEST_PROJECT_NAME, refSpecs)).thenReturn(Collections.emptySet());
+
+ objectUnderTest.run();
+
+ verify(pullReplicationApiRequestMetrics, never()).stop(any());
+ assertThat(testMetricMaker.getTimer("replication_latency")).isEqualTo(0);
+ }
+
+ @Test
+ public void shouldRecordReplicationLatencyMetricWhenAtLeastOneRefWasReplicated()
+ throws Exception {
+ setupMocks(true);
+ String filteredRef = "refs/heads/filteredRef";
+ Set<String> refSpecs = Set.of(TEST_REF, filteredRef);
+ createTestStates(TEST_REF, 1);
+ createTestStates(filteredRef, 1);
+ setupFetchFactoryMock(
+ List.of(new FetchFactoryEntry.Builder().refSpecNameWithDefaults(TEST_REF).build()),
+ Optional.of(List.of(TEST_REF)));
+ objectUnderTest.addRefs(refSpecs);
+ objectUnderTest.setReplicationFetchFilter(replicationFilter);
+ ReplicationFetchFilter mockFilter = mock(ReplicationFetchFilter.class);
+ when(replicationFilter.get()).thenReturn(mockFilter);
+ when(mockFilter.filter(TEST_PROJECT_NAME, refSpecs)).thenReturn(Set.of(TEST_REF));
+
+ objectUnderTest.run();
+
+ verify(pullReplicationApiRequestMetrics).stop(any());
+ assertThat(testMetricMaker.getTimer("replication_latency")).isGreaterThan(0);
+ }
+
private void setupRequestScopeMock() {
when(scoper.scope(any()))
.thenAnswer(
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PermanentFailureExceptionTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PermanentFailureExceptionTest.java
index 09a465c..fcb0702 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PermanentFailureExceptionTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PermanentFailureExceptionTest.java
@@ -18,7 +18,7 @@
import com.googlesource.gerrit.plugins.replication.pull.fetch.InexistentRefTransportException;
import com.googlesource.gerrit.plugins.replication.pull.fetch.PermanentTransportException;
-import com.jcraft.jsch.JSchException;
+import org.apache.sshd.common.SshException;
import org.eclipse.jgit.errors.TransportException;
import org.junit.Test;
@@ -29,7 +29,9 @@
assertThat(
PermanentTransportException.wrapIfPermanentTransportException(
new TransportException(
- "SSH error", new JSchException("UnknownHostKey: some.place"))))
+ "SSH error",
+ new SshException(
+ "Failed (UnsupportedCredentialItem) to execute: some.commands"))))
.isInstanceOf(PermanentTransportException.class);
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationFanoutConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationFanoutConfigIT.java
index 5cf01b1..0721dd2 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationFanoutConfigIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationFanoutConfigIT.java
@@ -49,7 +49,8 @@
@UseLocalDisk
@TestPlugin(
name = "pull-replication",
- sysModule = "com.googlesource.gerrit.plugins.replication.pull.PullReplicationModule")
+ sysModule = "com.googlesource.gerrit.plugins.replication.pull.PullReplicationModule",
+ httpModule = "com.googlesource.gerrit.plugins.replication.pull.api.HttpModule")
public class PullReplicationFanoutConfigIT extends LightweightPluginDaemonTest {
private static final Optional<String> ALL_PROJECTS = Optional.empty();
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationIT.java
index 18f7a0c..29bf7e4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationIT.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Android Open Source Project
+// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,9 +14,45 @@
package com.googlesource.gerrit.plugins.replication.pull;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.google.gerrit.acceptance.GitUtil.pushOne;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+
+import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.SkipProjectClone;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.entities.Permission;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.events.HeadUpdatedListener;
+import com.google.gerrit.extensions.events.ProjectDeletedListener;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.googlesource.gerrit.plugins.replication.AutoReloadConfigDecorator;
+import com.googlesource.gerrit.plugins.replication.pull.client.FetchApiClient;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Ignore;
+import org.junit.Test;
@SkipProjectClone
@UseLocalDisk
@@ -25,4 +61,346 @@
sysModule =
"com.googlesource.gerrit.plugins.replication.pull.PullReplicationITAbstract$PullReplicationTestModule",
httpModule = "com.googlesource.gerrit.plugins.replication.pull.api.HttpModule")
-public class PullReplicationIT extends PullReplicationITAbstract {}
+public class PullReplicationIT extends PullReplicationSetupBase {
+
+ @Override
+ protected void setReplicationSource(
+ String remoteName, List<String> replicaSuffixes, Optional<String> project)
+ throws IOException {
+ List<String> fetchUrls =
+ buildReplicaURLs(replicaSuffixes, s -> gitPath.resolve("${name}" + s + ".git").toString());
+ config.setStringList("remote", remoteName, "url", fetchUrls);
+ config.setString("remote", remoteName, "apiUrl", adminRestSession.url());
+ config.setString("remote", remoteName, "fetch", "+refs/*:refs/*");
+ config.setInt("remote", remoteName, "timeout", 600);
+ config.setInt("remote", remoteName, "replicationDelay", TEST_REPLICATION_DELAY);
+ project.ifPresent(prj -> config.setString("remote", remoteName, "projects", prj));
+ config.setBoolean("gerrit", null, "autoReload", true);
+ config.save();
+ }
+
+ @Override
+ public void setUpTestPlugin() throws Exception {
+ setUpTestPlugin(false);
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateNewChangeRef() throws Exception {
+ testRepo = cloneProject(createTestProject(project + TEST_REPLICATION_SUFFIX));
+
+ Result pushResult = createChange();
+ RevCommit sourceCommit = pushResult.getCommit();
+ String sourceRef = pushResult.getPatchSet().refName();
+
+ ReplicationQueue pullReplicationQueue = getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ sourceRef,
+ ObjectId.zeroId().getName(),
+ sourceCommit.getId().getName(),
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, sourceRef) != null);
+
+ Ref targetBranchRef = getRef(repo, sourceRef);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId()).isEqualTo(sourceCommit.getId());
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateNewBranch() throws Exception {
+ String testProjectName = project + TEST_REPLICATION_SUFFIX;
+ createTestProject(testProjectName);
+
+ String newBranch = "refs/heads/mybranch";
+ String master = "refs/heads/master";
+ BranchInput input = new BranchInput();
+ input.revision = master;
+ gApi.projects().name(testProjectName).branch(newBranch).create(input);
+ String branchRevision = gApi.projects().name(testProjectName).branch(newBranch).get().revision;
+
+ ReplicationQueue pullReplicationQueue =
+ plugin.getSysInjector().getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ newBranch,
+ ObjectId.zeroId().getName(),
+ branchRevision,
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project);
+ Repository sourceRepo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, newBranch) != null);
+
+ Ref targetBranchRef = getRef(repo, newBranch);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId().getName()).isEqualTo(branchRevision);
+ }
+ }
+
+ @Test
+ @UseLocalDisk
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateForceUpdatedBranch() throws Exception {
+ boolean forcedPush = true;
+ String testProjectName = project + TEST_REPLICATION_SUFFIX;
+ NameKey testProjectNameKey = createTestProject(testProjectName);
+
+ String newBranch = "refs/heads/mybranch";
+ String master = "refs/heads/master";
+ BranchInput input = new BranchInput();
+ input.revision = master;
+ gApi.projects().name(testProjectName).branch(newBranch).create(input);
+
+ projectOperations
+ .project(testProjectNameKey)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(newBranch).group(REGISTERED_USERS).force(true))
+ .update();
+
+ String branchRevision = gApi.projects().name(testProjectName).branch(newBranch).get().revision;
+
+ ReplicationQueue pullReplicationQueue =
+ plugin.getSysInjector().getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ newBranch,
+ ObjectId.zeroId().getName(),
+ branchRevision,
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, newBranch) != null);
+
+ Ref targetBranchRef = getRef(repo, newBranch);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId().getName()).isEqualTo(branchRevision);
+ }
+
+ TestRepository<InMemoryRepository> testProject = cloneProject(testProjectNameKey);
+ fetch(testProject, RefNames.REFS_HEADS + "*:" + RefNames.REFS_HEADS + "*");
+ RevCommit amendedCommit = testProject.amendRef(newBranch).message("Amended commit").create();
+ PushResult pushResult =
+ pushOne(testProject, newBranch, newBranch, false, forcedPush, Collections.emptyList());
+ Collection<RemoteRefUpdate> pushedRefs = pushResult.getRemoteUpdates();
+ assertThat(pushedRefs).hasSize(1);
+ assertThat(pushedRefs.iterator().next().getStatus()).isEqualTo(Status.OK);
+
+ FakeGitReferenceUpdatedEvent forcedPushEvent =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ newBranch,
+ branchRevision,
+ amendedCommit.getId().getName(),
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(forcedPushEvent);
+
+ try (Repository repo = repoManager.openRepository(project);
+ Repository sourceRepo = repoManager.openRepository(project)) {
+ waitUntil(
+ () ->
+ checkedGetRef(repo, newBranch) != null
+ && checkedGetRef(repo, newBranch)
+ .getObjectId()
+ .getName()
+ .equals(amendedCommit.getId().getName()));
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateNewChangeRefCGitClient() throws Exception {
+ AutoReloadConfigDecorator autoReloadConfigDecorator =
+ getInstance(AutoReloadConfigDecorator.class);
+
+ config.setBoolean("replication", null, "useCGitClient", true);
+ config.save();
+
+ autoReloadConfigDecorator.reload();
+
+ testRepo = cloneProject(createTestProject(project + TEST_REPLICATION_SUFFIX));
+
+ Result pushResult = createChange();
+ RevCommit sourceCommit = pushResult.getCommit();
+ String sourceRef = pushResult.getPatchSet().refName();
+
+ ReplicationQueue pullReplicationQueue = getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ sourceRef,
+ ObjectId.zeroId().getName(),
+ sourceCommit.getId().getName(),
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, sourceRef) != null);
+
+ Ref targetBranchRef = getRef(repo, sourceRef);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId()).isEqualTo(sourceCommit.getId());
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateNewBranchCGitClient() throws Exception {
+ AutoReloadConfigDecorator autoReloadConfigDecorator =
+ getInstance(AutoReloadConfigDecorator.class);
+
+ config.setBoolean("replication", null, "useCGitClient", true);
+ config.save();
+
+ autoReloadConfigDecorator.reload();
+
+ String testProjectName = project + TEST_REPLICATION_SUFFIX;
+ createTestProject(testProjectName);
+
+ String newBranch = "refs/heads/mybranch";
+ String master = "refs/heads/master";
+ BranchInput input = new BranchInput();
+ input.revision = master;
+ gApi.projects().name(testProjectName).branch(newBranch).create(input);
+ String branchRevision = gApi.projects().name(testProjectName).branch(newBranch).get().revision;
+
+ ReplicationQueue pullReplicationQueue =
+ plugin.getSysInjector().getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ newBranch,
+ ObjectId.zeroId().getName(),
+ branchRevision,
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project);
+ Repository sourceRepo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, newBranch) != null);
+
+ Ref targetBranchRef = getRef(repo, newBranch);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId().getName()).isEqualTo(branchRevision);
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldCreateNewProject() throws Exception {
+ NameKey projectToCreate = Project.nameKey(project.get() + "_created");
+
+ setReplicationSource(TEST_REPLICATION_REMOTE, "", Optional.of(projectToCreate.get()));
+ config.save();
+ AutoReloadConfigDecorator autoReloadConfigDecorator =
+ getInstance(AutoReloadConfigDecorator.class);
+ autoReloadConfigDecorator.reload();
+ Source source =
+ getInstance(SourcesCollection.class).getByRemoteName(TEST_REPLICATION_REMOTE).get();
+
+ FetchApiClient client = getInstance(FetchApiClient.Factory.class).create(source);
+ client.initProject(projectToCreate, new URIish(source.getApis().get(0)));
+
+ waitUntil(() -> repoManager.list().contains(projectToCreate));
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateProjectDeletion() throws Exception {
+ String projectToDelete = project.get();
+ setReplicationSource(TEST_REPLICATION_REMOTE, "", Optional.of(projectToDelete));
+ config.save();
+ AutoReloadConfigDecorator autoReloadConfigDecorator =
+ getInstance(AutoReloadConfigDecorator.class);
+ autoReloadConfigDecorator.reload();
+
+ ProjectDeletedListener.Event event =
+ new ProjectDeletedListener.Event() {
+ @Override
+ public String getProjectName() {
+ return projectToDelete;
+ }
+
+ @Override
+ public NotifyHandling getNotify() {
+ return NotifyHandling.NONE;
+ }
+ };
+ for (ProjectDeletedListener l : deletedListeners) {
+ l.onProjectDeleted(event);
+ }
+
+ waitUntil(() -> !repoManager.list().contains(project));
+ }
+
+ @Test
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ public void shouldReplicateHeadUpdate() throws Exception {
+ String testProjectName = project.get();
+ setReplicationSource(TEST_REPLICATION_REMOTE, "", Optional.of(testProjectName));
+ config.save();
+ AutoReloadConfigDecorator autoReloadConfigDecorator =
+ getInstance(AutoReloadConfigDecorator.class);
+ autoReloadConfigDecorator.reload();
+
+ String newBranch = "refs/heads/mybranch";
+ String master = "refs/heads/master";
+ BranchInput input = new BranchInput();
+ input.revision = master;
+ gApi.projects().name(testProjectName).branch(newBranch).create(input);
+
+ ReplicationQueue pullReplicationQueue =
+ plugin.getSysInjector().getInstance(ReplicationQueue.class);
+
+ HeadUpdatedListener.Event event = new FakeHeadUpdateEvent(master, newBranch, testProjectName);
+ pullReplicationQueue.onHeadUpdated(event);
+
+ waitUntil(
+ () -> {
+ try {
+ return gApi.projects().name(testProjectName).head().equals(newBranch);
+ } catch (RestApiException e) {
+ return false;
+ }
+ });
+ }
+
+ @Ignore
+ @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+ @GerritConfig(name = "container.replica", value = "true")
+ public void shouldReplicateNewChangeRefToReplica() throws Exception {
+ testRepo = cloneProject(createTestProject(project + TEST_REPLICATION_SUFFIX));
+
+ Result pushResult = createChange();
+ RevCommit sourceCommit = pushResult.getCommit();
+ String sourceRef = pushResult.getPatchSet().refName();
+
+ ReplicationQueue pullReplicationQueue = getInstance(ReplicationQueue.class);
+ FakeGitReferenceUpdatedEvent event =
+ new FakeGitReferenceUpdatedEvent(
+ project,
+ sourceRef,
+ ObjectId.zeroId().getName(),
+ sourceCommit.getId().getName(),
+ TEST_REPLICATION_REMOTE);
+ pullReplicationQueue.onEvent(event);
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ waitUntil(() -> checkedGetRef(repo, sourceRef) != null);
+
+ Ref targetBranchRef = getRef(repo, sourceRef);
+ assertThat(targetBranchRef).isNotNull();
+ assertThat(targetBranchRef.getObjectId()).isEqualTo(sourceCommit.getId());
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java
index 1cffec7..603528d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java
@@ -369,7 +369,7 @@
}
@Test
- public void shouldCallDeleteWhenReplicateProjectDeletionsTrue() throws Exception {
+ public void shouldCallDeleteWhenReplicateProjectDeletionsTrue() {
when(source.wouldDeleteProject(any())).thenReturn(true);
String projectName = "testProject";
@@ -385,7 +385,7 @@
}
@Test
- public void shouldNotCallDeleteWhenProjectNotToDelete() throws Exception {
+ public void shouldNotCallDeleteWhenProjectNotToDelete() {
when(source.wouldDeleteProject(any())).thenReturn(false);
FakeProjectDeletedEvent event = new FakeProjectDeletedEvent("testProject");
@@ -397,7 +397,7 @@
}
@Test
- public void shouldScheduleUpdateHeadWhenWouldFetchProject() throws Exception {
+ public void shouldScheduleUpdateHeadWhenWouldFetchProject() {
when(source.wouldFetchProject(any())).thenReturn(true);
String projectName = "aProject";
@@ -413,7 +413,7 @@
}
@Test
- public void shouldNotScheduleUpdateHeadWhenNotWouldFetchProject() throws Exception {
+ public void shouldNotScheduleUpdateHeadWhenNotWouldFetchProject() {
when(source.wouldFetchProject(any())).thenReturn(false);
String projectName = "aProject";
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ActionITBase.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ActionITBase.java
index a46846e..4a3fa55 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ActionITBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ActionITBase.java
@@ -162,7 +162,7 @@
}
public ResponseHandler<Object> assertHttpResponseCode(int responseCode) {
- return new ResponseHandler<Object>() {
+ return new ResponseHandler<>() {
@Override
public Object handleResponse(HttpResponse response)
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
index f1f9e44..fc1b02c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
@@ -102,7 +102,6 @@
fetchStateLog,
projectCache,
sourceCollection,
- applyObject,
permissionBackend,
eventDispatcherDataItem,
new LocalGitRepositoryManagerProvider(gitManager));
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionActionIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionActionIT.java
index 10415e4..cf7515e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionActionIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionActionIT.java
@@ -94,10 +94,11 @@
@Test
@GerritConfig(name = "gerrit.instanceId", value = "testInstanceId")
@GerritConfig(name = "container.replica", value = "true")
- public void shouldReturnForbiddenForUserWithoutPermissionsOnReplica() throws Exception {
+ public void shouldReturnUnauthorizedForUserWithoutPermissionsOnReplica() throws Exception {
httpClientFactory
.create(source)
- .execute(createDeleteRequest(), assertHttpResponseCode(HttpServletResponse.SC_FORBIDDEN));
+ .execute(
+ createDeleteRequest(), assertHttpResponseCode(HttpServletResponse.SC_UNAUTHORIZED));
}
@Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java
index c543969..0f13881 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java
@@ -184,12 +184,13 @@
@Test
@GerritConfig(name = "gerrit.instanceId", value = "testInstanceId")
@GerritConfig(name = "container.replica", value = "true")
- public void shouldReturnForbiddenForUserWithoutPermissionsWhenNodeIsAReplica() throws Exception {
+ public void shouldReturnUnauthorizedForUserWithoutPermissionsWhenNodeIsAReplica()
+ throws Exception {
httpClientFactory
.create(source)
.execute(
createPutRequestWithHeaders(),
- assertHttpResponseCode(HttpServletResponse.SC_FORBIDDEN));
+ assertHttpResponseCode(HttpServletResponse.SC_UNAUTHORIZED));
}
@Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java
index 9e5ca8d..9492d49 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java
@@ -4,18 +4,25 @@
import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_UNPROCESSABLE_ENTITY;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.atLeastOnce;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import com.google.common.net.MediaType;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.*;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.restapi.project.ProjectsCollection;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.util.Providers;
import java.io.*;
import java.nio.charset.StandardCharsets;
+import java.util.Optional;
import javax.servlet.FilterChain;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
@@ -37,10 +44,12 @@
@Mock private ProjectInitializationAction projectInitializationAction;
@Mock private UpdateHeadAction updateHEADAction;
@Mock private ProjectDeletionAction projectDeletionAction;
- @Mock private ProjectsCollection projectsCollection;
- @Mock private ProjectResource projectResource;
+ @Mock private ProjectCache projectCache;
+ @Mock private ProjectState projectState;
@Mock private ServletOutputStream outputStream;
@Mock private PrintWriter printWriter;
+ @Mock private IdentifiedUser identifiedUserMock;
+ @Mock private AnonymousUser anonymousUserMock;
private final String PLUGIN_NAME = "pull-replication";
private final String PROJECT_NAME = "some-project";
private final String PROJECT_NAME_GIT = "some-project.git";
@@ -60,6 +69,10 @@
private final Response OK_RESPONSE = Response.ok();
private PullReplicationFilter createPullReplicationFilter() {
+ return createPullReplicationFilter(identifiedUserMock);
+ }
+
+ private PullReplicationFilter createPullReplicationFilter(CurrentUser currentUser) {
return new PullReplicationFilter(
fetchAction,
applyObjectAction,
@@ -67,8 +80,9 @@
projectInitializationAction,
updateHEADAction,
projectDeletionAction,
- projectsCollection,
- PLUGIN_NAME);
+ projectCache,
+ PLUGIN_NAME,
+ Providers.of(currentUser));
}
private void defineBehaviours(byte[] payload, String uri) throws Exception {
@@ -76,15 +90,14 @@
InputStream is = new ByteArrayInputStream(payload);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
when(request.getReader()).thenReturn(bufferedReader);
- when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
- .thenReturn(projectResource);
+ when(projectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(Optional.of(projectState));
when(response.getWriter()).thenReturn(printWriter);
}
private void verifyBehaviours() throws Exception {
verify(request, atLeastOnce()).getRequestURI();
verify(request).getReader();
- verify(projectsCollection).parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME));
+ verify(projectCache).get(Project.nameKey(PROJECT_NAME));
verify(response).getWriter();
verify(response).setContentType("application/json");
verify(response).setStatus(HttpServletResponse.SC_OK);
@@ -107,7 +120,7 @@
pullReplicationFilter.doFilter(request, response, filterChain);
verifyBehaviours();
- verify(fetchAction).apply(eq(projectResource), any());
+ verify(fetchAction).apply(any(ProjectResource.class), any());
}
@Test
@@ -130,7 +143,7 @@
pullReplicationFilter.doFilter(request, response, filterChain);
verifyBehaviours();
- verify(applyObjectAction).apply(eq(projectResource), any());
+ verify(applyObjectAction).apply(any(ProjectResource.class), any());
}
@Test
@@ -152,7 +165,7 @@
pullReplicationFilter.doFilter(request, response, filterChain);
verifyBehaviours();
- verify(applyObjectsAction).apply(eq(projectResource), any());
+ verify(applyObjectsAction).apply(any(ProjectResource.class), any());
}
@Test
@@ -160,15 +173,11 @@
when(request.getRequestURI()).thenReturn(INIT_PROJECT_URI);
when(request.getHeader(ACCEPT)).thenReturn(MediaType.PLAIN_TEXT_UTF_8.toString());
- when(projectInitializationAction.initProject(PROJECT_NAME_GIT)).thenReturn(true);
- when(response.getWriter()).thenReturn(printWriter);
final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
pullReplicationFilter.doFilter(request, response, filterChain);
- verify(request, times(5)).getRequestURI();
- verify(projectInitializationAction).initProject(eq(PROJECT_NAME_GIT));
- verify(response).getWriter();
+ verify(projectInitializationAction).service(request, response);
}
@Test
@@ -183,15 +192,14 @@
pullReplicationFilter.doFilter(request, response, filterChain);
verifyBehaviours();
- verify(updateHEADAction).apply(eq(projectResource), any());
+ verify(updateHEADAction).apply(any(ProjectResource.class), any());
}
@Test
public void shouldFilterProjectDeletionAction() throws Exception {
when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
when(request.getMethod()).thenReturn("DELETE");
- when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
- .thenReturn(projectResource);
+ when(projectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(Optional.of(projectState));
when(projectDeletionAction.apply(any(), any())).thenReturn(OK_RESPONSE);
when(response.getWriter()).thenReturn(printWriter);
@@ -199,8 +207,8 @@
pullReplicationFilter.doFilter(request, response, filterChain);
verify(request, times(7)).getRequestURI();
- verify(projectsCollection).parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME));
- verify(projectDeletionAction).apply(eq(projectResource), any());
+ verify(projectCache).get(Project.nameKey(PROJECT_NAME));
+ verify(projectDeletionAction).apply(any(ProjectResource.class), any());
verify(response).getWriter();
verify(response).setContentType("application/json");
verify(response).setStatus(OK_RESPONSE.statusCode());
@@ -215,6 +223,17 @@
}
@Test
+ public void shouldGoNextInChainWhenAnonymousRequestUriDoesNotMatch() throws Exception {
+ when(request.getRequestURI()).thenReturn("any-url");
+ lenient().when(response.getOutputStream()).thenReturn(outputStream);
+
+ final PullReplicationFilter pullReplicationFilter =
+ createPullReplicationFilter(anonymousUserMock);
+ pullReplicationFilter.doFilter(request, response, filterChain);
+ verify(filterChain).doFilter(request, response);
+ }
+
+ @Test
public void shouldBe404WhenJsonIsMalformed() throws Exception {
byte[] payloadMalformedJson = "some-json-malformed".getBytes(StandardCharsets.UTF_8);
InputStream is = new ByteArrayInputStream(payloadMalformedJson);
@@ -230,24 +249,10 @@
}
@Test
- public void shouldBe500WhenProjectCannotBeInitiated() throws Exception {
- when(request.getRequestURI()).thenReturn(INIT_PROJECT_URI);
- when(request.getHeader(ACCEPT)).thenReturn(MediaType.PLAIN_TEXT_UTF_8.toString());
- when(projectInitializationAction.initProject(PROJECT_NAME_GIT)).thenReturn(false);
- when(response.getOutputStream()).thenReturn(outputStream);
-
- final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
- pullReplicationFilter.doFilter(request, response, filterChain);
-
- verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
-
- @Test
public void shouldBe500WhenResourceNotFound() throws Exception {
when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
when(request.getMethod()).thenReturn("DELETE");
- when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
- .thenReturn(projectResource);
+ when(projectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(Optional.of(projectState));
when(projectDeletionAction.apply(any(), any()))
.thenThrow(new ResourceNotFoundException("resource not found"));
when(response.getOutputStream()).thenReturn(outputStream);
@@ -280,6 +285,19 @@
}
@Test
+ public void shouldBe401WhenUserIsAnonymous() throws Exception {
+ byte[] payloadFetchAction = "{}".getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadFetchAction, FETCH_URI);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter(anonymousUserMock);
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+
+ @Test
public void shouldBe422WhenEntityCannotBeProcessed() throws Exception {
byte[] payloadFetchAction =
("{"
@@ -304,8 +322,7 @@
public void shouldBe409WhenThereIsResourceConflict() throws Exception {
when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
when(request.getMethod()).thenReturn("DELETE");
- when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
- .thenReturn(projectResource);
+ when(projectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(Optional.of(projectState));
when(projectDeletionAction.apply(any(), any()))
.thenThrow(new ResourceConflictException("Resource conflict"));