Consider the headName value when creating a new repository The creation of a new repository is notified through the ProjectCreatedEvent but the headName was ignored, causing the new project to be always created with a default head. Use the headName in the ProjectCreatedEvent and use it for creating the new empty repository. Also consider the 'headName' parameter when the creation happens via REST-API, even though the client-side of the replication via HTTP isn't implemented yet in this change and will be expanded in a follow-up change. Bug: Issue 430486435 Change-Id: I2a37b01e55c1acbc5e51bde365dd84c9943c9b31
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java index b94189e..e21aaf3 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueue.java
@@ -750,10 +750,10 @@ private Optional<HttpResult> initProject( Project.NameKey project, URIish uri, FetchApiClient fetchClient, Optional<HttpResult> result) - throws IOException { + throws IOException, URISyntaxException { + RevisionReader revisionReader = revReaderProvider.get(); RevisionData refsMetaConfigRevisionData = - revReaderProvider - .get() + revisionReader .read(project, null, RefNames.REFS_CONFIG, 0) .orElseThrow( () -> @@ -764,7 +764,12 @@ List<RevisionData> refsMetaConfigDataList = fetchWholeMetaHistory(project, RefNames.REFS_CONFIG, refsMetaConfigRevisionData); HttpResult initProjectResult = - fetchClient.initProject(project, uri, System.currentTimeMillis(), refsMetaConfigDataList); + fetchClient.initProject( + project, + revisionReader.getHeadName(project), + uri, + System.currentTimeMillis(), + refsMetaConfigDataList); if (initProjectResult.isSuccessful()) { result = Optional.of(fetchClient.callFetch(project, FetchOne.ALL_REFS, uri)); } else {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java index 9458083..68fa98a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java
@@ -19,6 +19,7 @@ import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; import com.google.gerrit.entities.Project; +import com.google.gerrit.entities.RefNames; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.inject.Inject; import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig; @@ -319,4 +320,15 @@ return "type:" + type; } } + + @Nullable + public String getHeadName(Project.NameKey project) throws IOException { + try (Repository repo = gitRepositoryManager.openRepository(project)) { + Ref ref = repo.exactRef(RefNames.HEAD); + if (ref == null) { + return null; + } + return ref.getTarget().getName(); + } + } }
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 3214787..c7e8a8c 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
@@ -22,6 +22,7 @@ import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; +import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; @@ -103,15 +104,17 @@ } String gitRepositoryName = getGitRepositoryName(httpServletRequest); + String headName = getGitRepositoryHeadName(httpServletRequest); try { boolean initProjectStatus; String contentType = httpServletRequest.getContentType(); if (checkContentType(contentType, MediaType.JSON_UTF_8)) { // init project request includes project configuration in JSON format. - initProjectStatus = initProjectWithConfiguration(httpServletRequest, gitRepositoryName); + initProjectStatus = + initProjectWithConfiguration(httpServletRequest, gitRepositoryName, headName); } else if (checkContentType(contentType, MediaType.PLAIN_TEXT_UTF_8)) { // init project request does not include project configuration. - initProjectStatus = initProject(gitRepositoryName); + initProjectStatus = initProject(gitRepositoryName, RefNames.HEAD); } else { setResponse( httpServletResponse, @@ -146,17 +149,17 @@ } } - public boolean initProject(String gitRepositoryName) + public boolean initProject(String gitRepositoryName, String headName) throws AuthException, PermissionBackendException, IOException { - if (initProject(gitRepositoryName, true)) { - repLog.info("Init project API from {}", gitRepositoryName); + if (initProject(gitRepositoryName, headName, true)) { + repLog.info("Init project {} with head {}", gitRepositoryName, headName); return true; } return false; } private boolean initProjectWithConfiguration( - HttpServletRequest httpServletRequest, String gitRepositoryName) + HttpServletRequest httpServletRequest, String gitRepositoryName, String headName) throws AuthException, PermissionBackendException, IOException, @@ -167,7 +170,7 @@ RevisionsInput input = PayloadSerDes.parseRevisionsInput(httpServletRequest); validateInput(input); - if (!initProject(gitRepositoryName, false)) { + if (!initProject(gitRepositoryName, headName, false)) { return false; } @@ -206,7 +209,8 @@ return true; } - private boolean initProject(String gitRepositoryName, boolean needsProjectReindexing) + private boolean initProject( + String gitRepositoryName, String headName, boolean needsProjectReindexing) throws AuthException, PermissionBackendException, IOException { // When triggered internally(for example by consuming stream events) user is not provided // and internal user is returned. Project creation should be always allowed for internal user. @@ -220,7 +224,7 @@ } LocalFS localFS = new LocalFS(maybeUri.get()); Project.NameKey projectNameKey = Project.NameKey.parse(gitRepositoryName); - if (localFS.createProject(projectNameKey, RefNames.HEAD)) { + if (localFS.createProject(projectNameKey, headName)) { if (needsProjectReindexing) { projectCache.onCreateProject(projectNameKey); } @@ -247,6 +251,11 @@ return Url.decode(path.substring(path.lastIndexOf('/') + 1)); } + private String getGitRepositoryHeadName(HttpServletRequest httpServletRequest) { + String headName = httpServletRequest.getParameter("headName"); + return MoreObjects.firstNonNull(headName, RefNames.HEAD); + } + private void logExceptionAndUpdateResponse( HttpServletResponse httpServletResponse, Exception e,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchApiClient.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchApiClient.java index b908435..67651ff 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchApiClient.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchApiClient.java
@@ -16,6 +16,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; +import com.google.gerrit.common.Nullable; import com.google.gerrit.entities.Project; import com.google.gerrit.entities.Project.NameKey; import com.googlesource.gerrit.plugins.replication.pull.Source; @@ -23,6 +24,7 @@ import com.googlesource.gerrit.plugins.replication.pull.api.data.BatchApplyObjectData; import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionData; import java.io.IOException; +import java.net.URISyntaxException; import java.util.List; import org.eclipse.jgit.transport.URIish; @@ -68,10 +70,11 @@ */ HttpResult initProject( Project.NameKey project, + @Nullable String headName, URIish uri, long eventCreatedOn, List<RevisionData> refsMetaConfigRevisionData) - throws IOException; + throws IOException, URISyntaxException; HttpResult deleteProject(Project.NameKey project, URIish apiUri) throws IOException;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClient.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClient.java index c3bb378..ec33a64 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClient.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClient.java
@@ -44,6 +44,8 @@ import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionsInput; import com.googlesource.gerrit.plugins.replication.pull.filter.SyncRefsFilter; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; @@ -59,6 +61,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.message.BasicHeader; @@ -190,11 +193,12 @@ @Override public HttpResult initProject( NameKey project, + @Nullable String headName, URIish uri, long eventCreatedOn, List<RevisionData> refsMetaConfigRevisionData) - throws IOException { - String url = formatInitProjectUrl(uri.toString(), project); + throws IOException, URISyntaxException { + URI url = formatInitProjectUrl(uri.toString(), project, headName); RevisionData[] inputData = new RevisionData[refsMetaConfigRevisionData.size()]; RevisionsInput input = @@ -321,10 +325,18 @@ targetUri, urlAuthenticationPrefix, Url.encode(project.get()), pluginName, api); } - private String formatInitProjectUrl(String targetUri, Project.NameKey project) { - return String.format( - "%s/%splugins/%s/init-project/%s.git", - targetUri, urlAuthenticationPrefix, pluginName, Url.encode(project.get())); + private URI formatInitProjectUrl( + String targetUri, Project.NameKey project, @Nullable String headName) + throws URISyntaxException { + String baseUrl = + String.format( + "%s/%splugins/%s/init-project/%s.git", + targetUri, urlAuthenticationPrefix, pluginName, Url.encode(project.get())); + URIBuilder baseUriBuilder = new URIBuilder(baseUrl); + if (headName != null) { + baseUriBuilder = baseUriBuilder.addParameter("headName", headName); + } + return baseUriBuilder.build(); } private void requireNull(Object object, String string) {
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 eb2d7e0..d8cf100 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
@@ -164,7 +164,8 @@ } else if (event instanceof ProjectCreatedEvent) { ProjectCreatedEvent projectCreatedEvent = (ProjectCreatedEvent) event; try { - projectInitializationAction.initProject(getProjectRepositoryName(projectCreatedEvent)); + projectInitializationAction.initProject( + getProjectRepositoryName(projectCreatedEvent), projectCreatedEvent.headName); fetchRefsAsync( FetchOne.ALL_REFS, projectCreatedEvent.instanceId,
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITBase.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITBase.java index a6058d3..a73a97e 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITBase.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITBase.java
@@ -376,6 +376,7 @@ FetchApiClient client = getInstance(FetchApiClient.Factory.class).create(source); client.initProject( projectToCreate, + null, new URIish(source.getApis().get(0)), System.currentTimeMillis(), Collections.emptyList());
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java index 7f4ca4e..88a7ba1 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueTest.java
@@ -182,7 +182,7 @@ when(fetchRestApiClient.callFetch(any(), anyString(), any(), anyLong(), anyBoolean())) .thenReturn(fetchHttpResult); when(fetchRestApiClient.callBatchFetch(any(), any(), any())).thenReturn(batchFetchHttpResult); - when(fetchRestApiClient.initProject(any(), any(), anyLong(), any())) + when(fetchRestApiClient.initProject(any(), any(), any(), anyLong(), any())) .thenReturn(successfulHttpResult); when(successfulHttpResult.isSuccessful()).thenReturn(true); when(httpResult.isSuccessful()).thenReturn(true); @@ -273,7 +273,7 @@ objectUnderTest.start(); objectUnderTest.onEvent(event); - verify(fetchRestApiClient).initProject(any(), any(), anyLong(), any()); + verify(fetchRestApiClient).initProject(any(), any(), any(), anyLong(), any()); } @Test @@ -299,7 +299,7 @@ objectUnderTest.start(); objectUnderTest.onEvent(event); - verify(fetchRestApiClient, never()).initProject(any(), any(), anyLong(), any()); + verify(fetchRestApiClient, never()).initProject(any(), any(), any(), anyLong(), any()); } @Test @@ -316,7 +316,7 @@ objectUnderTest.start(); objectUnderTest.onEvent(event); - verify(fetchRestApiClient, never()).initProject(any(), any(), anyLong(), any()); + verify(fetchRestApiClient, never()).initProject(any(), any(), any(), anyLong(), any()); } @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClientBase.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClientBase.java index cfff177..4c756f6 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClientBase.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/client/FetchRestApiClientBase.java
@@ -557,8 +557,10 @@ @Test public void shouldCallInitProjectEndpoint() throws Exception { + String headName = RefNames.REFS_HEADS + "main"; objectUnderTest.initProject( Project.nameKey("test_repo"), + headName, new URIish(api), eventCreatedOn, Collections.singletonList(createSampleRevisionData())); @@ -576,6 +578,7 @@ String.format( "%s/plugins/pull-replication/init-project/test_repo.git", urlAuthenticationPrefix())); + assertThat(httpPut.getURI().getQuery()).isEqualTo("headName=" + headName); assertThat(payload).isEqualTo(expectedInitProjectWithConfigPayload); assertAuthentication(httpPut); }
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 980c509..dd6b110 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
@@ -25,6 +25,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.Lists; import com.google.gerrit.entities.Project; +import com.google.gerrit.entities.RefNames; import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.ProjectCreatedEvent; @@ -208,10 +209,12 @@ ProjectCreatedEvent event = new ProjectCreatedEvent(); event.instanceId = REMOTE_INSTANCE_ID; event.projectName = TEST_PROJECT; + event.headName = RefNames.HEAD; objectUnderTest.onEvent(event); - verify(projectInitializationAction).initProject(String.format("%s.git", TEST_PROJECT)); + verify(projectInitializationAction) + .initProject(String.format("%s.git", TEST_PROJECT), RefNames.HEAD); } @Test @@ -221,10 +224,11 @@ ProjectCreatedEvent event = new ProjectCreatedEvent(); event.instanceId = REMOTE_INSTANCE_ID; event.projectName = TEST_PROJECT; + event.headName = RefNames.HEAD; objectUnderTest.onEvent(event); - verify(projectInitializationAction, never()).initProject(any()); + verify(projectInitializationAction, never()).initProject(any(), eq(RefNames.HEAD)); } @Test @@ -234,10 +238,11 @@ ProjectCreatedEvent event = new ProjectCreatedEvent(); event.instanceId = REMOTE_INSTANCE_ID; event.projectName = TEST_PROJECT; + event.headName = RefNames.HEAD; objectUnderTest.onEvent(event); - verify(projectInitializationAction, never()).initProject(any()); + verify(projectInitializationAction, never()).initProject(any(), eq(RefNames.HEAD)); } @Test