List<URIish> getURIs(Project.NameKey project, String urlMatch) { List<URIish> r = Lists.newArrayListWithCapacity(config.getRemoteConfig().getURIs().size()); for (URIish uri : config.getRemoteConfig().getURIs()) { if (matches(uri, urlMatch)) { String name = project.get(); if (needsUrlEncoding(uri)) { name = encode(name); } String remoteNameStyle = config.getRemoteNameStyle(); if (remoteNameStyle.equals("dash")) { name = name.replace("/", "-"); } else if (remoteNameStyle.equals("underscore")) { name = name.replace("/", "_"); } else if (remoteNameStyle.equals("basenameOnly")) { name = FilenameUtils.getBaseName(name); } else if (!remoteNameStyle.equals("slash")) { repLog.debug( String.format("Unknown remoteNameStyle: %s, falling back to slash", remoteNameStyle)); } String replacedPath = ReplicationQueue.replaceName(uri.getPath(), name, isSingleProjectMatch()); if (replacedPath != null) { uri = uri.setPath(replacedPath); r.add(uri); } } } return r; }
boolean wouldPushRef(String ref) { if (!config.replicatePermissions() && RefNames.REFS_CONFIG.equals(ref)) { return false; } for (RefSpec s : config.getRemoteConfig().getPushRefSpecs()) { if (s.matchSource(ref)) { return true; } } return false; }
void schedule(Project.NameKey project, String ref, URIish uri, ReplicationState state) { repLog.info("scheduling replication {}:{} => {}", project, ref, uri); if (!shouldReplicate(project, ref, state)) { return; } if (!config.replicatePermissions()) { PushOne e; synchronized (stateLock) { e = pending.get(uri); } if (e == null) { try (Repository git = gitManager.openRepository(project)) { try { Ref head = git.exactRef(Constants.HEAD); if (head != null && head.isSymbolic() && RefNames.REFS_CONFIG.equals(head.getLeaf().getName())) { return; } } catch (IOException err) { stateLog.error(String.format("cannot check type of project %s", project), err, state); return; } } catch (IOException err) { stateLog.error(String.format("source project %s not available", project), err, state); return; } } } synchronized (stateLock) { PushOne e = pending.get(uri); if (e == null) { e = opFactory.create(project, uri); pool.schedule(e, config.getDelay(), TimeUnit.SECONDS); pending.put(uri, e); } e.addRef(ref); state.increasePushTaskCount(project.get(), ref); e.addState(ref, state); repLog.info("scheduled {}:{} => {} to run after {}s", project, ref, e, config.getDelay()); } }
boolean wouldPushProject(Project.NameKey project) { if (!shouldReplicate(project)) { return false; } // by default push all projects List<String> projects = config.getProjects(); if (projects.isEmpty()) { return true; } return (new ReplicationFilter(projects)).matches(project); }
boolean isSingleProjectMatch() { List<String> projects = config.getProjects(); boolean ret = (projects.size() == 1); if (ret) { String projectMatch = projects.get(0); if (ReplicationFilter.getPatternType(projectMatch) != ReplicationFilter.PatternType.EXACT_MATCH) { // projectMatch is either regular expression, or wild-card. // // Even though they might refer to a single project now, they need not // after new projects have been created. Hence, we do not treat them as // matching a single project. ret = false; } } return ret; }
int getLockErrorMaxRetries() { return config.getLockErrorMaxRetries(); }
ImmutableList<String> getProjects() { return config.getProjects(); }
ImmutableList<String> getAuthGroupNames() { return config.getAuthGroupNames(); }
ImmutableList<String> getUrls() { return config.getUrls(); }
boolean isReplicateProjectDeletions() { return config.replicateProjectDeletions(); }
private boolean shouldReplicate(ProjectControl projectControl) { return projectControl.isReadable() && (!projectControl.isHidden() || config.replicateHiddenProjects()); }
boolean isCreateMissingRepos() { return config.createMissingRepos(); }
/** * It schedules again a PushOp instance. * * <p>If the reason for rescheduling is to avoid a collision with an in-flight push to the same * URI, we don't mark the operation as "retrying," and we schedule using the replication delay, * rather than the retry delay. Otherwise, the operation is marked as "retrying" and scheduled to * run following the minutes count determined by class attribute retryDelay. * * <p>In case the PushOp instance to be scheduled has same URI than one marked as "retrying," it * adds to the one pending the refs list of the parameter instance. * * <p>In case the PushOp instance to be scheduled has the same URI as one pending, but not marked * "retrying," 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 synchronizing on the stateLock object. * * @param pushOp The PushOp instance to be scheduled. */ void reschedule(PushOne pushOp, RetryReason reason) { synchronized (stateLock) { URIish uri = pushOp.getURI(); PushOne 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()); pendingPushOp.addStates(pushOp.getStates()); pushOp.removeStates(); } 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.canceledByReplication(); pending.remove(uri); pushOp.addRefs(pendingPushOp.getRefs()); pushOp.addStates(pendingPushOp.getStates()); pendingPushOp.removeStates(); } } if (pendingPushOp == null || !pendingPushOp.isRetrying()) { pending.put(uri, pushOp); switch (reason) { case COLLISION: pool.schedule(pushOp, config.getDelay(), TimeUnit.SECONDS); break; case TRANSPORT_ERROR: case REPOSITORY_MISSING: default: pushOp.setToRetry(); pool.schedule(pushOp, config.getRetryDelay(), TimeUnit.MINUTES); break; } } } }
String getRemoteConfigName() { return config.getRemoteConfig().getName(); }
boolean isReplicatePermissions() { return config.replicatePermissions(); }
protected Destination( Injector injector, DestinationConfiguration cfg, RemoteSiteUser.Factory replicationUserFactory, PluginUser pluginUser, GitRepositoryManager gitRepositoryManager, GroupBackend groupBackend, ReplicationStateListener stateLog, GroupIncludeCache groupIncludeCache) { config = cfg; gitManager = gitRepositoryManager; this.stateLog = stateLog; final CurrentUser remoteUser; if (!cfg.getAuthGroupNames().isEmpty()) { ImmutableSet.Builder<AccountGroup.UUID> builder = ImmutableSet.builder(); for (String name : cfg.getAuthGroupNames()) { GroupReference g = GroupBackends.findExactSuggestion(groupBackend, name); if (g != null) { builder.add(g.getUUID()); addRecursiveParents(g.getUUID(), builder, groupIncludeCache); } else { repLog.warn(String.format("Group \"%s\" not recognized, removing from authGroup", name)); } } remoteUser = replicationUserFactory.create(new ListGroupMembership(builder.build())); } else { remoteUser = pluginUser; } Injector child = injector.createChildInjector( new FactoryModule() { @Override protected void configure() { bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST); bind(PerThreadRequestScope.Propagator.class); bind(PerRequestProjectControlCache.class).in(RequestScoped.class); bind(Destination.class).toInstance(Destination.this); bind(RemoteConfig.class).toInstance(config.getRemoteConfig()); install(new FactoryModuleBuilder().build(PushOne.Factory.class)); } @Provides public PerThreadRequestScope.Scoper provideScoper( final PerThreadRequestScope.Propagator propagator, final Provider<RequestScopedReviewDbProvider> dbProvider) { final RequestContext requestContext = new RequestContext() { @Override public CurrentUser getUser() { return remoteUser; } @Override public Provider<ReviewDb> getReviewDbProvider() { return dbProvider.get(); } }; return new PerThreadRequestScope.Scoper() { @Override public <T> Callable<T> scope(Callable<T> callable) { return propagator.scope(requestContext, callable); } }; } }); projectControlFactory = child.getInstance(ProjectControl.Factory.class); opFactory = child.getInstance(PushOne.Factory.class); threadScoper = child.getInstance(PerThreadRequestScope.Scoper.class); }
public void start(WorkQueue workQueue) { String poolName = "ReplicateTo-" + config.getRemoteConfig().getName(); pool = workQueue.createQueue(config.getPoolThreads(), poolName); }