Update project HEAD from stream event Handle `ProjectHeadUpdatedEvent` and update project's `HEAD` reference on the replica. Bug: Issue 40014694 Change-Id: Ib9a83442c390a493eb839d9edb115148d6f07438
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/EventsBrokerMessageConsumer.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/EventsBrokerMessageConsumer.java index a9b4945..cf7abba 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/EventsBrokerMessageConsumer.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/event/EventsBrokerMessageConsumer.java
@@ -23,6 +23,8 @@ import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.inject.Inject; @@ -58,7 +60,11 @@ try { eventListener.fetchRefsForEvent(event); if (shutdownState.isShuttingDown()) stop(); - } catch (AuthException | PermissionBackendException | IOException e) { + } catch (AuthException + | PermissionBackendException + | IOException + | UnprocessableEntityException + | ResourceNotFoundException e) { throw new EventRejectedException(event, e); } }
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 4c621ac..2f992cd 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
@@ -24,13 +24,16 @@ 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.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.server.config.GerritInstanceId; import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.EventListener; import com.google.gerrit.server.events.ProjectCreatedEvent; import com.google.gerrit.server.events.ProjectEvent; +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.permissions.PermissionBackendException; @@ -47,6 +50,7 @@ import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob.Factory; 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; import com.googlesource.gerrit.plugins.replication.pull.filter.ExcludedRefsFilter; import java.io.IOException; import java.util.Optional; @@ -59,6 +63,7 @@ private final DeleteRefCommand deleteCommand; private final ExcludedRefsFilter refsFilter; private final Factory fetchJobFactory; + private final UpdateHeadCommand updateHeadCommand; private final ProjectInitializationAction projectInitializationAction; private final Provider<PullReplicationApiRequestMetrics> metricsProvider; private final SourcesCollection sources; @@ -70,6 +75,7 @@ public StreamEventListener( @Nullable @GerritInstanceId String instanceId, DeleteRefCommand deleteCommand, + UpdateHeadCommand updateHeadCommand, ProjectInitializationAction projectInitializationAction, WorkQueue workQueue, FetchJob.Factory fetchJobFactory, @@ -79,6 +85,7 @@ @Named(APPLY_OBJECTS_CACHE) Cache<ApplyObjectsCacheKey, Long> refUpdatesSucceededCache) { this.instanceId = instanceId; this.deleteCommand = deleteCommand; + this.updateHeadCommand = updateHeadCommand; this.projectInitializationAction = projectInitializationAction; this.workQueue = workQueue; this.fetchJobFactory = fetchJobFactory; @@ -95,7 +102,11 @@ public void onEvent(Event event) { try { fetchRefsForEvent(event); - } catch (AuthException | PermissionBackendException | IOException e) { + } catch (AuthException + | PermissionBackendException + | IOException + | UnprocessableEntityException + | ResourceNotFoundException e) { logger.atSevere().withCause(e).log( "This is the event handler of Gerrit's event-bus. It isn't" + "supposed to throw any exception, otherwise the other handlers " @@ -104,7 +115,8 @@ } public void fetchRefsForEvent(Event event) - throws AuthException, PermissionBackendException, IOException { + throws AuthException, PermissionBackendException, IOException, UnprocessableEntityException, + ResourceNotFoundException { if (instanceId.equals(event.instanceId) || !shouldReplicateProject(event)) { return; } @@ -159,6 +171,15 @@ "Cannot initialise project:%s", projectCreatedEvent.projectName); throw e; } + } else if (event instanceof ProjectHeadUpdatedEvent) { + ProjectHeadUpdatedEvent headUpdatedEvent = (ProjectHeadUpdatedEvent) event; + try { + updateHeadCommand.doUpdate(headUpdatedEvent.getProjectNameKey(), headUpdatedEvent.newHead); + } catch (UnprocessableEntityException | ResourceNotFoundException e) { + logger.atSevere().withCause(e).log( + "Failed to update HEAD on project: %s", headUpdatedEvent.projectName); + throw e; + } } }
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 552853e..e91ce8d 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
@@ -29,6 +29,7 @@ import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.ProjectCreatedEvent; +import com.google.gerrit.server.events.ProjectHeadUpdatedEvent; import com.google.gerrit.server.events.RefUpdatedEvent; import com.google.gerrit.server.git.WorkQueue; import com.googlesource.gerrit.plugins.replication.pull.ApplyObjectsCacheKey; @@ -40,6 +41,7 @@ import com.googlesource.gerrit.plugins.replication.pull.api.FetchJob; 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; import com.googlesource.gerrit.plugins.replication.pull.filter.ExcludedRefsFilter; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jgit.lib.ObjectId; @@ -66,6 +68,7 @@ @Mock private ScheduledExecutorService executor; @Mock private FetchJob fetchJob; @Mock private FetchJob.Factory fetchJobFactory; + @Mock private UpdateHeadCommand updateHeadCommand; @Mock private DeleteRefCommand deleteRefCommand; @Captor ArgumentCaptor<Input> inputCaptor; @Mock private PullReplicationApiRequestMetrics metrics; @@ -92,6 +95,7 @@ new StreamEventListener( INSTANCE_ID, deleteRefCommand, + updateHeadCommand, projectInitializationAction, workQueue, fetchJobFactory, @@ -311,4 +315,17 @@ verify(executor).submit(any(FetchJob.class)); } + + @Test + public void shouldUpdateProjectHeadOnProjectHeadUpdatedEvent() throws Exception { + ProjectHeadUpdatedEvent event = new ProjectHeadUpdatedEvent(); + event.projectName = TEST_PROJECT; + event.oldHead = "refs/heads/master"; + event.newHead = "refs/heads/main"; + event.instanceId = REMOTE_INSTANCE_ID; + + objectUnderTest.onEvent(event); + + verify(updateHeadCommand).doUpdate(event.getProjectNameKey(), event.newHead); + } }