Add missing project creation support for replicas Currently replication of project initialization is only possible on master nodes. Add the same functionality to the replicas. Bug: Issue 15204 Change-Id: Ia3f15caf4ab1278ce97a21585b2da2f687ec1496
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 53b8e8a..8f5c9d0 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
@@ -93,7 +93,7 @@ "Cannot initialize project " + projectName); } - private boolean initProject(String projectName) { + protected boolean initProject(String projectName) { Optional<URIish> maybeUri = getGitRepositoryURI(projectName); if (!maybeUri.isPresent()) { logger.atSevere().log("Cannot initialize project '{}'", projectName);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java index 65b8e1b..1425be6 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
@@ -15,10 +15,13 @@ package com.googlesource.gerrit.plugins.replication.pull.api; import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_UNPROCESSABLE_ENTITY; +import static com.googlesource.gerrit.plugins.replication.pull.api.HttpServletOps.checkAcceptHeader; +import static com.googlesource.gerrit.plugins.replication.pull.api.HttpServletOps.setResponse; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_CONFLICT; import static javax.servlet.http.HttpServletResponse.SC_CREATED; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; @@ -28,10 +31,12 @@ import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.httpd.AllRequestFilter; import com.google.gerrit.httpd.restapi.RestApiServlet; import com.google.gerrit.json.OutputFormat; @@ -48,6 +53,7 @@ import com.google.inject.TypeLiteral; import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction.Input; import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionInput; +import com.googlesource.gerrit.plugins.replication.pull.api.exception.InitProjectException; import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; @@ -67,6 +73,7 @@ private FetchAction fetchAction; private ApplyObjectAction applyObjectAction; + private ProjectInitializationAction projectInitializationAction; private ProjectsCollection projectsCollection; private Gson gson; private Provider<CurrentUser> userProvider; @@ -75,10 +82,12 @@ public PullReplicationFilter( FetchAction fetchAction, ApplyObjectAction applyObjectAction, + ProjectInitializationAction projectInitializationAction, ProjectsCollection projectsCollection, Provider<CurrentUser> userProvider) { this.fetchAction = fetchAction; this.applyObjectAction = applyObjectAction; + this.projectInitializationAction = projectInitializationAction; this.projectsCollection = projectsCollection; this.userProvider = userProvider; this.gson = OutputFormat.JSON.newGsonBuilder().create(); @@ -107,6 +116,15 @@ } else { httpResponse.sendError(SC_UNAUTHORIZED); } + } else if (isInitProjectAction(httpRequest)) { + if (userProvider.get().isIdentifiedUser()) { + if (!checkAcceptHeader(httpRequest, httpResponse)) { + return; + } + doInitProject(httpRequest, httpResponse); + } else { + httpResponse.sendError(SC_UNAUTHORIZED); + } } else { chain.doFilter(request, response); } @@ -126,11 +144,27 @@ } catch (ResourceConflictException e) { RestApiServlet.replyError( httpRequest, httpResponse, SC_CONFLICT, e.getMessage(), e.caching(), e); + } catch (InitProjectException | ResourceNotFoundException e) { + RestApiServlet.replyError( + httpRequest, httpResponse, SC_INTERNAL_SERVER_ERROR, e.getMessage(), e.caching(), e); } catch (Exception e) { throw new ServletException(e); } } + private void doInitProject(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + throws RestApiException, IOException { + + String path = httpRequest.getRequestURI(); + String projectName = Url.decode(path.substring(path.lastIndexOf('/') + 1)); + if (projectInitializationAction.initProject(projectName)) { + setResponse( + httpResponse, HttpServletResponse.SC_CREATED, "Project " + projectName + " initialized"); + return; + } + throw new InitProjectException(projectName); + } + @SuppressWarnings("unchecked") private Response<Map<String, Object>> doApplyObject(HttpServletRequest httpRequest) throws RestApiException, IOException, PermissionBackendException { @@ -222,4 +256,8 @@ private boolean isFetchAction(HttpServletRequest httpRequest) { return httpRequest.getRequestURI().endsWith("pull-replication~fetch"); } + + private boolean isInitProjectAction(HttpServletRequest httpRequest) { + return httpRequest.getRequestURI().contains("pull-replication/init-project/"); + } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java new file mode 100644 index 0000000..85a7729 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/exception/InitProjectException.java
@@ -0,0 +1,25 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.replication.pull.api.exception; + +import com.google.gerrit.extensions.restapi.RestApiException; + +public class InitProjectException extends RestApiException { + private static final long serialVersionUID = 1L; + + public InitProjectException(String projectName) { + super("Cannot create project " + projectName); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java index 5fc22e4..919ac34 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/ProjectInitializationActionIT.java
@@ -17,6 +17,7 @@ import static com.googlesource.gerrit.plugins.replication.pull.api.ProjectInitializationAction.getProjectInitializationUrl; import com.google.common.net.MediaType; +import com.google.gerrit.acceptance.config.GerritConfig; import com.google.gerrit.extensions.restapi.Url; import javax.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpGet; @@ -25,6 +26,8 @@ import org.junit.Test; public class ProjectInitializationActionIT extends ActionITBase { + public static final String INVALID_TEST_PROJECT_NAME = "\0"; + private String testProjectName = "new/Project"; @Test public void shouldReturnUnauthorizedForUserWithoutPermissions() throws Exception { @@ -63,11 +66,60 @@ getNewProjectRequest, assertHttpResponseCode(HttpServletResponse.SC_OK), getContext()); } + @Test + @GerritConfig(name = "container.replica", value = "true") + public void shouldCreateRepositoryWhenNodeIsAReplica() throws Exception { + httpClientFactory + .create(source) + .execute( + createPutRequestWithHeaders(), + assertHttpResponseCode(HttpServletResponse.SC_CREATED), + getContext()); + } + + @Test + @GerritConfig(name = "container.replica", value = "true") + public void shouldReturnInternalServerErrorIfProjectCannotBeCreatedWhenNodeIsAReplica() + throws Exception { + testProjectName = INVALID_TEST_PROJECT_NAME; + url = getURL(); + + httpClientFactory + .create(source) + .execute( + createPutRequestWithHeaders(), + assertHttpResponseCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR), + getContext()); + } + + @Test + @GerritConfig(name = "container.replica", value = "true") + public void shouldReturnBadRequestIfContentNotSetWhenNodeIsAReplica() throws Exception { + httpClientFactory + .create(source) + .execute( + createPutRequestWithoutHeaders(), + assertHttpResponseCode(HttpServletResponse.SC_BAD_REQUEST), + getContext()); + } + + @Test + @GerritConfig(name = "container.replica", value = "true") + public void shouldReturnUnauthorizedForUserWithoutPermissionsWhenNodeIsAReplica() + throws Exception { + httpClientFactory + .create(source) + .execute( + createPutRequestWithHeaders(), + assertHttpResponseCode(HttpServletResponse.SC_UNAUTHORIZED), + getAnonymousContext()); + } + @Override protected String getURL() { return userRestSession.url() + "/" - + getProjectInitializationUrl("pull-replication", Url.encode("new/Project")); + + getProjectInitializationUrl("pull-replication", Url.encode(testProjectName)); } protected HttpPut createPutRequestWithHeaders() {