#910 fix missing instance id for newly created subversion repositories in format 7

This commit is contained in:
Sebastian Sdorra
2017-05-05 09:45:58 +02:00
parent 1bab7eac8e
commit 2915a65da2
6 changed files with 286 additions and 3 deletions

View File

@@ -40,7 +40,8 @@ package sonia.scm.repository;
public enum Compatibility
{
NONE(false, false, false, false, false),
PRE14(true, true, true, true, false), PRE15(false, true, true, true, false),
PRE14(true, true, true, true, false),
PRE15(false, true, true, true, false),
PRE16(false, false, true, true, false),
PRE17(false, false, false, true, false),
WITH17(false, false, false, false, true);

View File

@@ -0,0 +1,147 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.repository;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Fixes Subversion repositories with db format 7, but without an instance id in the db/uuid file.
*
* @see <a href="https://goo.gl/c3Ih89">910</a>
* @author Sebastian Sdorra
* @since 1.52
*/
public final class InstanceIDFix {
private static final Logger LOG = LoggerFactory.getLogger(InstanceIDFix.class);
private static final String PATH_DB = "db";
private static final String PATH_FORMAT = "format";
private static final String PATH_UUID = "uuid";
private static final String DB_FORMAT = "7";
private static final Charset CHARSET = Charsets.US_ASCII;
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private final File repository;
private final String dbFormat;
private final List<String> uuids;
/**
* Creates a new instance to fix subversion repositories.
*
* @param repository root directory of subversion repository
*
* @throws IOException
*/
public InstanceIDFix(File repository) throws IOException {
this.repository = repository;
this.dbFormat = readDBFormat(repository);
this.uuids = readUUIDS(repository);
}
/**
* Returns {@core true} if the repository format is 7 and the instance id is missing.
*
* @return {@core true} if the repository must be fixed.
*/
public boolean isRequired() {
return DB_FORMAT.equals(dbFormat) && uuids.size() == 1;
}
private String readDBFormat(File directory) throws IOException {
return Files.readFirstLine(dbFile(directory, PATH_FORMAT), CHARSET);
}
private File dbFile(File directory, String filename) {
return new File(directory, PATH_DB + File.separator + filename);
}
private List<String> readUUIDS(File directory) throws IOException {
return new ArrayList<>(Files.readLines(dbFile(directory, PATH_UUID), CHARSET));
}
/**
* Add missing instance id to the uuid file of the repository and returns the generated instance id.
*
* @throws IOException
* @return generated instance id
*/
public String addInstanceID() throws IOException {
Preconditions.checkState(isRequired(), "repository has already an instance id and does not require the fix");
String uuid = uuids.get(0);
String instanceID = generateInstanceID();
addInstanceID(uuid, instanceID);
return instanceID;
}
private void addInstanceID(String uuid, String instanceID) throws IOException {
LOG.info("created instance id {} for subversion repository {} format 7", instanceID, uuid);
String uuidFileContent = createUUIDFileContent(uuid, instanceID);
Files.write(uuidFileContent, dbFile(repository, PATH_UUID), CHARSET);
uuids.add(instanceID);
}
private String generateInstanceID() {
return UUID.randomUUID().toString();
}
private String createUUIDFileContent(String uuid, String instanceID) {
return new StringBuilder(uuid)
.append(LINE_SEPARATOR)
.append(instanceID)
.append(LINE_SEPARATOR)
.toString();
}
@VisibleForTesting
List<String> getUuids() {
return ImmutableList.copyOf(uuids);
}
@VisibleForTesting
File getRepository() {
return repository;
}
}

View File

@@ -317,6 +317,8 @@ public class SvnRepositoryHandler
comp.isPre15Compatible(), comp.isPre16Compatible(),
comp.isPre17Compatible(), comp.isWith17Compatible());
fixMissingInstanceID(directory);
svnRepository = SVNRepositoryFactory.create(url);
String uuid = svnRepository.getRepositoryUUID(true);
@@ -346,6 +348,14 @@ public class SvnRepositoryHandler
SvnUtil.closeSession(svnRepository);
}
}
private void fixMissingInstanceID(File directory) throws IOException {
InstanceIDFix fix = new InstanceIDFix(directory);
if (fix.isRequired()) {
fix.addInstanceID();
}
}
/**
* Method description

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.repository;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
/**
* Unit tests for {@link InstanceIDFix}.
*
* @author Sebastian Sdorra
*/
@RunWith(MockitoJUnitRunner.class)
public class InstanceIDFixTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testIsRequired() throws SVNException, IOException {
// svnkit uses db format 4 as default, so NONE must not be fixed
assertFalse(createFix(Compatibility.NONE).isRequired());
assertFalse(createFix(Compatibility.PRE14).isRequired());
assertFalse(createFix(Compatibility.PRE15).isRequired());
assertFalse(createFix(Compatibility.PRE16).isRequired());
// WITH17 creates db format 7, which is subversion >= 1.9 or not?
assertTrue(createFix(Compatibility.WITH17).isRequired());
}
@Test
public void testAddInstanceID() throws SVNException, IOException {
InstanceIDFix fix = createFix(Compatibility.WITH17);
assertTrue(fix.isRequired());
String instanceID = fix.addInstanceID();
assertFalse(fix.isRequired());
fix = new InstanceIDFix(fix.getRepository());
assertFalse(fix.isRequired());
assertTrue(fix.getUuids().contains(instanceID));
}
@Test(expected = IllegalStateException.class)
public void testAddInstanceIDAllreadyFixedRepository() throws SVNException, IOException {
InstanceIDFix fix = createFix(Compatibility.WITH17);
assertTrue(fix.isRequired());
fix.addInstanceID();
fix.addInstanceID();
}
private InstanceIDFix createFix(Compatibility compatibility) throws SVNException, IOException {
return new InstanceIDFix(createRepository(compatibility));
}
private File createRepository(Compatibility compatibility) throws SVNException, IOException {
File directory = tempFolder.newFolder();
SVNRepositoryFactory.createLocalRepository(directory, null, true, false, compatibility.isPre14Compatible(),
compatibility.isPre15Compatible(), compatibility.isPre16Compatible(),
compatibility.isPre17Compatible(), compatibility.isWith17Compatible());
return directory;
}
}

View File

@@ -35,6 +35,8 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import sonia.scm.io.DefaultFileSystem;
import sonia.scm.store.StoreFactory;
@@ -43,6 +45,9 @@ import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
/**
*
@@ -66,7 +71,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase
assertTrue(format.isFile());
File db = new File(directory, "db");
assertTrue(db.exists());
assertTrue(db.isDirectory());
}
@@ -96,4 +101,18 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase
return handler;
}
@Test
public void testCreatedUUID() throws RepositoryException, IOException {
SvnRepositoryHandler handler = (SvnRepositoryHandler) getHandler();
handler.getConfig().setCompatibility(Compatibility.WITH17);
Repository repository = RepositoryTestData.createRestaurantAtTheEndOfTheUniverse();
handler.create(repository);
File directory = handler.getDirectory(repository);
File uuidFile = new File(directory, "db" + File.separator + "uuid");
List<String> lines = Files.readLines(uuidFile, Charsets.UTF_8);
assertEquals(2, lines.size());
}
}

View File

@@ -76,6 +76,11 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase
protected abstract RepositoryHandler createRepositoryHandler(
StoreFactory factory, File directory);
protected RepositoryHandler getHandler() {
return handler;
}
/**
* Method description
*
@@ -202,7 +207,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase
/** Field description */
protected File baseDirectory;
/** Field description */
private RepositoryHandler handler;
}