diff options
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.java | 166 |
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()) { |