Repository export read-only lock (#1519)

* Lock repository for read-only access only while exporting
* Create read-only check api

Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
Eduard Heimbuch
2021-02-04 15:29:49 +01:00
committed by GitHub
parent 04c6243f64
commit ac5d145266
54 changed files with 1104 additions and 182 deletions

View File

@@ -88,6 +88,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static java.util.Collections.singletonList;
import static java.util.stream.Stream.of;
@@ -104,23 +105,24 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_SELF;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.mockito.MockitoAnnotations.openMocks;
@SubjectAware(
username = "trillian",
password = "secret",
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
@SuppressWarnings("UnstableApiUsage")
public class RepositoryRootResourceTest extends RepositoryTestBase {
private static final String REALM = "AdminRealm";
@@ -160,6 +162,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
private Repository repositoryMarkedAsExported;
@InjectMocks
private RepositoryToRepositoryDtoMapperImpl repositoryToDtoMapper;
@@ -168,7 +171,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() {
initMocks(this);
openMocks(this);
super.repositoryToDtoMapper = repositoryToDtoMapper;
super.dtoToRepositoryMapper = dtoToRepositoryMapper;
super.manager = repositoryManager;
@@ -316,7 +319,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
dispatcher.invoke(request, response);
assertEquals(SC_NO_CONTENT, response.getStatus());
verify(repositoryManager).modify(anyObject());
verify(repositoryManager).modify(any());
}
@Test
@@ -336,7 +339,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
assertEquals(SC_CONFLICT, response.getStatus());
assertThat(response.getContentAsString()).contains("space/repo");
verify(repositoryManager, never()).modify(anyObject());
verify(repositoryManager, never()).modify(any());
}
@Test
@@ -355,7 +358,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
dispatcher.invoke(request, response);
assertEquals(SC_BAD_REQUEST, response.getStatus());
verify(repositoryManager, never()).modify(anyObject());
verify(repositoryManager, never()).modify(any());
}
@Test
@@ -368,7 +371,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
dispatcher.invoke(request, response);
assertEquals(SC_NO_CONTENT, response.getStatus());
verify(repositoryManager).delete(anyObject());
verify(repositoryManager).delete(any());
}
@Test
@@ -826,7 +829,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
/**
* This method is a slightly adapted copy of Lin Zaho's gist at https://gist.github.com/lin-zhao/9985191
*/
private MockHttpRequest multipartRequest(MockHttpRequest request, Map<String, InputStream> files, RepositoryDto repository) throws IOException {
private void multipartRequest(MockHttpRequest request, Map<String, InputStream> files, RepositoryDto repository) throws IOException {
String boundary = UUID.randomUUID().toString();
request.contentType("multipart/form-data; boundary=" + boundary);
@@ -864,6 +867,5 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
formWriter.flush();
}
request.setInputStream(new ByteArrayInputStream(buffer.toByteArray()));
return request;
}
}

View File

@@ -33,6 +33,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryExportingCheck;
import sonia.scm.repository.RepositoryTestData;
import sonia.scm.repository.api.BundleCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
@@ -47,6 +48,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -70,6 +72,8 @@ class FullScmRepositoryExporterTest {
private TarArchiveRepositoryStoreExporter storeExporter;
@Mock
private WorkdirProvider workdirProvider;
@Mock
private RepositoryExportingCheck repositoryExportingCheck;
@InjectMocks
private FullScmRepositoryExporter exporter;
@@ -81,6 +85,7 @@ class FullScmRepositoryExporterTest {
when(serviceFactory.create(REPOSITORY)).thenReturn(repositoryService);
when(environmentGenerator.generate()).thenReturn(new byte[0]);
when(metadataGenerator.generate(REPOSITORY)).thenReturn(new byte[0]);
when(repositoryExportingCheck.withExportingLock(any(), any())).thenAnswer(invocation -> invocation.getArgument(1, Supplier.class).get());
}
@Test
@@ -96,6 +101,7 @@ class FullScmRepositoryExporterTest {
verify(environmentGenerator, times(1)).generate();
verify(metadataGenerator, times(1)).generate(REPOSITORY);
verify(bundleCommandBuilder, times(1)).bundle(any(OutputStream.class));
verify(repositoryExportingCheck).withExportingLock(eq(REPOSITORY), any());
workDirsCreated.forEach(wd -> assertThat(wd).doesNotExist());
}

View File

@@ -108,8 +108,7 @@ public class DefaultRepositoryManagerPerfTest {
Set<RepositoryHandler> handlerSet = ImmutableSet.of(repositoryHandler);
NamespaceStrategy namespaceStrategy = mock(NamespaceStrategy.class);
repositoryManager = new DefaultRepositoryManager(
configuration,
contextProvider,
contextProvider,
keyGenerator,
repositoryDAO,
handlerSet,

View File

@@ -65,7 +65,6 @@ import java.util.Map;
import java.util.Set;
import java.util.Stack;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasProperty;
@@ -109,7 +108,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
private RepositoryDAO repositoryDAO;
{
static {
ThreadContext.unbindSubject();
}
@@ -121,8 +120,6 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
private NamespaceStrategy namespaceStrategy = mock(NamespaceStrategy.class);
private ScmConfiguration configuration;
private String mockedNamespace = "default_namespace";
@Before
@@ -552,11 +549,9 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
handlerSet.add(createRepositoryHandler("hg", "Mercurial"));
handlerSet.add(createRepositoryHandler("svn", "SVN"));
this.configuration = new ScmConfiguration();
when(namespaceStrategy.createNamespace(Mockito.any(Repository.class))).thenAnswer(invocation -> mockedNamespace);
return new DefaultRepositoryManager(configuration, contextProvider,
return new DefaultRepositoryManager(contextProvider,
keyGenerator, repositoryDAO, handlerSet, Providers.of(namespaceStrategy));
}

View File

@@ -21,13 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.update;
import org.junit.jupiter.api.Test;
import sonia.scm.migration.UpdateStep;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.store.InMemoryConfigurationEntryStore;
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
import sonia.scm.version.Version;