Notify listeners when a new project is created via replication In a complex multi-site with high-availability setup, the plugins that are reacting on the creation of a project were not able to detect the ones created via replication, which lead to unpredictable consequences. To fix this, when a project is created via replication, notify all `NewProjectCreatedListener`s of the operation that took place in the local node. Bug: Issue 40015518 Change-Id: I1b22bf1fb0c7654d4ad2493eb0a5a0e568500e53
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationAction.java index 9afae71..155b6a1 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationAction.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationAction.java
@@ -27,12 +27,15 @@ import com.google.common.net.MediaType; import com.google.gerrit.entities.Project; import com.google.gerrit.entities.RefNames; +import com.google.gerrit.extensions.events.NewProjectCreatedListener; +import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.index.project.ProjectIndexer; import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent; import com.google.gerrit.server.permissions.GlobalPermission; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; @@ -69,6 +72,7 @@ private final ProjectIndexer projectIndexer; private final ApplyObjectCommand applyObjectCommand; private final ProjectCache projectCache; + private final DynamicSet<NewProjectCreatedListener> newProjectCreatedListeners; @Inject ProjectInitializationAction( @@ -77,13 +81,15 @@ PermissionBackend permissionBackend, ProjectIndexer projectIndexer, ApplyObjectCommand applyObjectCommand, - ProjectCache projectCache) { + ProjectCache projectCache, + DynamicSet<NewProjectCreatedListener> newProjectCreatedListeners) { this.gerritConfigOps = gerritConfigOps; this.userProvider = userProvider; this.permissionBackend = permissionBackend; this.projectIndexer = projectIndexer; this.applyObjectCommand = applyObjectCommand; this.projectCache = projectCache; + this.newProjectCreatedListeners = newProjectCreatedListeners; } @Override @@ -167,6 +173,10 @@ input.getLabel(), input.getEventCreatedOn()); projectCache.onCreateProject(Project.nameKey(projectName)); + // In case pull-replication is used in conjunction with multi-site, by convention the remote + // label is populated with the instanceId. That's why we are passing input.getLabel() + // to the Event to notify + notifyListenersOfNewProjectCreation(projectName, input.getLabel()); repLog.info( "Init project API from {} for {}:{} - {}", input.getLabel(), @@ -234,4 +244,38 @@ return false; } } + + private void notifyListenersOfNewProjectCreation(String projectName, String instanceId) { + NewProjectCreatedListener.Event newProjectCreatedEvent = + new Event(RefNames.REFS_CONFIG, projectName, instanceId); + newProjectCreatedListeners.forEach(l -> l.onNewProjectCreated(newProjectCreatedEvent)); + } + + private static class Event extends AbstractNoNotifyEvent + implements NewProjectCreatedListener.Event { + private final String headName; + private final String projectName; + private final String instanceId; + + public Event(String headName, String projectName, String maybeInstanceId) { + this.headName = headName; + this.projectName = projectName; + this.instanceId = maybeInstanceId; + } + + @Override + public String getHeadName() { + return headName; + } + + @Override + public String getProjectName() { + return projectName; + } + + @Override + public String getInstanceId() { + return instanceId; + } + } }