Fix issue with ref deletion and global-refdb Do not reuse `com.google.gerrit.server.restapi.project.DeleteRef` class to delete ref because it operates with an high-level repository manager. Using the high-level repository manager may be an issue in come cases where it is wrapped, like in the multi-site setup (see [1]), with a global-refdb and therefore may not be able to complete the REST-API successfully. [1] https://gerrit.googlesource.com/plugins/multi-site/ Original-Author: maczech@gmail.com Bug: Issue 16128 Change-Id: I2034a48fe04489e2827de7c44bdb0e67de3f4665
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java index 0c5e016..2a3a79d 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
@@ -19,41 +19,52 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.entities.Project; import com.google.gerrit.extensions.registration.DynamicItem; -import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.server.events.EventDispatcher; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; +import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectState; -import com.google.gerrit.server.restapi.project.DeleteRef; import com.google.inject.Inject; import com.googlesource.gerrit.plugins.replication.pull.Context; import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent; import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger; import com.googlesource.gerrit.plugins.replication.pull.ReplicationState; +import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject; +import com.googlesource.gerrit.plugins.replication.pull.fetch.RefUpdateState; import java.io.IOException; import java.util.Optional; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; public class DeleteRefCommand { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final PullReplicationStateLogger fetchStateLog; - private final DeleteRef deleteRef; + private final ApplyObject applyObject; private final DynamicItem<EventDispatcher> eventDispatcher; private final ProjectCache projectCache; + private final PermissionBackend permissionBackend; + private final LocalDiskRepositoryManager gitManager; @Inject public DeleteRefCommand( PullReplicationStateLogger fetchStateLog, ProjectCache projectCache, - DeleteRef deleteRef, - DynamicItem<EventDispatcher> eventDispatcher) { + ApplyObject applyObject, + PermissionBackend permissionBackend, + DynamicItem<EventDispatcher> eventDispatcher, + LocalDiskRepositoryManager gitManager) { this.fetchStateLog = fetchStateLog; this.projectCache = projectCache; - this.deleteRef = deleteRef; + this.applyObject = applyObject; this.eventDispatcher = eventDispatcher; + this.permissionBackend = permissionBackend; + this.gitManager = gitManager; } public void deleteRef(Project.NameKey name, String refName, String sourceLabel) @@ -66,8 +77,15 @@ } try { + projectState.get().checkStatePermitsWrite(); + permissionBackend + .currentUser() + .project(projectState.get().getNameKey()) + .ref(refName) + .check(RefPermission.DELETE); + Context.setLocalEvent(true); - deleteRef.deleteSingleRef(projectState.get(), refName); + deleteRef(name, refName); eventDispatcher .get() @@ -83,7 +101,7 @@ "Unexpected error while trying to delete ref '%s' on project %s and notifying it", refName, name); throw RestApiException.wrap(e.getMessage(), e); - } catch (ResourceConflictException e) { + } catch (IOException e) { eventDispatcher .get() .postEvent( @@ -110,4 +128,18 @@ throw RestApiException.wrap(e.getMessage(), e); } } + + private RefUpdateState deleteRef(Project.NameKey name, String refName) throws IOException { + + try (Repository repository = gitManager.openRepository(name)) { + RefUpdate.Result result; + RefUpdate u = repository.updateRef(refName); + u.setExpectedOldObjectId(repository.exactRef(refName).getObjectId()); + u.setNewObjectId(ObjectId.zeroId()); + u.setForceUpdate(true); + + result = u.delete(); + return new RefUpdateState(refName, result); + } + } }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java index daf2001..4415a4b 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
@@ -16,6 +16,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -24,12 +25,22 @@ import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.EventDispatcher; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; +import com.google.gerrit.server.permissions.PermissionBackend; +import com.google.gerrit.server.permissions.PermissionBackend.ForProject; +import com.google.gerrit.server.permissions.PermissionBackend.ForRef; +import com.google.gerrit.server.permissions.PermissionBackend.WithUser; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectState; -import com.google.gerrit.server.restapi.project.DeleteRef; import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent; import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger; +import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject; import java.util.Optional; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.lib.Repository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,19 +59,42 @@ @Mock private DynamicItem<EventDispatcher> eventDispatcherDataItem; @Mock private EventDispatcher eventDispatcher; @Mock private ProjectCache projectCache; - @Mock private DeleteRef deleteRef; + @Mock private ApplyObject applyObject; @Mock private ProjectState projectState; + @Mock private PermissionBackend permissionBackend; + @Mock private WithUser currentUser; + @Mock private ForProject forProject; + @Mock private ForRef forRef; + @Mock private LocalDiskRepositoryManager gitManager; + @Mock private RefUpdate refUpdate; + @Mock private Repository repository; + @Mock private Ref currentRef; + @Mock private RefDatabase refDb; @Captor ArgumentCaptor<Event> eventCaptor; private DeleteRefCommand objectUnderTest; @Before - public void setup() { + public void setup() throws Exception { when(eventDispatcherDataItem.get()).thenReturn(eventDispatcher); when(projectCache.get(any())).thenReturn(Optional.of(projectState)); + when(permissionBackend.currentUser()).thenReturn(currentUser); + when(currentUser.project(any())).thenReturn(forProject); + when(forProject.ref(any())).thenReturn(forRef); + when(gitManager.openRepository(any())).thenReturn(repository); + when(repository.updateRef(any())).thenReturn(refUpdate); + when(repository.getRefDatabase()).thenReturn(refDb); + when(refDb.exactRef(anyString())).thenReturn(currentRef); + when(refUpdate.delete()).thenReturn(Result.FORCED); objectUnderTest = - new DeleteRefCommand(fetchStateLog, projectCache, deleteRef, eventDispatcherDataItem); + new DeleteRefCommand( + fetchStateLog, + projectCache, + applyObject, + permissionBackend, + eventDispatcherDataItem, + gitManager); } @Test