Use stream events to delete repositories This functionality in combination with events-broker and multi-site provides a backfill mechanism for REST API calls missed when the node was unreachable. Co-Authored-By: Marcin Czech <maczech@gmail.com> Bug: Issue 40014693 Change-Id: I31cb7378ba7aa5a5532ba7007fc5780db2723908
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java index 6a21104..18574fd 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectDeletionAction.java
@@ -39,12 +39,12 @@ import org.eclipse.jgit.transport.URIish; @Singleton -class ProjectDeletionAction +public class ProjectDeletionAction implements RestModifyView<ProjectResource, ProjectDeletionAction.DeleteInput> { private static final PluginPermission DELETE_PROJECT = new PluginPermission("delete-project", "deleteProject"); - static class DeleteInput {} + public static class DeleteInput {} private final Provider<CurrentUser> userProvider; private final GerritConfigOps gerritConfigOps;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListener.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListener.java index f863148..4c9793c 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListener.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListener.java
@@ -25,7 +25,9 @@ import com.google.gerrit.entities.Project.NameKey; import com.google.gerrit.entities.RefNames; import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.server.config.GerritInstanceId; import com.google.gerrit.server.data.RefUpdateAttribute; @@ -37,9 +39,12 @@ import com.google.gerrit.server.events.RefUpdatedEvent; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.restapi.project.ProjectsCollection; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; +import com.googlesource.gerrit.plugins.deleteproject.ProjectDeletedEvent; import com.googlesource.gerrit.plugins.replication.pull.ApplyObjectsCacheKey; import com.googlesource.gerrit.plugins.replication.pull.FetchOne; import com.googlesource.gerrit.plugins.replication.pull.Source; @@ -47,6 +52,7 @@ import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction; import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob; import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob.Factory; +import com.googlesource.gerrit.plugins.replication.pull.api.ProjectDeletionAction; import com.googlesource.gerrit.plugins.replication.pull.api.ProjectInitializationAction; import com.googlesource.gerrit.plugins.replication.pull.api.PullReplicationApiRequestMetrics; import com.googlesource.gerrit.plugins.replication.pull.api.UpdateHeadCommand; @@ -68,6 +74,8 @@ private final String instanceId; private final WorkQueue workQueue; private final Cache<ApplyObjectsCacheKey, Long> refUpdatesSucceededCache; + private final ProjectDeletionAction projectDeletionAction; + private final ProjectsCollection projectsCollection; @Inject public StreamEventListener( @@ -79,7 +87,9 @@ Provider<PullReplicationApiRequestMetrics> metricsProvider, SourcesCollection sources, ExcludedRefsFilter excludedRefsFilter, - @Named(APPLY_OBJECTS_CACHE) Cache<ApplyObjectsCacheKey, Long> refUpdatesSucceededCache) { + @Named(APPLY_OBJECTS_CACHE) Cache<ApplyObjectsCacheKey, Long> refUpdatesSucceededCache, + ProjectDeletionAction projectDeletionAction, + ProjectsCollection projectsCollection) { this.instanceId = instanceId; this.updateHeadCommand = updateHeadCommand; this.projectInitializationAction = projectInitializationAction; @@ -89,6 +99,8 @@ this.sources = sources; this.refsFilter = excludedRefsFilter; this.refUpdatesSucceededCache = refUpdatesSucceededCache; + this.projectDeletionAction = projectDeletionAction; + this.projectsCollection = projectsCollection; requireNonNull( Strings.emptyToNull(this.instanceId), "gerrit.instanceId cannot be null or empty"); @@ -173,6 +185,25 @@ "Failed to update HEAD on project: %s", headUpdatedEvent.projectName); throw e; } + } else if (event instanceof ProjectDeletedEvent) { + deleteProject((ProjectDeletedEvent) event); + } + } + + protected void deleteProject(ProjectEvent projectDeletedEvent) { + try { + ProjectResource projectResource = + projectsCollection.parse( + TopLevelResource.INSTANCE, + IdString.fromDecoded(projectDeletedEvent.getProjectNameKey().get())); + projectDeletionAction.apply(projectResource, new ProjectDeletionAction.DeleteInput()); + } catch (ResourceNotFoundException e) { + logger.atFine().withCause(e).log( + "Repository not found whilst trying to delete project:%s", + projectDeletedEvent.getProjectNameKey().get()); + } catch (Exception e) { + logger.atSevere().withCause(e).log( + "Cannot delete project:%s", projectDeletedEvent.getProjectNameKey().get()); } } @@ -202,6 +233,12 @@ && source.wouldCreateProject(projectCreatedEvent.getProjectNameKey()); } + if (event instanceof ProjectDeletedEvent) { + ProjectDeletedEvent projectDeletedEvent = (ProjectDeletedEvent) event; + + return source.wouldDeleteProject(projectDeletedEvent.getProjectNameKey()); + } + ProjectEvent projectEvent = (ProjectEvent) event; return source.wouldFetchProject(projectEvent.getProjectNameKey()); }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListenerTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListenerTest.java index a60fed6..980c509 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListenerTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/event/StreamEventListenerTest.java
@@ -31,12 +31,14 @@ import com.google.gerrit.server.events.ProjectHeadUpdatedEvent; import com.google.gerrit.server.events.RefUpdatedEvent; import com.google.gerrit.server.git.WorkQueue; +import com.google.gerrit.server.restapi.project.ProjectsCollection; import com.googlesource.gerrit.plugins.replication.pull.ApplyObjectsCacheKey; import com.googlesource.gerrit.plugins.replication.pull.FetchOne; import com.googlesource.gerrit.plugins.replication.pull.Source; import com.googlesource.gerrit.plugins.replication.pull.SourcesCollection; import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction; import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob; +import com.googlesource.gerrit.plugins.replication.pull.api.ProjectDeletionAction; import com.googlesource.gerrit.plugins.replication.pull.api.ProjectInitializationAction; import com.googlesource.gerrit.plugins.replication.pull.api.PullReplicationApiRequestMetrics; import com.googlesource.gerrit.plugins.replication.pull.api.UpdateHeadCommand; @@ -72,6 +74,9 @@ @Mock private SourcesCollection sources; @Mock private Source source; @Mock private ExcludedRefsFilter refsFilter; + @Mock private ProjectDeletionAction projectDeletionAction; + @Mock private ProjectsCollection projectsCollection; + private Cache<ApplyObjectsCacheKey, Long> cache; private StreamEventListener objectUnderTest; @@ -98,7 +103,9 @@ () -> metrics, sources, refsFilter, - cache); + cache, + projectDeletionAction, + projectsCollection); } @Test