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