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() {