Merge branch 'stable-2.16' into stable-3.0 * stable-2.16: Upgrade bazlets to latest stable-2.16 to build with 2.16.26 API Upgrade bazlets to latest stable-2.16 to build with 2.16.23 API Change-Id: I1aaee339d8cd7bd22a5fa8a349a5000b8b97f4fb
diff --git a/.bazelversion b/.bazelversion index fd2a018..7c69a55 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -3.1.0 +3.7.0
diff --git a/WORKSPACE b/WORKSPACE index 27db984..e658752 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -3,26 +3,15 @@ load("//:bazlets.bzl", "load_bazlets") load_bazlets( - commit = "6c39deb06f58bb62162ccb6865964f531739f512", + commit = "a010a539824bd467059f414412a47b80101352d7", #local_path = "/home/<user>/projects/bazlets", ) -# Snapshot Plugin API -#load( -# "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl", -# "gerrit_api_maven_local", -#) - -# Load snapshot Plugin API -#gerrit_api_maven_local() - -# Release Plugin API load( "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl", "gerrit_api", ) -# Load release Plugin API gerrit_api() load("//:external_plugin_deps.bzl", "external_plugin_deps")
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java index 04426bc..170e0d6 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java
@@ -28,17 +28,22 @@ public class LockUnlockProject { private final MetaDataUpdate.Server metaDataUpdateFactory; private final ProjectCache projectCache; + private final ProjectConfig.Factory projectConfigFactory; @Inject - LockUnlockProject(MetaDataUpdate.Server metaDataUpdateFactory, ProjectCache projectCache) { + LockUnlockProject( + MetaDataUpdate.Server metaDataUpdateFactory, + ProjectCache projectCache, + ProjectConfig.Factory projectConfigFactory) { this.metaDataUpdateFactory = metaDataUpdateFactory; this.projectCache = projectCache; + this.projectConfigFactory = projectConfigFactory; } public void lock(Project.NameKey key) throws IOException, ConfigInvalidException { MetaDataUpdate md = metaDataUpdateFactory.create(key); - ProjectConfig projectConfig = ProjectConfig.read(md); + ProjectConfig projectConfig = projectConfigFactory.read(md); Project p = projectConfig.getProject(); p.setState(ProjectState.READ_ONLY); @@ -50,7 +55,7 @@ public void unlock(Project.NameKey key) throws IOException, ConfigInvalidException { MetaDataUpdate md = metaDataUpdateFactory.create(key); - ProjectConfig projectConfig = ProjectConfig.read(md); + ProjectConfig projectConfig = projectConfigFactory.read(md); Project p = projectConfig.getProject(); p.setState(ProjectState.ACTIVE);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java index 6962811..568a7be 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java
@@ -14,12 +14,14 @@ package com.googlesource.gerrit.plugins.renameproject; +import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND; import static com.googlesource.gerrit.plugins.renameproject.RenameOwnProjectCapability.RENAME_OWN_PROJECT; import static com.googlesource.gerrit.plugins.renameproject.RenameProjectCapability.RENAME_PROJECT; import com.google.gerrit.extensions.annotations.Exports; import com.google.gerrit.extensions.config.CapabilityDefinition; import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.extensions.restapi.RestApiModule; import com.google.inject.AbstractModule; import com.google.inject.internal.UniqueAnnotations; import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler; @@ -45,5 +47,13 @@ bind(RenamePreconditions.class); bind(IndexUpdateHandler.class); bind(RevertRenameProject.class); + + install( + new RestApiModule() { + @Override + protected void configure() { + post(PROJECT_KIND, "rename").to(RenameProject.class); + } + }); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java index 14b5898..80b1306 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java
@@ -14,17 +14,16 @@ package com.googlesource.gerrit.plugins.renameproject; +import static com.googlesource.gerrit.plugins.renameproject.RenameProject.CANCELLATION_MSG; import static com.googlesource.gerrit.plugins.renameproject.RenameProject.WARNING_LIMIT; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.SshCommand; -import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.googlesource.gerrit.plugins.renameproject.monitor.CommandProgressMonitor; @@ -33,6 +32,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.List; +import java.util.Optional; import org.kohsuke.args4j.Argument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,23 +40,18 @@ @CommandMetaData(name = "rename", description = "Rename project") public final class RenameCommand extends SshCommand { @Argument(index = 0, required = true, metaVar = "OLDPROJECT", usage = "project to rename") - private String projectControl; + private ProjectState projectState; @Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "new name for the project") private String newProjectName; private static final Logger log = LoggerFactory.getLogger(RenameCommand.class); private final RenameProject renameProject; - private final Provider<ProjectCache> projectCacheProvider; private final Provider<CurrentUser> self; @Inject - protected RenameCommand( - RenameProject renameProject, - Provider<ProjectCache> projectCacheProvider, - Provider<CurrentUser> self) { + protected RenameCommand(RenameProject renameProject, Provider<CurrentUser> self) { this.renameProject = renameProject; - this.projectCacheProvider = projectCacheProvider; this.self = self; } @@ -65,22 +60,19 @@ try { RenameProject.Input input = new RenameProject.Input(); input.name = newProjectName; - ProjectResource rsrc = - new ProjectResource( - projectCacheProvider.get().get(new Project.NameKey(projectControl)), self.get()); + ProjectResource rsrc = new ProjectResource(projectState, self.get()); try (CommandProgressMonitor monitor = new CommandProgressMonitor(stdout)) { - renameProject.assertCanRename(rsrc, input, monitor); - List<Change.Id> changeIds = renameProject.getChanges(rsrc, monitor); + renameProject.assertCanRename(rsrc, input, Optional.of(monitor)); + List<Change.Id> changeIds = renameProject.getChanges(rsrc, Optional.of(monitor)); if (continueRename(changeIds, monitor)) { - renameProject.doRename(changeIds, rsrc, input, monitor); + renameProject.doRename(changeIds, rsrc, input, Optional.of(monitor)); } else { - String cancellationMsg = "Rename operation was cancelled by user."; - log.debug(cancellationMsg); - stdout.println(cancellationMsg); + log.debug(CANCELLATION_MSG); + stdout.println(CANCELLATION_MSG); stdout.flush(); } } - } catch (RestApiException | OrmException | IOException e) { + } catch (RestApiException | IOException e) { throw die(e); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java index 88c0e3b..6449cba 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java
@@ -17,10 +17,10 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.MultimapBuilder; import com.google.gerrit.extensions.systemstatus.ServerInformation; +import com.google.gerrit.json.OutputFormat; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.AuditEvent; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.audit.AuditService; import com.google.gerrit.server.util.PluginLogFile; import com.google.gerrit.server.util.SystemLog;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java index 5a11a17..053e8b9 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java
@@ -33,8 +33,8 @@ * +0100] INFO 1000000 admin OK \ myProject {"name": newName} * * <p>The log entry for a failed project rename will look like this: [2015-03-05 12:14:30,180 - * +0100] ERROR 1000000 admin FAIL \ myProject {"name": newName} - * com.google.gwtorm.server.OrmException: \ Failed to access the database + * +0100] ERROR 1000000 admin FAIL \ myProject {"name": newName} TODO(mmiller): \ Failed to access + * the database */ @Override public String format(LoggingEvent event) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java index d1f1ba5..c7937b6 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
@@ -25,7 +25,10 @@ import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.Change.Id; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; @@ -35,11 +38,11 @@ import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.project.ProjectResource; -import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.name.Named; +import com.googlesource.gerrit.plugins.renameproject.RenameProject.Input; import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler; import com.googlesource.gerrit.plugins.renameproject.conditions.RenamePreconditions; import com.googlesource.gerrit.plugins.renameproject.database.DatabaseRenameHandler; @@ -49,18 +52,39 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.eclipse.jgit.errors.ConfigInvalidException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton -public class RenameProject { +public class RenameProject implements RestModifyView<ProjectResource, Input> { + + @Override + public Object apply(ProjectResource resource, Input input) + throws IOException, AuthException, BadRequestException, ResourceConflictException, + InterruptedException, ConfigInvalidException, RenameRevertException { + assertCanRename(resource, input, Optional.empty()); + List<Id> changeIds = getChanges(resource, Optional.empty()); + + if (changeIds == null || changeIds.size() <= WARNING_LIMIT || input.continueWithRename) { + doRename(changeIds, resource, input, Optional.empty()); + } else { + log.debug(CANCELLATION_MSG); + return Response.none(); + } + return Response.ok(""); + } static class Input { String name; + boolean continueWithRename; } static final int WARNING_LIMIT = 5000; + static final String CANCELLATION_MSG = + "Rename cancelled due to number of changes exceeding warning limit and user's will to not continue"; + private static final Logger log = LoggerFactory.getLogger(RenameProject.class); private static final String CACHE_NAME = "changeid_project"; @@ -147,10 +171,11 @@ return true; } - void assertCanRename(ProjectResource rsrc, Input input, ProgressMonitor pm) + void assertCanRename(ProjectResource rsrc, Input input, Optional<ProgressMonitor> pm) throws ResourceConflictException, BadRequestException, AuthException { try { - pm.beginTask("Checking preconditions"); + pm.ifPresent(progressMonitor -> progressMonitor.beginTask("Checking preconditions")); + assertNewNameNotNull(input); assertRenamePermission(rsrc); renamePreconditions.assertCanRename(rsrc, new Project.NameKey(input.name)); @@ -160,8 +185,9 @@ } } - void doRename(List<Change.Id> changeIds, ProjectResource rsrc, Input input, ProgressMonitor pm) - throws InterruptedException, OrmException, ConfigInvalidException, IOException { + void doRename( + List<Change.Id> changeIds, ProjectResource rsrc, Input input, Optional<ProgressMonitor> pm) + throws InterruptedException, ConfigInvalidException, IOException, RenameRevertException { Project.NameKey oldProjectKey = rsrc.getNameKey(); Project.NameKey newProjectKey = new Project.NameKey(input.name); Exception ex = null; @@ -212,7 +238,7 @@ } void fsRenameStep( - Project.NameKey oldProjectKey, Project.NameKey newProjectKey, ProgressMonitor pm) + Project.NameKey oldProjectKey, Project.NameKey newProjectKey, Optional<ProgressMonitor> pm) throws IOException { fsHandler.rename(oldProjectKey, newProjectKey, pm); logPerformedStep(Step.FILESYSTEM, newProjectKey, oldProjectKey); @@ -228,10 +254,9 @@ List<Change.Id> changeIds, Project.NameKey oldProjectKey, Project.NameKey newProjectKey, - ProgressMonitor pm) - throws OrmException { - List<Change.Id> updatedChangeIds = - dbHandler.rename(changeIds, oldProjectKey, newProjectKey, pm); + Optional<ProgressMonitor> pm) + throws IOException, ConfigInvalidException, RenameRevertException { + List<Change.Id> updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, pm); logPerformedStep(Step.DATABASE, newProjectKey, oldProjectKey); return updatedChangeIds; } @@ -240,7 +265,7 @@ List<Change.Id> updatedChangeIds, Project.NameKey oldProjectKey, Project.NameKey newProjectKey, - ProgressMonitor pm) + Optional<ProgressMonitor> pm) throws InterruptedException { indexHandler.updateIndex(updatedChangeIds, newProjectKey, pm); logPerformedStep(Step.INDEX, newProjectKey, oldProjectKey); @@ -276,9 +301,9 @@ return stepsPerformed; } - List<Change.Id> getChanges(ProjectResource rsrc, ProgressMonitor pm) - throws OrmException, IOException { - pm.beginTask("Retrieving the list of changes from DB"); + List<Change.Id> getChanges(ProjectResource rsrc, Optional<ProgressMonitor> opm) + throws IOException { + opm.ifPresent(pm -> pm.beginTask("Retrieving the list of changes from DB")); Project.NameKey oldProjectKey = rsrc.getNameKey(); return dbHandler.getChangeIds(oldProjectKey); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java index 0cb6256..b0e467a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
@@ -13,10 +13,8 @@ // limitations under the License. package com.googlesource.gerrit.plugins.renameproject; -import com.google.gwtorm.server.OrmException; - /** Add cause for exception during revert operation */ -public class RenameRevertException extends OrmException { +public class RenameRevertException extends Exception { private static final long serialVersionUID = 1L; public RenameRevertException(Throwable revertException, Throwable cause) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java index 5de66f2..0671c71 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java
@@ -17,7 +17,6 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change.Id; import com.google.gerrit.reviewdb.client.Project; -import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.googlesource.gerrit.plugins.renameproject.RenameProject.Step; import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler; @@ -28,6 +27,8 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Optional; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,13 +57,13 @@ List<Id> changeIds, Project.NameKey oldProjectKey, Project.NameKey newProjectKey, - ProgressMonitor pm) - throws IOException, OrmException { - pm.beginTask("Reverting the rename procedure."); + Optional<ProgressMonitor> opm) + throws IOException, RenameRevertException, ConfigInvalidException { + opm.ifPresent(pm -> pm.beginTask("Reverting the rename procedure.")); List<Change.Id> updatedChangeIds = Collections.emptyList(); if (stepsPerformed.contains(Step.FILESYSTEM)) { try { - fsHandler.rename(newProjectKey, oldProjectKey, pm); + fsHandler.rename(newProjectKey, oldProjectKey, opm); log.debug("Reverted the git repo name to {} successfully.", oldProjectKey.get()); } catch (IOException e) { log.error( @@ -76,12 +77,12 @@ } if (stepsPerformed.contains(Step.DATABASE)) { try { - updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, oldProjectKey, pm); + updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, opm); log.debug( "Reverted the changes in DB successfully from project {} to project {}.", newProjectKey.get(), oldProjectKey.get()); - } catch (OrmException e) { + } catch (RenameRevertException | ConfigInvalidException e) { log.error( "Failed to revert changes in DB for project {}. Secondary indexes not reverted. Exception caught: {}", oldProjectKey.get(), @@ -91,7 +92,7 @@ } if (stepsPerformed.contains(Step.INDEX)) { try { - indexHandler.updateIndex(updatedChangeIds, oldProjectKey, pm); + indexHandler.updateIndex(updatedChangeIds, oldProjectKey, opm); log.debug( "Reverted the secondary index successfully from project {} to project {}.", newProjectKey.get(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java index 9efa51d..3e30f6e 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java
@@ -28,7 +28,6 @@ import com.google.gerrit.server.submit.MergeOpRepoManager; import com.google.gerrit.server.submit.SubmoduleException; import com.google.gerrit.server.submit.SubmoduleOp; -import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -102,7 +101,7 @@ log.error(message); throw new CannotRenameProjectException(message); } - } catch (PermissionBackendException | OrmException | RestApiException e) { + } catch (PermissionBackendException | RestApiException e) { throw new CannotRenameProjectException(e); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java index a837267..2ed4ad5 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java
@@ -19,7 +19,6 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.ServerInitiated; import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountsUpdate; @@ -28,26 +27,19 @@ import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult; -import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.server.query.account.InternalAccountQuery; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.renameproject.RenameRevertException; import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor; import java.io.IOException; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -58,69 +50,37 @@ public class DatabaseRenameHandler { private static final Logger log = LoggerFactory.getLogger(DatabaseRenameHandler.class); - private final SchemaFactory<ReviewDb> schemaFactory; - private final ChangeNotes.Factory schemaFactoryNoteDb; + private final ChangeNotes.Factory schemaFactory; private final GitRepositoryManager repoManager; private final Provider<InternalAccountQuery> accountQueryProvider; private final Provider<AccountsUpdate> accountsUpdateProvider; - private NotesMigration migration; + + private Project.NameKey oldProjectKey; @Inject public DatabaseRenameHandler( - SchemaFactory<ReviewDb> schemaFactory, - ChangeNotes.Factory schemaFactoryNoteDb, + ChangeNotes.Factory schemaFactory, GitRepositoryManager repoManager, - NotesMigration migration, Provider<InternalAccountQuery> accountQueryProvider, @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) { this.accountQueryProvider = accountQueryProvider; this.schemaFactory = schemaFactory; - this.schemaFactoryNoteDb = schemaFactoryNoteDb; this.repoManager = repoManager; this.accountsUpdateProvider = accountsUpdateProvider; - this.migration = migration; } - public List<Change.Id> getChangeIds(Project.NameKey oldProjectKey) - throws OrmException, IOException { + public List<Change.Id> getChangeIds(Project.NameKey oldProjectKey) throws IOException { log.debug("Starting to retrieve changes from the DB for project {}", oldProjectKey.get()); - ReviewDb db = schemaFactory.open(); - return (isNoteDb()) - ? getChangeIdsFromNoteDb(oldProjectKey, db) - : getChangeIdsFromReviewDb(oldProjectKey, db); - } + this.oldProjectKey = oldProjectKey; - private List<Change.Id> getChangeIdsFromReviewDb(Project.NameKey oldProjectKey, ReviewDb db) - throws OrmException { - List<Change.Id> changeIds = new ArrayList<>(); - Connection conn = ((JdbcSchema) db).getConnection(); - String query = - "select change_id from changes where dest_project_name ='" + oldProjectKey.get() + "';"; - try (Statement stmt = conn.createStatement(); - ResultSet changes = stmt.executeQuery(query)) { - while (changes != null && changes.next()) { - Change.Id changeId = new Change.Id(changes.getInt(1)); - changeIds.add(changeId); - } - log.debug( - "Number of changes in reviewDb related to project {} are {}", - oldProjectKey.get(), - changeIds.size()); - return changeIds; - } catch (SQLException e) { - throw new OrmException(e); - } - } - - private List<Change.Id> getChangeIdsFromNoteDb(Project.NameKey oldProjectKey, ReviewDb db) - throws IOException { List<Change.Id> changeIds = new ArrayList<>(); Stream<ChangeNotesResult> changes = - schemaFactoryNoteDb.scan(repoManager.openRepository(oldProjectKey), db, oldProjectKey); + schemaFactory.scan(repoManager.openRepository(oldProjectKey), oldProjectKey); Iterator<ChangeNotesResult> iterator = changes.iterator(); while (iterator.hasNext()) { ChangeNotesResult change = iterator.next(); - changeIds.add(change.id()); + Change.Id changeId = change.id(); + changeIds.add(changeId); } log.debug( "Number of changes in noteDb related to project {} are {}", @@ -129,101 +89,21 @@ return changeIds; } - private boolean isNoteDb() { - return migration.disableChangeReviewDb(); - } - public List<Change.Id> rename( - List<Change.Id> changes, - Project.NameKey oldProjectKey, - Project.NameKey newProjectKey, - ProgressMonitor pm) - throws OrmException, RenameRevertException { - pm.beginTask("Updating changes in the database"); - ReviewDb db = schemaFactory.open(); - return (isNoteDb()) - ? renameInNoteDb(changes, oldProjectKey, newProjectKey) - : renameInReviewDb(changes, oldProjectKey, newProjectKey, db); - } - - private List<Change.Id> renameInReviewDb( - List<Change.Id> changes, - Project.NameKey oldProjectKey, - Project.NameKey newProjectKey, - ReviewDb db) - throws OrmException { - Connection conn = ((JdbcSchema) db).getConnection(); - try (Statement stmt = conn.createStatement()) { - conn.setAutoCommit(false); - try { - try { - log.debug("Updating the changes in reviewDb related to project {}", oldProjectKey.get()); - for (Change.Id cd : changes) { - stmt.addBatch( - "update changes set dest_project_name='" - + newProjectKey.get() - + "' where change_id =" - + cd.id - + ";"); - } - stmt.executeBatch(); - conn.commit(); - } catch (SQLException e) { - throw new OrmException(e); - } - updateWatchEntries(oldProjectKey, newProjectKey); - log.debug( - "Successfully updated the changes in reviewDb related to project {}", - oldProjectKey.get()); - return changes; - } catch (OrmException e) { - try { - log.error( - "Failed to update changes in reviewDb for project {}, exception caught: {}. Rolling back the operation.", - oldProjectKey.get(), - e.toString()); - conn.rollback(); - } catch (SQLException revertEx) { - log.error( - "Failed to rollback changes in reviewDb from project {} to project {}, exception caught: {}", - newProjectKey.get(), - oldProjectKey.get(), - revertEx.toString()); - throw new RenameRevertException(revertEx, e); - } - try { - updateWatchEntries(newProjectKey, oldProjectKey); - } catch (OrmException revertEx) { - log.error( - "Failed to update watched changes in reviewDb from project {} to project {}, exception caught: {}", - newProjectKey.get(), - oldProjectKey.get(), - revertEx.toString()); - throw new RenameRevertException(revertEx, e); - } - throw e; - } finally { - conn.setAutoCommit(true); - } - } catch (SQLException e) { - throw new OrmException(e); - } - } - - private List<Change.Id> renameInNoteDb( - List<Change.Id> changes, Project.NameKey oldProjectKey, Project.NameKey newProjectKey) - throws OrmException { + List<Change.Id> changes, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm) + throws RenameRevertException, IOException, ConfigInvalidException { + opm.ifPresent(pm -> pm.beginTask("Updating changes in the database")); log.debug("Updating the changes in noteDb related to project {}", oldProjectKey.get()); try { - updateWatchEntries(oldProjectKey, newProjectKey); - } catch (OrmException e) { + updateWatchEntries(newProjectKey); + } catch (Exception e) { log.error( "Failed to update changes in noteDb for project {}, exception caught: {}. Rolling back the operation.", oldProjectKey.get(), e.toString()); try { - updateWatchEntries(newProjectKey, oldProjectKey); - } catch (OrmException revertEx) { + updateWatchEntries(newProjectKey); + } catch (Exception revertEx) { log.error( "Failed to rollback changes in noteDb from project {} to project {}, exception caught: {}", newProjectKey.get(), @@ -239,8 +119,8 @@ return changes; } - private void updateWatchEntries(Project.NameKey oldProjectKey, Project.NameKey newProjectKey) - throws OrmException { + private void updateWatchEntries(Project.NameKey newProjectKey) + throws IOException, ConfigInvalidException { for (AccountState a : accountQueryProvider.get().byWatchedProject(oldProjectKey)) { Account.Id accountId = a.getAccount().getId(); ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches = @@ -274,14 +154,14 @@ a.getUserName(), newProjectKey.get(), e); - throw new OrmException(e); + throw e; } catch (IOException e) { log.error( "Updating watch entry for user {} in project {} failed.", a.getUserName(), newProjectKey.get(), e); - throw new OrmException(e); + throw e; } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java index ed12dc3..f75b784 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java
@@ -17,17 +17,14 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.index.change.ChangeIndexer; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Inject; import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.renameproject.Configuration; import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -40,30 +37,27 @@ public class IndexUpdateHandler { private static final Logger log = LoggerFactory.getLogger(IndexUpdateHandler.class); - private final SchemaFactory<ReviewDb> schemaFactory; private final ChangeIndexer indexer; private final Configuration config; @Inject - public IndexUpdateHandler( - SchemaFactory<ReviewDb> schemaFactory, ChangeIndexer indexer, Configuration config) { - this.schemaFactory = schemaFactory; + public IndexUpdateHandler(ChangeIndexer indexer, Configuration config) { this.indexer = indexer; this.config = config; } public void updateIndex( - List<Change.Id> changeIds, Project.NameKey newProjectKey, ProgressMonitor pm) + List<Change.Id> changeIds, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm) throws InterruptedException { log.debug("Starting to index {} change(s).", changeIds.size()); ExecutorService executor = Executors.newFixedThreadPool( config.getIndexThreads(), new ThreadFactoryBuilder().setNameFormat("Rename-Index-%d").build()); - pm.beginTask("Indexing changes", changeIds.size()); + opm.ifPresent(pm -> pm.beginTask("Indexing changes", changeIds.size())); List<Callable<Boolean>> callableTasks = new ArrayList<>(changeIds.size()); for (Change.Id id : changeIds) { - callableTasks.add(new IndexTask(id, newProjectKey, pm)); + callableTasks.add(new IndexTask(id, newProjectKey, opm)); } List<Future<Boolean>> tasksCompleted = executor.invokeAll(callableTasks); executor.shutdown(); @@ -90,9 +84,10 @@ private Change.Id changeId; private Project.NameKey newProjectKey; - private ProgressMonitor monitor; + private Optional<ProgressMonitor> monitor; - IndexTask(Change.Id changeId, Project.NameKey newProjectKey, ProgressMonitor monitor) { + IndexTask( + Change.Id changeId, Project.NameKey newProjectKey, Optional<ProgressMonitor> monitor) { this.changeId = changeId; this.newProjectKey = newProjectKey; this.monitor = monitor; @@ -100,14 +95,9 @@ @Override public Boolean call() throws Exception { - try (ReviewDb db = schemaFactory.open()) { - indexer.index(db, newProjectKey, changeId); - monitor.update(1); - return Boolean.TRUE; - } catch (OrmException | IOException e) { - log.error("Failed to reindex change {} from index, Cause: {}", changeId, e); - throw e; - } + indexer.index(newProjectKey, changeId); + monitor.ifPresent(monitor -> monitor.update(1)); + return Boolean.TRUE; } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java index 29a76d8..1572d53 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java +++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java
@@ -26,6 +26,7 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Comparator; +import java.util.Optional; import java.util.stream.Stream; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Repository; @@ -45,12 +46,12 @@ } public void rename( - Project.NameKey oldProjectKey, Project.NameKey newProjectKey, ProgressMonitor pm) + Project.NameKey oldProjectKey, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm) throws IOException, RepositoryNotFoundException { Repository repository = repoManager.openRepository(oldProjectKey); File repoFile = repository.getDirectory(); RepositoryCache.close(repository); - pm.beginTask("Renaming git repository"); + opm.ifPresent(pm -> pm.beginTask("Renaming git repository")); renameGitRepository(repoFile, newProjectKey, oldProjectKey); }
diff --git a/src/main/resources/Documentation/rest-api-rename.md b/src/main/resources/Documentation/rest-api-rename.md new file mode 100644 index 0000000..70ecf15 --- /dev/null +++ b/src/main/resources/Documentation/rest-api-rename.md
@@ -0,0 +1,55 @@ +@PLUGIN@ - /@PLUGIN@/ REST API +=================================== + +This page describes the REST endpoint that is added by the @PLUGIN@ +plugin. + +Please also take note of the general information on the +[REST API](../../../Documentation/rest-api.html). + +This API implements a REST equivalent of the Ssh rename-project command. +For more information, refer to: +* [Ssh rename-project command](cmd-rename.md) +------------------------------------------ + +REQUEST +------- +``` +POST /projects/project-1/@PLUGIN@~rename HTTP/1.1 + { + "name" : "project-2", + } +``` +to rename project-1 to project-2. + +By default, if project-1 has more than 5000 changes, the rename procedure will be cancelled as it +can take longer time and can degrade in performance in that time frame. + +To rename a project with more than 5000 changes, the following request is needed: +``` +POST /projects/project-1/@PLUGIN@~rename HTTP/1.1 + { + "name" : "project-2", + "continueWithRename" : "true" + } +``` + +RESPONSE +-------- +If rename succeeded: + +``` +HTTP/1.1 200 OK +``` + +If rename was cancelled due to user's intent to not proceed when the number of changes exceeds the +warning limit of 5000 changes: + +``` +HTTP/1.1 204 No Content +``` + +ACCESS +------ +Same as ssh version of the command, caller must be a member of a group that is granted the +'Rename Project' (provided by this plugin) or 'Administrate Server' capabilities.
diff --git a/src/test/README.md b/src/test/README.md new file mode 100644 index 0000000..f6147ff --- /dev/null +++ b/src/test/README.md
@@ -0,0 +1,16 @@ +# How to e2e-test this plugin + +Consider the +[instructions](https://gerrit-review.googlesource.com/Documentation/dev-e2e-tests.html) +on how to use Gerrit core's Gatling framework, to run non-core test +scenarios such as this plugin one below: + +``` + $ sbt "gatling:testOnly com.googlesource.gerrit.plugins.renameproject.scenarios.RenameProject" +``` + +Scenario scala source files and their companion json resource ones are +stored under the usual src/test directories. That structure follows the +scala package one from the scenario classes. The core framework expects +such a directory structure for both the scala and resources (json data) +files.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java index c0ae8f0..7df0e60 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java +++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java
@@ -19,15 +19,18 @@ import com.google.common.cache.Cache; import com.google.gerrit.acceptance.LightweightPluginDaemonTest; import com.google.gerrit.acceptance.PushOneCommit.Result; +import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.acceptance.TestPlugin; import com.google.gerrit.acceptance.UseLocalDisk; import com.google.gerrit.acceptance.UseSsh; +import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.extensions.client.ProjectWatchInfo; import com.google.gerrit.reviewdb.client.Change.Id; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project.NameKey; import com.google.gerrit.server.project.ProjectState; import com.google.inject.Inject; +import com.googlesource.gerrit.plugins.renameproject.RenameProject.Input; import java.util.List; import javax.inject.Named; import org.eclipse.jgit.junit.TestRepository; @@ -44,6 +47,8 @@ private static final String NEW_PROJECT_NAME = "newProject"; private static final String CACHE_NAME = "changeid_project"; + @Inject private RequestScopeOperations requestScopeOperations; + @Inject @Named(CACHE_NAME) private Cache<Id, String> changeIdProjectCache; @@ -95,12 +100,13 @@ @Test @UseLocalDisk public void testRenameSubscribedFail() throws Exception { - NameKey superProject = createProject("super-project"); + NameKey superProject = createProjectOverAPI("super-project", null, true, null); TestRepository<?> superRepo = cloneProject(superProject); - NameKey subProject = createProject("subscribed-to-project"); + NameKey subProject = createProjectOverAPI("subscribed-to-project", null, true, null); SubmoduleUtil.allowSubmoduleSubscription( metaDataUpdateFactory, projectCache, + projectConfigFactory, subProject, "refs/heads/master", superProject, @@ -145,4 +151,39 @@ assertThat(changeIdProjectCache.getIfPresent(changeID)).isNull(); } + + @Test + @UseLocalDisk + public void testRenameViaHttpSuccessful() throws Exception { + createChange(); + RestResponse r = renameProjectTo(NEW_PROJECT_NAME); + r.assertOK(); + + ProjectState projectState = projectCache.get(Project.nameKey(NEW_PROJECT_NAME)); + assertThat(projectState).isNotNull(); + assertThat(queryProvider.get().byProject(project)).isEmpty(); + assertThat(queryProvider.get().byProject(Project.nameKey(NEW_PROJECT_NAME))).isNotEmpty(); + } + + @Test + @UseLocalDisk + public void testRenameViaHttpWithEmptyNewName() throws Exception { + createChange(); + String newProjectName = ""; + RestResponse r = renameProjectTo(newProjectName); + r.assertBadRequest(); + + ProjectState projectState = projectCache.get(Project.nameKey(newProjectName)); + assertThat(projectState).isNull(); + } + + private RestResponse renameProjectTo(String newName) throws Exception { + requestScopeOperations.setApiUser(user.id()); + sender.clear(); + String endPoint = "/projects/" + project.get() + "/" + PLUGIN_NAME + "~rename"; + Input i = new Input(); + i.name = newName; + i.continueWithRename = true; + return adminRestSession.post(endPoint, i); + } }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java index d060414..f77d80e 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java
@@ -30,6 +30,7 @@ import com.googlesource.gerrit.plugins.renameproject.RenameProject.Step; import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor; import java.util.List; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -45,7 +46,7 @@ private RevertRenameProject revertRenameProject; private Project.NameKey oldProjectKey; private Project.NameKey newProjectKey; - private ProgressMonitor pm; + private Optional<ProgressMonitor> pm; private ProjectResource oldRsrc; @Before @@ -56,7 +57,7 @@ oldProjectKey = project; newProjectKey = new Project.NameKey(NEW_PROJECT_NAME); - pm = Mockito.mock(ProgressMonitor.class); + pm = Optional.of(Mockito.mock(ProgressMonitor.class)); oldRsrc = Mockito.mock(ProjectResource.class); when(oldRsrc.getNameKey()).thenReturn(oldProjectKey);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java index e6fcdd1..68515c4 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java +++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java
@@ -45,6 +45,7 @@ static void allowSubmoduleSubscription( Server metaDataUpdateFactory, ProjectCache projectCache, + ProjectConfig.Factory projectConfigFactory, NameKey sub, String subBranch, NameKey superName, @@ -54,7 +55,7 @@ try (MetaDataUpdate md = metaDataUpdateFactory.create(sub)) { md.setMessage("Added superproject subscription"); SubscribeSection s; - ProjectConfig pc = ProjectConfig.read(md); + ProjectConfig pc = projectConfigFactory.read(md); if (pc.getSubscribeSections().containsKey(superName)) { s = pc.getSubscribeSections().get(superName); } else {
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json new file mode 100644 index 0000000..fd49984 --- /dev/null +++ b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json
@@ -0,0 +1,3 @@ +{ + "name": "${project}" +}
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json new file mode 100644 index 0000000..6be790c --- /dev/null +++ b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json
@@ -0,0 +1,6 @@ +[ + { + "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/_PROJECT/rename-project~rename", + "project": "_RENAMED" + } +]
diff --git a/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala b/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala new file mode 100644 index 0000000..36665ee --- /dev/null +++ b/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala
@@ -0,0 +1,55 @@ +// Copyright (C) 2020 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.renameproject.scenarios + +import com.google.gerrit.scenarios.{CreateProject, DeleteProject, GerritSimulation} +import io.gatling.core.Predef._ +import io.gatling.core.feeder.FeederBuilder +import io.gatling.core.structure.ScenarioBuilder + +import scala.concurrent.duration._ + +class RenameProject extends GerritSimulation { + private val data: FeederBuilder = jsonFile(resource).convert(keys).queue + private val projectName = className + private val renamedTo = uniqueName + + override def replaceOverride(in: String): String = { + val next = replaceKeyWith("_project", projectName, in) + replaceKeyWith("_renamed", renamedTo, next) + } + + private val createProject = new CreateProject(projectName) + private val deleteProject = new DeleteProject(renamedTo) + + private val test: ScenarioBuilder = scenario(uniqueName) + .feed(data) + .exec(httpRequest.body(ElFileBody(body)).asJson) + + setUp( + createProject.test.inject( + nothingFor(stepWaitTime(createProject) seconds), + atOnceUsers(1) + ), + test.inject( + nothingFor(stepWaitTime(this) seconds), + atOnceUsers(1) + ), + deleteProject.test.inject( + nothingFor(stepWaitTime(deleteProject) seconds), + atOnceUsers(1) + ), + ).protocols(httpProtocol) +}