summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java166
1 files changed, 126 insertions, 40 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
index fd7649a4d2..5cf5f7a32e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
@@ -14,27 +14,36 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ReplicationUser;
+import com.google.gerrit.server.account.GroupMembership;
+import com.google.gerrit.server.account.ListGroupMembership;
import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.PerRequestProjectControlCache;
import com.google.gerrit.server.project.ProjectControl;
-import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
-import com.google.inject.assistedinject.FactoryProvider;
+import com.google.inject.servlet.RequestScoped;
import com.jcraft.jsch.Session;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.RefSpec;
@@ -58,7 +67,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
/** Manages automatic replication to remote repositories. */
@@ -66,6 +74,13 @@ import java.util.concurrent.TimeUnit;
public class PushReplication implements ReplicationQueue {
static final Logger log = LoggerFactory.getLogger(PushReplication.class);
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(ReplicationQueue.class).to(PushReplication.class);
+ }
+ }
+
static {
// Install our own factory which always runs in batch mode, as we
// have no UI available for interactive prompting.
@@ -83,15 +98,18 @@ public class PushReplication implements ReplicationQueue {
private final List<ReplicationConfig> configs;
private final SchemaFactory<ReviewDb> database;
private final ReplicationUser.Factory replicationUserFactory;
+ private final GitRepositoryManager gitRepositoryManager;
@Inject
PushReplication(final Injector i, final WorkQueue wq, final SitePaths site,
- final ReplicationUser.Factory ruf, final SchemaFactory<ReviewDb> db)
+ final ReplicationUser.Factory ruf, final SchemaFactory<ReviewDb> db,
+ final GitRepositoryManager grm)
throws ConfigInvalidException, IOException {
injector = i;
workQueue = wq;
database = db;
replicationUserFactory = ruf;
+ gitRepositoryManager = grm;
configs = allConfigs(site);
}
@@ -105,7 +123,7 @@ public class PushReplication implements ReplicationQueue {
final String urlMatch) {
for (final ReplicationConfig cfg : configs) {
for (final URIish uri : cfg.getURIs(project, urlMatch)) {
- cfg.schedule(project, PushOp.MIRROR_ALL, uri);
+ cfg.schedule(project, PushOp.ALL_REFS, uri);
}
}
}
@@ -186,7 +204,7 @@ public class PushReplication implements ReplicationQueue {
}
r.add(new ReplicationConfig(injector, workQueue, c, cfg, database,
- replicationUserFactory));
+ replicationUserFactory, gitRepositoryManager));
}
return Collections.unmodifiableList(r);
}
@@ -254,16 +272,43 @@ public class PushReplication implements ReplicationQueue {
}
private void replicateProject(final URIish replicateURI, final String head) {
+ if (!replicateURI.isRemote()) {
+ replicateProjectLocally(replicateURI, head);
+ } else if (usingSSH(replicateURI)) {
+ replicateProjectOverSsh(replicateURI, head);
+ } else {
+ log.warn("Cannot create new project on remote site since neither the "
+ + "connection method is SSH nor the replication target is local: "
+ + replicateURI.toString());
+ return;
+ }
+ }
+
+ private void replicateProjectLocally(final URIish replicateURI,
+ final String head) {
+ try {
+ final Repository repo = new FileRepository(replicateURI.getPath());
+ try {
+ repo.create(true /* bare */);
+
+ final RefUpdate u = repo.updateRef(Constants.HEAD);
+ u.disableRefLog();
+ u.link(head);
+ } finally {
+ repo.close();
+ }
+ } catch (IOException e) {
+ log.error("Failed to replicate project locally: "
+ + replicateURI.getPath());
+ }
+ }
+
+ private void replicateProjectOverSsh(final URIish replicateURI,
+ final String head) {
SshSessionFactory sshFactory = SshSessionFactory.getInstance();
RemoteSession sshSession;
String projectPath = QuotedString.BOURNE.quote(replicateURI.getPath());
- if (!usingSSH(replicateURI)) {
- log.warn("Cannot create new project on remote site since the connection "
- + "method is not SSH: " + replicateURI.toString());
- return;
- }
-
OutputStream errStream = createErrStream();
String cmd =
"mkdir -p " + projectPath + "&& cd " + projectPath
@@ -308,7 +353,7 @@ public class PushReplication implements ReplicationQueue {
}
@Override
- public synchronized void write(final int b) throws IOException {
+ public synchronized void write(final int b) {
if (b == '\r') {
return;
}
@@ -341,10 +386,13 @@ public class PushReplication implements ReplicationQueue {
private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>();
private final PushOp.Factory opFactory;
private final ProjectControl.Factory projectControlFactory;
+ private final GitRepositoryManager mgr;
+ private final boolean replicatePermissions;
ReplicationConfig(final Injector injector, final WorkQueue workQueue,
final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db,
- final ReplicationUser.Factory replicationUserFactory) {
+ final ReplicationUser.Factory replicationUserFactory,
+ final GitRepositoryManager gitRepositoryManager) {
remote = rc;
delay = Math.max(0, getInt(rc, cfg, "replicationdelay", 15));
@@ -356,15 +404,18 @@ public class PushReplication implements ReplicationQueue {
String[] authGroupNames =
cfg.getStringList("remote", rc.getName(), "authGroup");
- final Set<AccountGroup.Id> authGroups;
+ final GroupMembership authGroups;
if (authGroupNames.length > 0) {
- authGroups = ConfigUtil.groupsFor(db, authGroupNames, //
- log, "Group \"{0}\" not in database, removing from authGroup");
+ authGroups = new ListGroupMembership(ConfigUtil.groupsFor(db, authGroupNames, //
+ log, "Group \"{0}\" not in database, removing from authGroup"));
} else {
authGroups = ReplicationUser.EVERYTHING_VISIBLE;
}
adminUrls = cfg.getStringList("remote", rc.getName(), "adminUrl");
+ replicatePermissions = cfg.getBoolean("remote", rc.getName(),
+ "replicatePermissions", true);
+ mgr = gitRepositoryManager;
final ReplicationUser remoteUser =
replicationUserFactory.create(authGroups);
@@ -373,18 +424,18 @@ public class PushReplication implements ReplicationQueue {
injector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
+ bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
+ bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
bind(CurrentUser.class).toInstance(remoteUser);
}
}).getInstance(ProjectControl.Factory.class);
- opFactory = injector.createChildInjector(new AbstractModule() {
+ opFactory = injector.createChildInjector(new FactoryModule() {
@Override
protected void configure() {
- bind(PushReplication.ReplicationConfig.class).toInstance(
- ReplicationConfig.this);
+ bind(PushReplication.ReplicationConfig.class).toInstance(ReplicationConfig.this);
bind(RemoteConfig.class).toInstance(remote);
- bind(PushOp.Factory.class).toProvider(
- FactoryProvider.newFactory(PushOp.Factory.class, PushOp.class));
+ factory(PushOp.Factory.class);
}
}).getInstance(PushOp.Factory.class);
}
@@ -396,15 +447,53 @@ public class PushReplication implements ReplicationQueue {
void schedule(final Project.NameKey project, final String ref,
final URIish uri) {
+ PerThreadRequestScope ctx = new PerThreadRequestScope();
+ PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
try {
- if (!controlFor(project).isVisible()) {
+ try {
+ if (!controlFor(project).isVisible()) {
+ return;
+ }
+ } catch (NoSuchProjectException e1) {
+ log.error("Internal error: project " + project
+ + " not found during replication");
return;
}
- } catch (NoSuchProjectException e1) {
- log.error("Internal error: project " + project
- + " not found during replication");
- return;
+ } finally {
+ PerThreadRequestScope.set(old);
+ }
+
+ if (!replicatePermissions) {
+ PushOp e;
+ synchronized (pending) {
+ e = pending.get(uri);
+ }
+ if (e == null) {
+ Repository git;
+ try {
+ git = mgr.openRepository(project);
+ } catch (RepositoryNotFoundException err) {
+ log.error("Internal error: project " + project
+ + " not found during replication", err);
+ return;
+ }
+ try {
+ Ref head = git.getRef(Constants.HEAD);
+ if (head != null
+ && head.isSymbolic()
+ && GitRepositoryManager.REF_CONFIG.equals(head.getLeaf().getName())) {
+ return;
+ }
+ } catch (IOException err) {
+ log.error("Internal error: cannot check type of project " + project
+ + " during replication", err);
+ return;
+ } finally {
+ git.close();
+ }
+ }
}
+
synchronized (pending) {
PushOp e = pending.get(uri);
if (e == null) {
@@ -443,16 +532,6 @@ public class PushReplication implements ReplicationQueue {
* @param pushOp The PushOp instance to be scheduled.
*/
void reschedule(final PushOp pushOp) {
- try {
- if (!controlFor(pushOp.getProjectNameKey()).isVisible()) {
- return;
- }
- } catch (NoSuchProjectException e1) {
- log.error("Internal error: project " + pushOp.getProjectNameKey()
- + " not found during replication");
- return;
- }
-
// It locks access to pending variable.
synchronized (pending) {
URIish uri = pushOp.getURI();
@@ -521,6 +600,9 @@ public class PushReplication implements ReplicationQueue {
}
boolean wouldPushRef(final String ref) {
+ if (!replicatePermissions && GitRepositoryManager.REF_CONFIG.equals(ref)) {
+ return false;
+ }
for (final RefSpec s : remote.getPushRefSpecs()) {
if (s.matchSource(ref)) {
return true;
@@ -529,6 +611,10 @@ public class PushReplication implements ReplicationQueue {
return false;
}
+ boolean isReplicatePermissions() {
+ return replicatePermissions;
+ }
+
List<URIish> getURIs(final Project.NameKey project, final String urlMatch) {
final List<URIish> r = new ArrayList<URIish>(remote.getURIs().size());
for (URIish uri : remote.getURIs()) {