Move replication logic to replication plugin
This splits all of the replication code out of the core server and moves it into a standard plugin. A new listener API is used inside of the core server to notify interested plugins of any Git reference changes, which the replication code can hook into to schedule its events. Change-Id: I77ee4440a009c2ce1c62fb6a445c7e3c912245f9
This commit is contained in:
@@ -26,8 +26,5 @@ public enum AccessPath {
|
||||
SSH_COMMAND,
|
||||
|
||||
/** Access from a Git client using any Git protocol. */
|
||||
GIT,
|
||||
|
||||
/** Access through replication */
|
||||
REPLICATION;
|
||||
GIT;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import com.google.gerrit.reviewdb.client.TrackingId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.config.TrackingFooter;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeOp;
|
||||
import com.google.gerrit.server.git.ReplicationQueue;
|
||||
import com.google.gerrit.server.mail.EmailException;
|
||||
import com.google.gerrit.server.mail.RebasedPatchSetSender;
|
||||
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
||||
@@ -240,7 +240,7 @@ public class ChangeUtil {
|
||||
RebasedPatchSetSender.Factory rebasedPatchSetSenderFactory,
|
||||
final ChangeHookRunner hooks, GitRepositoryManager gitManager,
|
||||
final PatchSetInfoFactory patchSetInfoFactory,
|
||||
final ReplicationQueue replication, PersonIdent myIdent,
|
||||
final GitReferenceUpdated replication, PersonIdent myIdent,
|
||||
final ChangeControl.Factory changeControlFactory,
|
||||
final ApprovalsUtil approvalsUtil) throws NoSuchChangeException,
|
||||
EmailException, OrmException, MissingObjectException,
|
||||
@@ -381,7 +381,7 @@ public class ChangeUtil {
|
||||
+ ": " + ru.getResult());
|
||||
}
|
||||
|
||||
replication.scheduleUpdate(change.getProject(), ru.getName());
|
||||
replication.fire(change.getProject(), ru.getName());
|
||||
|
||||
List<PatchSetApproval> patchSetApprovals = approvalsUtil.copyVetosToLatestPatchSet(change);
|
||||
|
||||
@@ -424,7 +424,7 @@ public class ChangeUtil {
|
||||
final RevertedSender.Factory revertedSenderFactory,
|
||||
final ChangeHooks hooks, GitRepositoryManager gitManager,
|
||||
final PatchSetInfoFactory patchSetInfoFactory,
|
||||
final ReplicationQueue replication, PersonIdent myIdent)
|
||||
final GitReferenceUpdated replication, PersonIdent myIdent)
|
||||
throws NoSuchChangeException, EmailException, OrmException,
|
||||
MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||
PatchSetInfoNotAvailableException {
|
||||
@@ -495,7 +495,7 @@ public class ChangeUtil {
|
||||
throw new IOException("Failed to create ref " + ps.getRefName()
|
||||
+ " in " + git.getDirectory() + ": " + ru.getResult());
|
||||
}
|
||||
replication.scheduleUpdate(db.changes().get(changeId).getProject(),
|
||||
replication.fire(db.changes().get(changeId).getProject(),
|
||||
ru.getName());
|
||||
|
||||
final ChangeMessage cmsg =
|
||||
@@ -525,7 +525,7 @@ public class ChangeUtil {
|
||||
|
||||
public static void deleteDraftChange(final PatchSet.Id patchSetId,
|
||||
GitRepositoryManager gitManager,
|
||||
final ReplicationQueue replication, final ReviewDb db)
|
||||
final GitReferenceUpdated replication, final ReviewDb db)
|
||||
throws NoSuchChangeException, OrmException, IOException {
|
||||
final Change.Id changeId = patchSetId.getParentKey();
|
||||
final Change change = db.changes().get(changeId);
|
||||
@@ -546,7 +546,7 @@ public class ChangeUtil {
|
||||
|
||||
public static void deleteOnlyDraftPatchSet(final PatchSet patch,
|
||||
final Change change, GitRepositoryManager gitManager,
|
||||
final ReplicationQueue replication, final ReviewDb db)
|
||||
final GitReferenceUpdated replication, final ReviewDb db)
|
||||
throws NoSuchChangeException, OrmException, IOException {
|
||||
final PatchSet.Id patchSetId = patch.getId();
|
||||
if (patch == null || !patch.isDraft()) {
|
||||
@@ -569,7 +569,7 @@ public class ChangeUtil {
|
||||
throw new IOException("Failed to delete ref " + patch.getRefName() +
|
||||
" in " + repo.getDirectory() + ": " + update.getResult());
|
||||
}
|
||||
replication.scheduleUpdate(change.getProject(), update.getName());
|
||||
replication.fire(change.getProject(), update.getName());
|
||||
} finally {
|
||||
repo.close();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
// Copyright (C) 2012 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,40 +14,37 @@
|
||||
|
||||
package com.google.gerrit.server;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.AccountProjectWatch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.account.CapabilityControl;
|
||||
import com.google.gerrit.server.account.GroupMembership;
|
||||
import com.google.gerrit.server.account.ListGroupMembership;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class ReplicationUser extends CurrentUser {
|
||||
/** Magic set of groups enabling read of any project and reference. */
|
||||
public static final GroupMembership EVERYTHING_VISIBLE =
|
||||
new ListGroupMembership(Collections.<AccountGroup.UUID>emptySet());
|
||||
|
||||
/**
|
||||
* User identity for plugin code that needs an identity.
|
||||
* <p>
|
||||
* An InternalUser has no real identity, it acts as the server and can access
|
||||
* anything it wants, anytime it wants, given the JVM's own direct access to
|
||||
* data. Plugins may use this when they need to have a CurrentUser with read
|
||||
* permission on anything.
|
||||
*/
|
||||
public class InternalUser extends CurrentUser {
|
||||
public interface Factory {
|
||||
ReplicationUser create(@Assisted GroupMembership authGroups);
|
||||
InternalUser create();
|
||||
}
|
||||
|
||||
private final GroupMembership effectiveGroups;
|
||||
|
||||
@Inject
|
||||
protected ReplicationUser(CapabilityControl.Factory capabilityControlFactory,
|
||||
@Assisted GroupMembership authGroups) {
|
||||
super(capabilityControlFactory, AccessPath.REPLICATION);
|
||||
effectiveGroups = authGroups;
|
||||
protected InternalUser(CapabilityControl.Factory capabilityControlFactory) {
|
||||
super(capabilityControlFactory, AccessPath.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupMembership getEffectiveGroups() {
|
||||
return effectiveGroups;
|
||||
return GroupMembership.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +57,8 @@ public class ReplicationUser extends CurrentUser {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public boolean isEverythingVisible() {
|
||||
return getEffectiveGroups() == EVERYTHING_VISIBLE;
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InternalUser";
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.ReplicationQueue;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
@@ -44,7 +44,7 @@ public class DeleteDraftPatchSet implements Callable<ReviewResult> {
|
||||
private final ChangeControl.Factory changeControlFactory;
|
||||
private final ReviewDb db;
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated replication;
|
||||
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||
|
||||
private final PatchSet.Id patchSetId;
|
||||
@@ -52,7 +52,7 @@ public class DeleteDraftPatchSet implements Callable<ReviewResult> {
|
||||
@Inject
|
||||
DeleteDraftPatchSet(ChangeControl.Factory changeControlFactory,
|
||||
ReviewDb db, GitRepositoryManager gitManager,
|
||||
ReplicationQueue replication, PatchSetInfoFactory patchSetInfoFactory,
|
||||
GitReferenceUpdated replication, PatchSetInfoFactory patchSetInfoFactory,
|
||||
@Assisted final PatchSet.Id patchSetId) {
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
this.db = db;
|
||||
|
||||
@@ -17,13 +17,16 @@ package com.google.gerrit.server.config;
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
|
||||
import com.google.gerrit.common.data.ApprovalTypes;
|
||||
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
|
||||
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.AuthType;
|
||||
import com.google.gerrit.rules.PrologModule;
|
||||
import com.google.gerrit.rules.RulesCache;
|
||||
import com.google.gerrit.server.FileTypeRegistry;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.InternalUser;
|
||||
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
|
||||
import com.google.gerrit.server.ReplicationUser;
|
||||
import com.google.gerrit.server.account.AccountByEmailCacheImpl;
|
||||
import com.google.gerrit.server.account.AccountCacheImpl;
|
||||
import com.google.gerrit.server.account.AccountInfoCacheFactory;
|
||||
@@ -39,12 +42,11 @@ import com.google.gerrit.server.account.MaterializedGroupMembership;
|
||||
import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.auth.ldap.LdapModule;
|
||||
import com.google.gerrit.server.events.EventFactory;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.ChangeMergeQueue;
|
||||
import com.google.gerrit.server.git.GitModule;
|
||||
import com.google.gerrit.server.git.MergeQueue;
|
||||
import com.google.gerrit.server.git.PushAllProjectsOp;
|
||||
import com.google.gerrit.server.git.ReloadSubmitQueueOp;
|
||||
import com.google.gerrit.server.git.SecureCredentialsProvider;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.git.TransferConfig;
|
||||
import com.google.gerrit.server.mail.FromAddressGenerator;
|
||||
@@ -119,6 +121,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
factory(AccountInfoCacheFactory.Factory.class);
|
||||
factory(CapabilityControl.Factory.class);
|
||||
factory(GroupInfoCacheFactory.Factory.class);
|
||||
factory(InternalUser.Factory.class);
|
||||
factory(ProjectNode.Factory.class);
|
||||
factory(ProjectState.Factory.class);
|
||||
factory(MaterializedGroupMembership.Factory.class);
|
||||
@@ -132,9 +135,6 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
bind(EventFactory.class);
|
||||
bind(TransferConfig.class);
|
||||
|
||||
factory(SecureCredentialsProvider.Factory.class);
|
||||
factory(PushAllProjectsOp.Factory.class);
|
||||
|
||||
bind(ChangeMergeQueue.class).in(SINGLETON);
|
||||
bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
|
||||
factory(ReloadSubmitQueueOp.Factory.class);
|
||||
@@ -150,6 +150,9 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
bind(ChangeControl.GenericFactory.class);
|
||||
bind(ProjectControl.GenericFactory.class);
|
||||
factory(FunctionState.Factory.class);
|
||||
factory(ReplicationUser.Factory.class);
|
||||
|
||||
bind(GitReferenceUpdated.class);
|
||||
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
|
||||
DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,9 @@ package com.google.gerrit.server.config;
|
||||
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.git.PushAllProjectsOp;
|
||||
import com.google.gerrit.server.git.ReloadSubmitQueueOp;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Configuration for a master node in a cluster of servers. */
|
||||
@@ -32,26 +29,15 @@ public class MasterNodeStartup extends LifecycleModule {
|
||||
}
|
||||
|
||||
static class OnStart implements LifecycleListener {
|
||||
private final PushAllProjectsOp.Factory pushAll;
|
||||
private final ReloadSubmitQueueOp.Factory submit;
|
||||
private final boolean replicateOnStartup;
|
||||
|
||||
@Inject
|
||||
OnStart(final PushAllProjectsOp.Factory pushAll,
|
||||
final ReloadSubmitQueueOp.Factory submit,
|
||||
final @GerritServerConfig Config cfg) {
|
||||
this.pushAll = pushAll;
|
||||
OnStart(final ReloadSubmitQueueOp.Factory submit) {
|
||||
this.submit = submit;
|
||||
|
||||
replicateOnStartup = cfg.getBoolean("gerrit", "replicateOnStartup", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (replicateOnStartup) {
|
||||
pushAll.create(null).start(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
submit.create().start(15, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ public final class SitePaths {
|
||||
|
||||
public final File gerrit_config;
|
||||
public final File secure_config;
|
||||
public final File replication_config;
|
||||
public final File contact_information_pub;
|
||||
|
||||
public final File ssl_keystore;
|
||||
@@ -78,7 +77,6 @@ public final class SitePaths {
|
||||
|
||||
gerrit_config = new File(etc_dir, "gerrit.config");
|
||||
secure_config = new File(etc_dir, "secure.config");
|
||||
replication_config = new File(etc_dir, "replication.config");
|
||||
contact_information_pub = new File(etc_dir, "contact_information.pub");
|
||||
|
||||
ssl_keystore = new File(etc_dir, "keystore");
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2012 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.google.gerrit.server.extensions.events;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GitReferenceUpdated {
|
||||
public static final GitReferenceUpdated DISABLED = new GitReferenceUpdated(
|
||||
Collections.<GitReferenceUpdatedListener> emptyList());
|
||||
|
||||
private final Iterable<GitReferenceUpdatedListener> listeners;
|
||||
|
||||
@Inject
|
||||
GitReferenceUpdated(DynamicSet<GitReferenceUpdatedListener> listeners) {
|
||||
this.listeners = listeners;
|
||||
}
|
||||
|
||||
GitReferenceUpdated(Iterable<GitReferenceUpdatedListener> listeners) {
|
||||
this.listeners = listeners;
|
||||
}
|
||||
|
||||
public void fire(Project.NameKey project, String ref) {
|
||||
Event event = new Event(project, ref);
|
||||
for (GitReferenceUpdatedListener l : listeners) {
|
||||
l.onGitReferenceUpdated(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Event implements GitReferenceUpdatedListener.Event {
|
||||
private final String projectName;
|
||||
private final String ref;
|
||||
|
||||
Event(Project.NameKey project, String ref) {
|
||||
this.projectName = project.get();
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GitReferenceUpdatedListener.Update> getUpdates() {
|
||||
GitReferenceUpdatedListener.Update update =
|
||||
new GitReferenceUpdatedListener.Update() {
|
||||
public String getRefName() {
|
||||
return ref;
|
||||
}
|
||||
};
|
||||
return ImmutableList.of(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.ConfigConstants;
|
||||
@@ -34,6 +36,9 @@ import org.eclipse.jgit.lib.StoredConfig;
|
||||
import org.eclipse.jgit.storage.file.LockFile;
|
||||
import org.eclipse.jgit.storage.file.WindowCache;
|
||||
import org.eclipse.jgit.storage.file.WindowCacheConfig;
|
||||
import org.eclipse.jgit.transport.JschConfigSessionFactory;
|
||||
import org.eclipse.jgit.transport.OpenSshConfig;
|
||||
import org.eclipse.jgit.transport.SshSessionFactory;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.eclipse.jgit.util.IO;
|
||||
import org.eclipse.jgit.util.RawParseUtils;
|
||||
@@ -82,6 +87,15 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// Install our own factory which always runs in batch mode, as we
|
||||
// have no UI available for interactive prompting.
|
||||
SshSessionFactory.setInstance(new JschConfigSessionFactory() {
|
||||
@Override
|
||||
protected void configure(OpenSshConfig.Host hc, Session session) {
|
||||
// Default configuration is batch mode.
|
||||
}
|
||||
});
|
||||
|
||||
final WindowCacheConfig c = new WindowCacheConfig();
|
||||
c.fromConfig(cfg);
|
||||
WindowCache.reconfigure(c);
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.mail.MergeFailSender;
|
||||
import com.google.gerrit.server.mail.MergedSender;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
@@ -134,7 +135,7 @@ public class MergeOp {
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final ProjectCache projectCache;
|
||||
private final FunctionState.Factory functionState;
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated replication;
|
||||
private final MergedSender.Factory mergedSenderFactory;
|
||||
private final MergeFailSender.Factory mergeFailSenderFactory;
|
||||
private final Provider<String> urlProvider;
|
||||
@@ -170,7 +171,7 @@ public class MergeOp {
|
||||
@Inject
|
||||
MergeOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> sf,
|
||||
final ProjectCache pc, final FunctionState.Factory fs,
|
||||
final ReplicationQueue rq, final MergedSender.Factory msf,
|
||||
final GitReferenceUpdated rq, final MergedSender.Factory msf,
|
||||
final MergeFailSender.Factory mfsf,
|
||||
@CanonicalWebUrl @Nullable final Provider<String> cwu,
|
||||
final ApprovalTypes approvalTypes, final PatchSetInfoFactory psif,
|
||||
@@ -1029,8 +1030,7 @@ public class MergeOp {
|
||||
ps.getProject().getDescription());
|
||||
}
|
||||
|
||||
replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate
|
||||
.getName());
|
||||
replication.fire(destBranch.getParentKey(), branchUpdate.getName());
|
||||
|
||||
Account account = null;
|
||||
final PatchSetApproval submitter = getSubmitter(db, mergeTip.patchsetId);
|
||||
@@ -1125,7 +1125,7 @@ public class MergeOp {
|
||||
} catch (CodeReviewNoteCreationException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
replication.scheduleUpdate(destBranch.getParentKey(),
|
||||
replication.fire(destBranch.getParentKey(),
|
||||
GitRepositoryManager.REFS_NOTES_REVIEW);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.git;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
@@ -86,13 +87,13 @@ public class MetaDataUpdate {
|
||||
@Assisted Repository db);
|
||||
}
|
||||
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated replication;
|
||||
private final Project.NameKey projectName;
|
||||
private final Repository db;
|
||||
private final CommitBuilder commit;
|
||||
|
||||
@Inject
|
||||
public MetaDataUpdate(ReplicationQueue replication,
|
||||
public MetaDataUpdate(GitReferenceUpdated replication,
|
||||
@Assisted Project.NameKey projectName, @Assisted Repository db) {
|
||||
this.replication = replication;
|
||||
this.projectName = projectName;
|
||||
@@ -123,8 +124,6 @@ public class MetaDataUpdate {
|
||||
}
|
||||
|
||||
void replicate(String ref) {
|
||||
if (replication.isEnabled()) {
|
||||
replication.scheduleUpdate(projectName, ref);
|
||||
}
|
||||
replication.fire(projectName, ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2011 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
||||
/** A disabled {@link ReplicationQueue}. */
|
||||
public final class NoReplication implements ReplicationQueue {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleUpdate(Project.NameKey project, String ref) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleFullSync(Project.NameKey project, String urlMatch) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replicateNewProject(Project.NameKey project, String head) {
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import com.google.inject.Scope;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class PerThreadRequestScope {
|
||||
public class PerThreadRequestScope {
|
||||
static class Propagator
|
||||
extends ThreadLocalRequestScopePropagator<PerThreadRequestScope> {
|
||||
Propagator() {
|
||||
@@ -49,13 +49,13 @@ class PerThreadRequestScope {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static PerThreadRequestScope set(PerThreadRequestScope ctx) {
|
||||
public static PerThreadRequestScope set(PerThreadRequestScope ctx) {
|
||||
PerThreadRequestScope old = current.get();
|
||||
current.set(ctx);
|
||||
return old;
|
||||
}
|
||||
|
||||
static final Scope REQUEST = new Scope() {
|
||||
public static final Scope REQUEST = new Scope() {
|
||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
|
||||
return new Provider<T>() {
|
||||
public T get() {
|
||||
@@ -81,7 +81,7 @@ class PerThreadRequestScope {
|
||||
final RequestCleanup cleanup;
|
||||
private final Map<Key<?>, Object> map;
|
||||
|
||||
PerThreadRequestScope() {
|
||||
public PerThreadRequestScope() {
|
||||
cleanup = new RequestCleanup();
|
||||
map = new HashMap<Key<?>, Object>();
|
||||
map.put(RC_KEY, cleanup);
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (C) 2009 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PushAllProjectsOp extends DefaultQueueOp {
|
||||
public interface Factory {
|
||||
PushAllProjectsOp create(String urlMatch);
|
||||
}
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(PushAllProjectsOp.class);
|
||||
|
||||
private final ProjectCache projectCache;
|
||||
private final ReplicationQueue replication;
|
||||
private final String urlMatch;
|
||||
|
||||
@Inject
|
||||
public PushAllProjectsOp(final WorkQueue wq, final ProjectCache projectCache,
|
||||
final ReplicationQueue rq, @Assisted @Nullable final String urlMatch) {
|
||||
super(wq);
|
||||
this.projectCache = projectCache;
|
||||
this.replication = rq;
|
||||
this.urlMatch = urlMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final int delay, final TimeUnit unit) {
|
||||
if (replication.isEnabled()) {
|
||||
super.start(delay, unit);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
for (final Project.NameKey nameKey : projectCache.all()) {
|
||||
replication.scheduleFullSync(nameKey, urlMatch);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
log.error("Cannot enumerate known projects", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "Replicate All Projects";
|
||||
if (urlMatch != null) {
|
||||
s = s + " to " + urlMatch;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
// Copyright (C) 2009 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.Project.NameKey;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import com.jcraft.jsch.JSchException;
|
||||
|
||||
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
|
||||
import org.eclipse.jgit.errors.NotSupportedException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.errors.TransportException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.FetchConnection;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.RemoteConfig;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.eclipse.jgit.transport.Transport;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A push to remote operation started by {@link ReplicationQueue}.
|
||||
* <p>
|
||||
* Instance members are protected by the lock within PushQueue. Callers must
|
||||
* take that lock to ensure they are working with a current view of the object.
|
||||
*/
|
||||
class PushOp implements ProjectRunnable {
|
||||
interface Factory {
|
||||
PushOp create(Project.NameKey d, URIish u);
|
||||
}
|
||||
|
||||
private static final Logger log = PushReplication.log;
|
||||
static final String ALL_REFS = "..all..";
|
||||
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
private final PushReplication.ReplicationConfig pool;
|
||||
private final RemoteConfig config;
|
||||
private final CredentialsProvider credentialsProvider;
|
||||
private final TagCache tagCache;
|
||||
|
||||
private final Set<String> delta = new HashSet<String>();
|
||||
private final Project.NameKey projectName;
|
||||
private final URIish uri;
|
||||
private boolean pushAllRefs;
|
||||
|
||||
private Repository db;
|
||||
|
||||
/**
|
||||
* It indicates if the current instance is in fact retrying to push.
|
||||
*/
|
||||
private boolean retrying;
|
||||
|
||||
private boolean canceled;
|
||||
|
||||
@Inject
|
||||
PushOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> s,
|
||||
final PushReplication.ReplicationConfig p, final RemoteConfig c,
|
||||
final SecureCredentialsProvider.Factory cpFactory,
|
||||
final TagCache tc,
|
||||
@Assisted final Project.NameKey d, @Assisted final URIish u) {
|
||||
repoManager = grm;
|
||||
schema = s;
|
||||
pool = p;
|
||||
config = c;
|
||||
credentialsProvider = cpFactory.create(c.getName());
|
||||
tagCache = tc;
|
||||
projectName = d;
|
||||
uri = u;
|
||||
}
|
||||
|
||||
public boolean isRetrying() {
|
||||
return retrying;
|
||||
}
|
||||
|
||||
public void setToRetry() {
|
||||
retrying = true;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
public boolean wasCanceled() {
|
||||
return canceled;
|
||||
}
|
||||
|
||||
URIish getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
void addRef(final String ref) {
|
||||
if (ALL_REFS.equals(ref)) {
|
||||
delta.clear();
|
||||
pushAllRefs = true;
|
||||
} else if (!pushAllRefs) {
|
||||
delta.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getRefs() {
|
||||
final Set<String> refs;
|
||||
|
||||
if (pushAllRefs) {
|
||||
refs = new HashSet<String>(1);
|
||||
refs.add(ALL_REFS);
|
||||
} else {
|
||||
refs = delta;
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
public void addRefs(Set<String> refs) {
|
||||
if (!pushAllRefs) {
|
||||
for (String ref : refs) {
|
||||
addRef(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||
try {
|
||||
runPushOperation();
|
||||
} finally {
|
||||
PerThreadRequestScope.set(old);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPushOperation() {
|
||||
// Lock the queue, and remove ourselves, so we can't be modified once
|
||||
// we start replication (instead a new instance, with the same URI, is
|
||||
// created and scheduled for a future point in time.)
|
||||
//
|
||||
pool.notifyStarting(this);
|
||||
|
||||
// It should only verify if it was canceled after calling notifyStarting,
|
||||
// since the canceled flag would be set locking the queue.
|
||||
if (!canceled) {
|
||||
try {
|
||||
db = repoManager.openRepository(projectName);
|
||||
runImpl();
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
log.error("Cannot replicate " + projectName + "; " + e.getMessage());
|
||||
|
||||
} catch (NoRemoteRepositoryException e) {
|
||||
log.error("Cannot replicate to " + uri + "; repository not found");
|
||||
|
||||
} catch (NotSupportedException e) {
|
||||
log.error("Cannot replicate to " + uri, e);
|
||||
|
||||
} catch (TransportException e) {
|
||||
final Throwable cause = e.getCause();
|
||||
if (cause instanceof JSchException
|
||||
&& cause.getMessage().startsWith("UnknownHostKey:")) {
|
||||
log.error("Cannot replicate to " + uri + ": " + cause.getMessage());
|
||||
} else {
|
||||
log.error("Cannot replicate to " + uri, e);
|
||||
}
|
||||
|
||||
// The remote push operation should be retried.
|
||||
pool.reschedule(this);
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot replicate to " + uri, e);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
log.error("Unexpected error during replication to " + uri, e);
|
||||
|
||||
} catch (Error e) {
|
||||
log.error("Unexpected error during replication to " + uri, e);
|
||||
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "push " + uri;
|
||||
}
|
||||
|
||||
private void runImpl() throws IOException {
|
||||
final Transport tn = Transport.open(db, uri);
|
||||
final PushResult res;
|
||||
try {
|
||||
res = pushVia(tn);
|
||||
} finally {
|
||||
try {
|
||||
tn.close();
|
||||
} catch (Throwable e2) {
|
||||
log.warn("Unexpected error while closing " + uri, e2);
|
||||
}
|
||||
}
|
||||
|
||||
for (final RemoteRefUpdate u : res.getRemoteUpdates()) {
|
||||
switch (u.getStatus()) {
|
||||
case OK:
|
||||
case UP_TO_DATE:
|
||||
case NON_EXISTING:
|
||||
break;
|
||||
|
||||
case NOT_ATTEMPTED:
|
||||
case AWAITING_REPORT:
|
||||
case REJECTED_NODELETE:
|
||||
case REJECTED_NONFASTFORWARD:
|
||||
case REJECTED_REMOTE_CHANGED:
|
||||
log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
|
||||
+ ": status " + u.getStatus().name());
|
||||
break;
|
||||
|
||||
case REJECTED_OTHER_REASON:
|
||||
if ("non-fast-forward".equals(u.getMessage())) {
|
||||
log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
|
||||
+ ", remote rejected non-fast-forward push."
|
||||
+ " Check receive.denyNonFastForwards variable in config file"
|
||||
+ " of destination repository.");
|
||||
} else {
|
||||
log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
|
||||
+ ", reason: " + u.getMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PushResult pushVia(final Transport tn) throws IOException,
|
||||
NotSupportedException, TransportException {
|
||||
tn.applyConfig(config);
|
||||
tn.setCredentialsProvider(credentialsProvider);
|
||||
|
||||
final List<RemoteRefUpdate> todo = generateUpdates(tn);
|
||||
if (todo.isEmpty()) {
|
||||
// If we have no commands selected, we have nothing to do.
|
||||
// Calling JGit at this point would just redo the work we
|
||||
// already did, and come up with the same answer. Instead
|
||||
// send back an empty result.
|
||||
//
|
||||
return new PushResult();
|
||||
}
|
||||
|
||||
return tn.push(NullProgressMonitor.INSTANCE, todo);
|
||||
}
|
||||
|
||||
private List<RemoteRefUpdate> generateUpdates(final Transport tn)
|
||||
throws IOException {
|
||||
final ProjectControl pc;
|
||||
try {
|
||||
pc = pool.controlFor(projectName);
|
||||
} catch (NoSuchProjectException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<String, Ref> local = db.getAllRefs();
|
||||
if (!pc.allRefsAreVisible()) {
|
||||
if (!pushAllRefs) {
|
||||
// If we aren't mirroring, reduce the space we need to filter
|
||||
// to only the references we will update during this operation.
|
||||
//
|
||||
Map<String, Ref> n = new HashMap<String, Ref>();
|
||||
for (String src : delta) {
|
||||
Ref r = local.get(src);
|
||||
if (r != null) {
|
||||
n.put(src, r);
|
||||
}
|
||||
}
|
||||
local = n;
|
||||
}
|
||||
|
||||
final ReviewDb meta;
|
||||
try {
|
||||
meta = schema.open();
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot read database to replicate to " + projectName, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
local = new VisibleRefFilter(tagCache, db, pc, meta, true).filter(local, true);
|
||||
} finally {
|
||||
meta.close();
|
||||
}
|
||||
}
|
||||
|
||||
final boolean noPerms = !pool.isReplicatePermissions();
|
||||
final List<RemoteRefUpdate> cmds = new ArrayList<RemoteRefUpdate>();
|
||||
if (pushAllRefs) {
|
||||
final Map<String, Ref> remote = listRemote(tn);
|
||||
|
||||
for (final Ref src : local.values()) {
|
||||
if (noPerms && GitRepositoryManager.REF_CONFIG.equals(src.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final RefSpec spec = matchSrc(src.getName());
|
||||
if (spec != null) {
|
||||
final Ref dst = remote.get(spec.getDestination());
|
||||
if (dst == null || !src.getObjectId().equals(dst.getObjectId())) {
|
||||
// Doesn't exist yet, or isn't the same value, request to push.
|
||||
//
|
||||
send(cmds, spec, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isMirror()) {
|
||||
for (final Ref ref : remote.values()) {
|
||||
if (!Constants.HEAD.equals(ref.getName())) {
|
||||
final RefSpec spec = matchDst(ref.getName());
|
||||
if (spec != null && !local.containsKey(spec.getSource())) {
|
||||
// No longer on local side, request removal.
|
||||
//
|
||||
delete(cmds, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (final String src : delta) {
|
||||
final RefSpec spec = matchSrc(src);
|
||||
if (spec != null) {
|
||||
// If the ref still exists locally, send it, otherwise delete it.
|
||||
//
|
||||
Ref srcRef = local.get(src);
|
||||
if (srcRef != null &&
|
||||
!(noPerms && GitRepositoryManager.REF_CONFIG.equals(src))) {
|
||||
send(cmds, spec, srcRef);
|
||||
} else if (config.isMirror()) {
|
||||
delete(cmds, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmds;
|
||||
}
|
||||
|
||||
private Map<String, Ref> listRemote(final Transport tn)
|
||||
throws NotSupportedException, TransportException {
|
||||
final FetchConnection fc = tn.openFetch();
|
||||
try {
|
||||
return fc.getRefsMap();
|
||||
} finally {
|
||||
fc.close();
|
||||
}
|
||||
}
|
||||
|
||||
private RefSpec matchSrc(final String ref) {
|
||||
for (final RefSpec s : config.getPushRefSpecs()) {
|
||||
if (s.matchSource(ref)) {
|
||||
return s.expandFromSource(ref);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private RefSpec matchDst(final String ref) {
|
||||
for (final RefSpec s : config.getPushRefSpecs()) {
|
||||
if (s.matchDestination(ref)) {
|
||||
return s.expandFromDestination(ref);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void send(final List<RemoteRefUpdate> cmds, final RefSpec spec,
|
||||
final Ref src) throws IOException {
|
||||
final String dst = spec.getDestination();
|
||||
final boolean force = spec.isForceUpdate();
|
||||
cmds.add(new RemoteRefUpdate(db, src, dst, force, null, null));
|
||||
}
|
||||
|
||||
private void delete(final List<RemoteRefUpdate> cmds, final RefSpec spec)
|
||||
throws IOException {
|
||||
final String dst = spec.getDestination();
|
||||
final boolean force = spec.isForceUpdate();
|
||||
cmds.add(new RemoteRefUpdate(db, (Ref) null, dst, force, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameKey getProjectNameKey() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteName() {
|
||||
return config.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomizedPrint() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,684 +0,0 @@
|
||||
// Copyright (C) 2009 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
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.GroupCache;
|
||||
import com.google.gerrit.server.account.GroupMembership;
|
||||
import com.google.gerrit.server.account.ListGroupMembership;
|
||||
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.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.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;
|
||||
import org.eclipse.jgit.transport.RemoteConfig;
|
||||
import org.eclipse.jgit.transport.RemoteSession;
|
||||
import org.eclipse.jgit.transport.SshSessionFactory;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.eclipse.jgit.util.QuotedString;
|
||||
import org.eclipse.jgit.util.io.StreamCopyThread;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Manages automatic replication to remote repositories. */
|
||||
@Singleton
|
||||
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.
|
||||
//
|
||||
SshSessionFactory.setInstance(new JschConfigSessionFactory() {
|
||||
@Override
|
||||
protected void configure(OpenSshConfig.Host hc, Session session) {
|
||||
// Default configuration is batch mode.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final Injector injector;
|
||||
private final WorkQueue workQueue;
|
||||
private final List<ReplicationConfig> configs;
|
||||
private final SchemaFactory<ReviewDb> database;
|
||||
private final ReplicationUser.Factory replicationUserFactory;
|
||||
private final GitRepositoryManager gitRepositoryManager;
|
||||
private final GroupCache groupCache;
|
||||
|
||||
@Inject
|
||||
PushReplication(final Injector i, final WorkQueue wq, final SitePaths site,
|
||||
final ReplicationUser.Factory ruf, final SchemaFactory<ReviewDb> db,
|
||||
final GitRepositoryManager grm, GroupCache gc)
|
||||
throws ConfigInvalidException, IOException {
|
||||
injector = i;
|
||||
workQueue = wq;
|
||||
database = db;
|
||||
replicationUserFactory = ruf;
|
||||
gitRepositoryManager = grm;
|
||||
groupCache = gc;
|
||||
configs = allConfigs(site);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return configs.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleFullSync(final Project.NameKey project,
|
||||
final String urlMatch) {
|
||||
for (final ReplicationConfig cfg : configs) {
|
||||
for (final URIish uri : cfg.getURIs(project, urlMatch)) {
|
||||
cfg.schedule(project, PushOp.ALL_REFS, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleUpdate(final Project.NameKey project, final String ref) {
|
||||
for (final ReplicationConfig cfg : configs) {
|
||||
if (cfg.wouldPushRef(ref)) {
|
||||
for (final URIish uri : cfg.getURIs(project, null)) {
|
||||
cfg.schedule(project, ref, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String replace(final String pat, final String key,
|
||||
final String val) {
|
||||
final int n = pat.indexOf("${" + key + "}");
|
||||
|
||||
if (n != -1) {
|
||||
return pat.substring(0, n) + val + pat.substring(n + 3 + key.length());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<ReplicationConfig> allConfigs(final SitePaths site)
|
||||
throws ConfigInvalidException, IOException {
|
||||
final FileBasedConfig cfg =
|
||||
new FileBasedConfig(site.replication_config, FS.DETECTED);
|
||||
|
||||
if (!cfg.getFile().exists()) {
|
||||
log.warn("No " + cfg.getFile() + "; not replicating");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (cfg.getFile().length() == 0) {
|
||||
log.info("Empty " + cfg.getFile() + "; not replicating");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
cfg.load();
|
||||
} catch (ConfigInvalidException e) {
|
||||
throw new ConfigInvalidException("Config file " + cfg.getFile()
|
||||
+ " is invalid: " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Cannot read " + cfg.getFile() + ": "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
|
||||
final List<ReplicationConfig> r = new ArrayList<ReplicationConfig>();
|
||||
for (final RemoteConfig c : allRemotes(cfg)) {
|
||||
if (c.getURIs().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final URIish u : c.getURIs()) {
|
||||
if (u.getPath() == null || !u.getPath().contains("${name}")) {
|
||||
throw new ConfigInvalidException("remote." + c.getName() + ".url"
|
||||
+ " \"" + u + "\" lacks ${name} placeholder in " + cfg.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
// In case if refspec destination for push is not set then we assume it is
|
||||
// equal to source
|
||||
for (RefSpec ref : c.getPushRefSpecs()) {
|
||||
if (ref.getDestination() == null) {
|
||||
ref.setDestination(ref.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
if (c.getPushRefSpecs().isEmpty()) {
|
||||
RefSpec spec = new RefSpec();
|
||||
spec = spec.setSourceDestination("refs/*", "refs/*");
|
||||
spec = spec.setForceUpdate(true);
|
||||
c.addPushRefSpec(spec);
|
||||
}
|
||||
|
||||
r.add(new ReplicationConfig(injector, workQueue, c, cfg, database,
|
||||
replicationUserFactory, gitRepositoryManager, groupCache));
|
||||
}
|
||||
return Collections.unmodifiableList(r);
|
||||
}
|
||||
|
||||
private List<RemoteConfig> allRemotes(final FileBasedConfig cfg)
|
||||
throws ConfigInvalidException {
|
||||
List<String> names = new ArrayList<String>(cfg.getSubsections("remote"));
|
||||
Collections.sort(names);
|
||||
|
||||
final List<RemoteConfig> result = new ArrayList<RemoteConfig>(names.size());
|
||||
for (final String name : names) {
|
||||
try {
|
||||
result.add(new RemoteConfig(cfg, name));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new ConfigInvalidException("remote " + name
|
||||
+ " has invalid URL in " + cfg.getFile());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replicateNewProject(Project.NameKey projectName, String head) {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ReplicationConfig config : configs) {
|
||||
List<URIish> uriList = config.getURIs(projectName, "*");
|
||||
String[] adminUrls = config.getAdminUrls();
|
||||
boolean adminURLUsed = false;
|
||||
|
||||
for (String url : adminUrls) {
|
||||
URIish adminURI = null;
|
||||
try {
|
||||
if (url != null && !url.isEmpty()) {
|
||||
adminURI = new URIish(url);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("The URL '" + url + "' is invalid");
|
||||
}
|
||||
|
||||
if (adminURI != null) {
|
||||
final String replacedPath =
|
||||
replace(adminURI.getPath(), "name", projectName.get());
|
||||
if (replacedPath != null) {
|
||||
adminURI = adminURI.setPath(replacedPath);
|
||||
if (usingSSH(adminURI)) {
|
||||
replicateProject(adminURI, head);
|
||||
adminURLUsed = true;
|
||||
} else {
|
||||
log.error("The adminURL '" + url
|
||||
+ "' is non-SSH which is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!adminURLUsed) {
|
||||
for (URIish uri : uriList) {
|
||||
replicateProject(uri, head);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
OutputStream errStream = createErrStream();
|
||||
String cmd =
|
||||
"mkdir -p " + projectPath + "&& cd " + projectPath
|
||||
+ "&& git init --bare" + "&& git symbolic-ref HEAD "
|
||||
+ QuotedString.BOURNE.quote(head);
|
||||
|
||||
try {
|
||||
sshSession = sshFactory.getSession(replicateURI, null, FS.DETECTED, 0);
|
||||
Process proc = sshSession.exec(cmd, 0);
|
||||
proc.getOutputStream().close();
|
||||
StreamCopyThread out = new StreamCopyThread(proc.getInputStream(), errStream);
|
||||
StreamCopyThread err = new StreamCopyThread(proc.getErrorStream(), errStream);
|
||||
out.start();
|
||||
err.start();
|
||||
try {
|
||||
proc.waitFor();
|
||||
out.halt();
|
||||
err.halt();
|
||||
} catch (InterruptedException interrupted) {
|
||||
// Don't wait, drop out immediately.
|
||||
}
|
||||
sshSession.disconnect();
|
||||
} catch (IOException e) {
|
||||
log.error("Communication error when trying to replicate to: "
|
||||
+ replicateURI.toString() + "\n" + "Error reported: "
|
||||
+ e.getMessage() + "\n" + "Error in communication: "
|
||||
+ errStream.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private OutputStream createErrStream() {
|
||||
return new OutputStream() {
|
||||
private StringBuilder all = new StringBuilder();
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String r = all.toString();
|
||||
while (r.endsWith("\n"))
|
||||
r = r.substring(0, r.length() - 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(final int b) {
|
||||
if (b == '\r') {
|
||||
return;
|
||||
}
|
||||
|
||||
sb.append((char) b);
|
||||
|
||||
if (b == '\n') {
|
||||
all.append(sb);
|
||||
sb.setLength(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean usingSSH(final URIish uri) {
|
||||
final String scheme = uri.getScheme();
|
||||
if (!uri.isRemote()) return false;
|
||||
if (scheme != null && scheme.toLowerCase().contains("ssh")) return true;
|
||||
if (scheme == null && uri.getHost() != null && uri.getPath() != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static class ReplicationConfig {
|
||||
private final RemoteConfig remote;
|
||||
private final String[] adminUrls;
|
||||
private final int delay;
|
||||
private final int retryDelay;
|
||||
private final WorkQueue.Executor pool;
|
||||
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 GitRepositoryManager gitRepositoryManager,
|
||||
GroupCache groupCache) {
|
||||
|
||||
remote = rc;
|
||||
delay = Math.max(0, getInt(rc, cfg, "replicationdelay", 15));
|
||||
retryDelay = Math.max(0, getInt(rc, cfg, "replicationretry", 1));
|
||||
|
||||
final int poolSize = Math.max(0, getInt(rc, cfg, "threads", 1));
|
||||
final String poolName = "ReplicateTo-" + rc.getName();
|
||||
pool = workQueue.createQueue(poolSize, poolName);
|
||||
|
||||
String[] authGroupNames =
|
||||
cfg.getStringList("remote", rc.getName(), "authGroup");
|
||||
final GroupMembership authGroups;
|
||||
if (authGroupNames.length > 0) {
|
||||
ImmutableSet.Builder<AccountGroup.UUID> builder = ImmutableSet.builder();
|
||||
for (String name : authGroupNames) {
|
||||
AccountGroup g = groupCache.get(new AccountGroup.NameKey(name));
|
||||
if (g != null) {
|
||||
builder.add(g.getGroupUUID());
|
||||
} else {
|
||||
log.warn("Group \"{0}\" not in database, removing from authGroup", name);
|
||||
}
|
||||
}
|
||||
authGroups = new ListGroupMembership(builder.build());
|
||||
} 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);
|
||||
|
||||
projectControlFactory =
|
||||
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 FactoryModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(PushReplication.ReplicationConfig.class).toInstance(ReplicationConfig.this);
|
||||
bind(RemoteConfig.class).toInstance(remote);
|
||||
factory(PushOp.Factory.class);
|
||||
}
|
||||
}).getInstance(PushOp.Factory.class);
|
||||
}
|
||||
|
||||
private int getInt(final RemoteConfig rc, final Config cfg,
|
||||
final String name, final int defValue) {
|
||||
return cfg.getInt("remote", rc.getName(), name, defValue);
|
||||
}
|
||||
|
||||
void schedule(final Project.NameKey project, final String ref,
|
||||
final URIish uri) {
|
||||
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||
try {
|
||||
try {
|
||||
if (!controlFor(project).isVisible()) {
|
||||
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;
|
||||
} catch (IOException err) {
|
||||
log.error("Internal error: unable to open project " + project
|
||||
+ " 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) {
|
||||
e = opFactory.create(project, uri);
|
||||
pool.schedule(e, delay, TimeUnit.SECONDS);
|
||||
pending.put(uri, e);
|
||||
}
|
||||
e.addRef(ref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It schedules again a PushOp instance.
|
||||
* <p>
|
||||
* It is assumed to be previously scheduled and found a
|
||||
* transport exception. It will schedule it as a push
|
||||
* operation to be retried after the minutes count
|
||||
* determined by class attribute retryDelay.
|
||||
* <p>
|
||||
* In case the PushOp instance to be scheduled has same
|
||||
* URI than one also pending for retry, it adds to the one
|
||||
* pending the refs list of the parameter instance.
|
||||
* <p>
|
||||
* In case the PushOp instance to be scheduled has same
|
||||
* URI than one pending, but not pending for retry, it
|
||||
* indicates the one pending should be canceled when it
|
||||
* starts executing, removes it from pending list, and
|
||||
* adds its refs to the parameter instance. The parameter
|
||||
* instance is scheduled for retry.
|
||||
* <p>
|
||||
* Notice all operations to indicate a PushOp should be
|
||||
* canceled, or it is retrying, or remove/add it from/to
|
||||
* pending Map should be protected by the lock on pending
|
||||
* Map class instance attribute.
|
||||
*
|
||||
* @param pushOp The PushOp instance to be scheduled.
|
||||
*/
|
||||
void reschedule(final PushOp pushOp) {
|
||||
// It locks access to pending variable.
|
||||
synchronized (pending) {
|
||||
URIish uri = pushOp.getURI();
|
||||
PushOp pendingPushOp = pending.get(uri);
|
||||
|
||||
if (pendingPushOp != null) {
|
||||
// There is one PushOp instance already pending to same URI.
|
||||
|
||||
if (pendingPushOp.isRetrying()) {
|
||||
// The one pending is one already retrying, so it should
|
||||
// maintain it and add to it the refs of the one passed
|
||||
// as parameter to the method.
|
||||
|
||||
// This scenario would happen if a PushOp has started running
|
||||
// and then before it failed due transport exception, another
|
||||
// one to same URI started. The first one would fail and would
|
||||
// be rescheduled, being present in pending list. When the
|
||||
// second one fails, it will also be rescheduled and then,
|
||||
// here, find out replication to its URI is already pending
|
||||
// for retry (blocking).
|
||||
pendingPushOp.addRefs(pushOp.getRefs());
|
||||
|
||||
} else {
|
||||
// The one pending is one that is NOT retrying, it was just
|
||||
// scheduled believing no problem would happen. The one pending
|
||||
// should be canceled, and this is done by setting its canceled
|
||||
// flag, removing it from pending list, and adding its refs to
|
||||
// the pushOp instance that should then, later, in this method,
|
||||
// be scheduled for retry.
|
||||
|
||||
// Notice that the PushOp found pending will start running and,
|
||||
// when notifying it is starting (with pending lock protection),
|
||||
// it will see it was canceled and then it will do nothing with
|
||||
// pending list and it will not execute its run implementation.
|
||||
|
||||
pendingPushOp.cancel();
|
||||
pending.remove(uri);
|
||||
|
||||
pushOp.addRefs(pendingPushOp.getRefs());
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingPushOp == null || !pendingPushOp.isRetrying()) {
|
||||
// The PushOp method param instance should be scheduled for retry.
|
||||
// Remember when retrying it should be used different delay.
|
||||
|
||||
pushOp.setToRetry();
|
||||
|
||||
pending.put(uri, pushOp);
|
||||
pool.schedule(pushOp, retryDelay, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProjectControl controlFor(final Project.NameKey project)
|
||||
throws NoSuchProjectException {
|
||||
return projectControlFactory.controlFor(project);
|
||||
}
|
||||
|
||||
void notifyStarting(final PushOp op) {
|
||||
synchronized (pending) {
|
||||
if (!op.wasCanceled()) {
|
||||
pending.remove(op.getURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
if (matches(uri, urlMatch)) {
|
||||
String name = project.get();
|
||||
if (needsUrlEncoding(uri)) {
|
||||
name = encode(name);
|
||||
}
|
||||
String replacedPath = replace(uri.getPath(), "name", name);
|
||||
if (replacedPath != null) {
|
||||
uri = uri.setPath(replacedPath);
|
||||
r.add(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static boolean needsUrlEncoding(URIish uri) {
|
||||
return "http".equalsIgnoreCase(uri.getScheme())
|
||||
|| "https".equalsIgnoreCase(uri.getScheme())
|
||||
|| "amazon-s3".equalsIgnoreCase(uri.getScheme());
|
||||
}
|
||||
|
||||
static String encode(String str) {
|
||||
try {
|
||||
// Some cleanup is required. The '/' character is always encoded as %2F
|
||||
// however remote servers will expect it to be not encoded as part of the
|
||||
// path used to the repository. Space is incorrectly encoded as '+' for this
|
||||
// context. In the path part of a URI space should be %20, but in form data
|
||||
// space is '+'. Our cleanup replace fixes these two issues.
|
||||
return URLEncoder.encode(str, "UTF-8")
|
||||
.replaceAll("%2[fF]", "/")
|
||||
.replace("+", "%20");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String[] getAdminUrls() {
|
||||
return this.adminUrls;
|
||||
}
|
||||
|
||||
private boolean matches(URIish uri, final String urlMatch) {
|
||||
if (urlMatch == null || urlMatch.equals("") || urlMatch.equals("*")) {
|
||||
return true;
|
||||
}
|
||||
return uri.toString().contains(urlMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountResolver;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
|
||||
import com.google.gerrit.server.mail.CreateChangeSender;
|
||||
import com.google.gerrit.server.mail.MergedSender;
|
||||
@@ -205,7 +206,7 @@ public class ReceiveCommits {
|
||||
private final CreateChangeSender.Factory createChangeSenderFactory;
|
||||
private final MergedSender.Factory mergedSenderFactory;
|
||||
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated replication;
|
||||
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||
private final ChangeHooks hooks;
|
||||
private final ApprovalsUtil approvalsUtil;
|
||||
@@ -254,7 +255,7 @@ public class ReceiveCommits {
|
||||
final CreateChangeSender.Factory createChangeSenderFactory,
|
||||
final MergedSender.Factory mergedSenderFactory,
|
||||
final ReplacePatchSetSender.Factory replacePatchSetFactory,
|
||||
final ReplicationQueue replication,
|
||||
final GitReferenceUpdated replication,
|
||||
final PatchSetInfoFactory patchSetInfoFactory,
|
||||
final ChangeHooks hooks,
|
||||
final ApprovalsUtil approvalsUtil,
|
||||
@@ -509,7 +510,7 @@ public class ReceiveCommits {
|
||||
// We only schedule direct refs updates for replication.
|
||||
// Change refs are scheduled when they are created.
|
||||
//
|
||||
replication.scheduleUpdate(project.getNameKey(), c.getRefName());
|
||||
replication.fire(project.getNameKey(), c.getRefName());
|
||||
Branch.NameKey destBranch = new Branch.NameKey(project.getNameKey(), c.getRefName());
|
||||
hooks.doRefUpdatedHook(destBranch, c.getOldId(), c.getNewId(), currentUser.getAccount());
|
||||
commandProgress.update(1);
|
||||
@@ -1155,7 +1156,7 @@ public class ReceiveCommits {
|
||||
throw new IOException("Failed to create ref " + ps.getRefName() + " in "
|
||||
+ repo.getDirectory() + ": " + ru.getResult());
|
||||
}
|
||||
replication.scheduleUpdate(project.getNameKey(), ru.getName());
|
||||
replication.fire(project.getNameKey(), ru.getName());
|
||||
|
||||
allNewChanges.add(change);
|
||||
|
||||
@@ -1457,7 +1458,7 @@ public class ReceiveCommits {
|
||||
throw new IOException("Failed to create ref " + ps.getRefName() + " in "
|
||||
+ repo.getDirectory() + ": " + ru.getResult());
|
||||
}
|
||||
replication.scheduleUpdate(project.getNameKey(), ru.getName());
|
||||
replication.fire(project.getNameKey(), ru.getName());
|
||||
hooks.doPatchsetCreatedHook(result.change, ps, db);
|
||||
request.cmd.setResult(ReceiveCommand.Result.OK);
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (C) 2009 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
||||
/** Manages replication to other nodes. */
|
||||
public interface ReplicationQueue {
|
||||
/** Is replication to one or more other destinations configured? */
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Schedule a full replication for a single project.
|
||||
* <p>
|
||||
* All remote URLs are checked to verify the are current with regards to the
|
||||
* local project state. If not, they are updated by pushing new refs, updating
|
||||
* existing ones which don't match, and deleting stale refs which have been
|
||||
* removed from the local repository.
|
||||
*
|
||||
* @param project identity of the project to replicate.
|
||||
* @param urlMatch substring that must appear in a URI to support replication.
|
||||
*/
|
||||
void scheduleFullSync(Project.NameKey project, String urlMatch);
|
||||
|
||||
/**
|
||||
* Schedule update of a single ref.
|
||||
* <p>
|
||||
* This method automatically tries to batch together multiple requests in the
|
||||
* same project, to take advantage of Git's native ability to update multiple
|
||||
* refs during a single push operation.
|
||||
*
|
||||
* @param project identity of the project to replicate.
|
||||
* @param ref unique name of the ref; must start with {@code refs/}.
|
||||
*/
|
||||
void scheduleUpdate(Project.NameKey project, String ref);
|
||||
|
||||
/**
|
||||
* Create new empty project at the remote sites.
|
||||
* <p>
|
||||
* When a new project has been created locally call this method to make sure
|
||||
* that the project will be created at the remote sites as well.
|
||||
*
|
||||
* @param project of the project to be created.
|
||||
* @param head name HEAD should point at (must be {@code refs/heads/...}).
|
||||
*/
|
||||
void replicateNewProject(Project.NameKey project, String head);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (C) 2011 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.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.transport.CredentialItem;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
|
||||
/** Looks up a remote's password in secure.config. */
|
||||
public class SecureCredentialsProvider extends CredentialsProvider {
|
||||
public interface Factory {
|
||||
SecureCredentialsProvider create(String remoteName);
|
||||
}
|
||||
|
||||
private final String cfgUser;
|
||||
private final String cfgPass;
|
||||
|
||||
@Inject
|
||||
SecureCredentialsProvider(@GerritServerConfig Config cfg,
|
||||
@Assisted String remoteName) {
|
||||
cfgUser = cfg.getString("remote", remoteName, "username");
|
||||
cfgPass = cfg.getString("remote", remoteName, "password");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInteractive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(CredentialItem... items) {
|
||||
for (CredentialItem i : items) {
|
||||
if (i instanceof CredentialItem.Username) {
|
||||
continue;
|
||||
} else if (i instanceof CredentialItem.Password) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean get(URIish uri, CredentialItem... items)
|
||||
throws UnsupportedCredentialItem {
|
||||
String username = uri.getUser();
|
||||
if (username == null) {
|
||||
username = cfgUser;
|
||||
}
|
||||
if (username == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String password = uri.getPass();
|
||||
if (password == null) {
|
||||
password = cfgPass;
|
||||
}
|
||||
if (password == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CredentialItem i : items) {
|
||||
if (i instanceof CredentialItem.Username) {
|
||||
((CredentialItem.Username) i).setValue(username);
|
||||
} else if (i instanceof CredentialItem.Password) {
|
||||
((CredentialItem.Password) i).setValue(password.toCharArray());
|
||||
} else {
|
||||
throw new UnsupportedCredentialItem(uri, i.getPromptText());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.util.SubmoduleSectionParser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
@@ -84,7 +85,7 @@ public class SubmoduleOp {
|
||||
private final Map<Change.Id, CodeReviewCommit> commits;
|
||||
private final PersonIdent myIdent;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated replication;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final Set<Branch.NameKey> updatedSubscribers;
|
||||
|
||||
@@ -96,7 +97,7 @@ public class SubmoduleOp {
|
||||
@Assisted Project destProject, @Assisted List<Change> submitted,
|
||||
@Assisted final Map<Change.Id, CodeReviewCommit> commits,
|
||||
@GerritPersonIdent final PersonIdent myIdent,
|
||||
GitRepositoryManager repoManager, ReplicationQueue replication) {
|
||||
GitRepositoryManager repoManager, GitReferenceUpdated replication) {
|
||||
this.destBranch = destBranch;
|
||||
this.mergeTip = mergeTip;
|
||||
this.rw = rw;
|
||||
@@ -331,7 +332,7 @@ public class SubmoduleOp {
|
||||
switch (rfu.update()) {
|
||||
case NEW:
|
||||
case FAST_FORWARD:
|
||||
replication.scheduleUpdate(subscriber.getParentKey(), rfu.getName());
|
||||
replication.fire(subscriber.getParentKey(), rfu.getName());
|
||||
// TODO since this is performed "in the background" no mail will be
|
||||
// sent to inform users about the updated branch
|
||||
break;
|
||||
|
||||
@@ -172,6 +172,10 @@ public class WorkQueue {
|
||||
);
|
||||
}
|
||||
|
||||
public void unregisterWorkQueue() {
|
||||
queues.remove(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <V> RunnableScheduledFuture<V> decorateTask(
|
||||
final Runnable runnable, RunnableScheduledFuture<V> r) {
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.google.gerrit.common.data.GroupReference;
|
||||
import com.google.gerrit.common.data.Permission;
|
||||
import com.google.gerrit.common.data.PermissionRule;
|
||||
import com.google.gerrit.common.errors.ProjectCreationFailedException;
|
||||
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
@@ -26,10 +28,10 @@ import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.config.ProjectOwnerGroups;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.git.ReplicationQueue;
|
||||
import com.google.gerrit.server.git.RepositoryCaseMismatchException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
@@ -64,7 +66,8 @@ public class CreateProject {
|
||||
private final Set<AccountGroup.UUID> projectOwnerGroups;
|
||||
private final IdentifiedUser currentUser;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final ReplicationQueue replication;
|
||||
private final GitReferenceUpdated referenceUpdated;
|
||||
private final DynamicSet<NewProjectCreatedListener> createdListener;
|
||||
private final PersonIdent serverIdent;
|
||||
private CreateProjectArgs createProjectArgs;
|
||||
private ProjectCache projectCache;
|
||||
@@ -74,14 +77,17 @@ public class CreateProject {
|
||||
@Inject
|
||||
CreateProject(@ProjectOwnerGroups Set<AccountGroup.UUID> pOwnerGroups,
|
||||
IdentifiedUser identifiedUser, GitRepositoryManager gitRepoManager,
|
||||
ReplicationQueue replicateq, ReviewDb db,
|
||||
GitReferenceUpdated referenceUpdated,
|
||||
DynamicSet<NewProjectCreatedListener> createdListener,
|
||||
ReviewDb db,
|
||||
@GerritPersonIdent PersonIdent personIdent, final GroupCache groupCache,
|
||||
final MetaDataUpdate.User metaDataUpdateFactory,
|
||||
@Assisted CreateProjectArgs createPArgs, ProjectCache pCache) {
|
||||
this.projectOwnerGroups = pOwnerGroups;
|
||||
this.currentUser = identifiedUser;
|
||||
this.repoManager = gitRepoManager;
|
||||
this.replication = replicateq;
|
||||
this.referenceUpdated = referenceUpdated;
|
||||
this.createdListener = createdListener;
|
||||
this.serverIdent = personIdent;
|
||||
this.createProjectArgs = createPArgs;
|
||||
this.projectCache = pCache;
|
||||
@@ -98,7 +104,20 @@ public class CreateProject {
|
||||
: createProjectArgs.branch;
|
||||
final Repository repo = repoManager.createRepository(nameKey);
|
||||
try {
|
||||
replication.replicateNewProject(nameKey, head);
|
||||
NewProjectCreatedListener.Event event = new NewProjectCreatedListener.Event() {
|
||||
@Override
|
||||
public String getProjectName() {
|
||||
return nameKey.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeadName() {
|
||||
return head;
|
||||
}
|
||||
};
|
||||
for (NewProjectCreatedListener l : createdListener) {
|
||||
l.onNewProjectCreated(event);
|
||||
}
|
||||
|
||||
final RefUpdate u = repo.updateRef(Constants.HEAD);
|
||||
u.disableRefLog();
|
||||
@@ -186,7 +205,7 @@ public class CreateProject {
|
||||
projectCache.onCreateProject(createProjectArgs.getProject());
|
||||
repoManager.setProjectDescription(createProjectArgs.getProject(),
|
||||
createProjectArgs.projectDescription);
|
||||
replication.scheduleUpdate(createProjectArgs.getProject(),
|
||||
referenceUpdated.fire(createProjectArgs.getProject(),
|
||||
GitRepositoryManager.REF_CONFIG);
|
||||
}
|
||||
|
||||
@@ -246,7 +265,7 @@ public class CreateProject {
|
||||
final Result result = ru.update();
|
||||
switch (result) {
|
||||
case NEW:
|
||||
replication.scheduleUpdate(project, ref);
|
||||
referenceUpdated.fire(project, ref);
|
||||
break;
|
||||
default: {
|
||||
throw new IOException(result.name());
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.ReplicationUser;
|
||||
import com.google.gerrit.server.InternalUser;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.GitReceivePackGroups;
|
||||
import com.google.gerrit.server.config.GitUploadPackGroups;
|
||||
@@ -185,7 +185,7 @@ public class ProjectControl {
|
||||
|
||||
/** Can this user see this project exists? */
|
||||
public boolean isVisible() {
|
||||
return (visibleForReplication()
|
||||
return (user instanceof InternalUser
|
||||
|| canPerformOnAnyRef(Permission.READ)) && !isHidden();
|
||||
}
|
||||
|
||||
@@ -196,16 +196,10 @@ public class ProjectControl {
|
||||
|
||||
/** Can this user see all the refs in this projects? */
|
||||
public boolean allRefsAreVisible() {
|
||||
return visibleForReplication()
|
||||
return user instanceof InternalUser
|
||||
|| canPerformOnAllRefs(Permission.READ);
|
||||
}
|
||||
|
||||
/** Is this project completely visible for replication? */
|
||||
boolean visibleForReplication() {
|
||||
return user instanceof ReplicationUser
|
||||
&& ((ReplicationUser) user).isEverythingVisible();
|
||||
}
|
||||
|
||||
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
|
||||
public boolean isOwner() {
|
||||
return isDeclaredOwner()
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.gerrit.common.errors.InvalidNameException;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.InternalUser;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
|
||||
import dk.brics.automaton.RegExp;
|
||||
@@ -101,7 +102,7 @@ public class RefControl {
|
||||
|
||||
/** Can this user see this reference exists? */
|
||||
public boolean isVisible() {
|
||||
return (projectControl.visibleForReplication() || canPerform(Permission.READ))
|
||||
return (getCurrentUser() instanceof InternalUser || canPerform(Permission.READ))
|
||||
&& canRead();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@ import com.google.gerrit.server.account.GroupUUID;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.NoReplication;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gwtorm.jdbc.JdbcExecutor;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
@@ -219,7 +219,8 @@ public class SchemaCreator {
|
||||
}
|
||||
}
|
||||
try {
|
||||
MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), allProjectsName, git);
|
||||
MetaDataUpdate md =
|
||||
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git);
|
||||
md.getCommitBuilder().setAuthor(serverUser);
|
||||
md.getCommitBuilder().setCommitter(serverUser);
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ import com.google.gerrit.reviewdb.client.SystemConfig;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.GroupUUID;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.NoReplication;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
@@ -166,7 +166,7 @@ class Schema_53 extends SchemaVersion {
|
||||
}
|
||||
try {
|
||||
MetaDataUpdate md =
|
||||
new MetaDataUpdate(new NoReplication(), nameKey, git);
|
||||
new MetaDataUpdate(GitReferenceUpdated.DISABLED, nameKey, git);
|
||||
md.getCommitBuilder().setAuthor(serverUser);
|
||||
md.getCommitBuilder().setCommitter(serverUser);
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.config.AllProjectsNameProvider;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.NoReplication;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -81,7 +81,8 @@ public class Schema_57 extends SchemaVersion {
|
||||
try {
|
||||
Repository git = mgr.openRepository(allProjects);
|
||||
try {
|
||||
MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), allProjects, git);
|
||||
MetaDataUpdate md =
|
||||
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjects, git);
|
||||
md.getCommitBuilder().setAuthor(serverUser);
|
||||
md.getCommitBuilder().setCommitter(serverUser);
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.NoReplication;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
@@ -92,7 +92,7 @@ public class Schema_64 extends SchemaVersion {
|
||||
}
|
||||
try {
|
||||
MetaDataUpdate md =
|
||||
new MetaDataUpdate(new NoReplication(), allProjects, git);
|
||||
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjects, git);
|
||||
md.getCommitBuilder().setAuthor(serverUser);
|
||||
md.getCommitBuilder().setCommitter(serverUser);
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.GroupUUID;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.NoReplication;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
@@ -92,7 +92,7 @@ public class Schema_65 extends SchemaVersion {
|
||||
}
|
||||
try {
|
||||
MetaDataUpdate md =
|
||||
new MetaDataUpdate(new NoReplication(), allProjects, git);
|
||||
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjects, git);
|
||||
ProjectConfig config = ProjectConfig.read(md);
|
||||
Map<Integer, ContributorAgreement> agreements = getAgreementToAdd(db, config);
|
||||
if (agreements.isEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user