Add the repository import and export with metadata for Subversion repositories (#1501)

* Add store exporter to collect the repository metadata
* Add EnvironmentInformationXmlGenerator
* Collect export data and put into compressed tar archive output stream
* Create full repository export endpoint.
* Add full repository export to ui
* Ignore irrelevant files from config store directory
* write metadata stores to file since a baos could teardown the server memory
* Migrate store name for git lfs files (#1504)

Changes the directory name for the git LFS blob store by
removing the repository id from the store name.

This is necessary for im- and exports of lfs blob stores,
because the original name had the repository id as a part
of it and therefore the old store would not be found when
the repository is imported with another id.

Existing blob files will be moved to the new store location
by an update step.

Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>

* Introduce util for migrations (#1505)

With this util it is more simple to rename
or delete stores.

* Rename files in export

Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
Eduard Heimbuch
2021-01-28 11:40:35 +01:00
committed by GitHub
parent a35c227a55
commit d91c71ace1
87 changed files with 4187 additions and 84 deletions

View File

@@ -0,0 +1,52 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.repository.api.ExportFailedException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import static sonia.scm.ContextEntry.ContextBuilder.noContext;
final class ExportCopier {
private ExportCopier() {
}
static void putFileContentIntoStream(Exporter exporter, Path file) {
try (OutputStream stream = exporter.put(file.getFileName().toString(), Files.size(file))) {
Files.copy(file, stream);
} catch (IOException e) {
throw new ExportFailedException(
noContext(),
"Could not copy file to export stream: " + file,
e
);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Function;
import static java.util.Optional.empty;
import static java.util.Optional.of;
class ExportableBlobFileStore extends ExportableDirectoryBasedFileStore {
static Function<StoreType, Optional<Function<Path, ExportableStore>>> BLOB_FACTORY =
storeType -> storeType == StoreType.BLOB ? of(ExportableBlobFileStore::new) : empty();
ExportableBlobFileStore(Path directory) {
super(directory);
}
@Override
StoreType getStoreType() {
return StoreType.BLOB;
}
boolean shouldIncludeFile(Path file) {
return file.getFileName().toString().endsWith(".blob");
}
}

View File

@@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Function;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static sonia.scm.store.ExportCopier.putFileContentIntoStream;
class ExportableConfigFileStore implements ExportableStore {
private final Path file;
static Function<StoreType, Optional<Function<Path, ExportableStore>>> CONFIG_FACTORY =
storeType -> storeType == StoreType.CONFIG ? of(ExportableConfigFileStore::new) : empty();
ExportableConfigFileStore(Path file) {
this.file = file;
}
@Override
public StoreEntryMetaData getMetaData() {
return new StoreEntryMetaData(StoreType.CONFIG, file.getFileName().toString());
}
@Override
public void export(Exporter exporter) throws IOException {
if (file.getFileName().toString().endsWith(".xml")) {
putFileContentIntoStream(exporter, file);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Function;
import static java.util.Optional.empty;
import static java.util.Optional.of;
class ExportableDataFileStore extends ExportableDirectoryBasedFileStore {
static Function<StoreType, Optional<Function<Path, ExportableStore>>> DATA_FACTORY =
storeType -> storeType == StoreType.DATA ? of(ExportableDataFileStore::new) : empty();
ExportableDataFileStore(Path directory) {
super(directory);
}
@Override
StoreType getStoreType() {
return StoreType.DATA;
}
boolean shouldIncludeFile(Path file) {
return file.getFileName().toString().endsWith(".xml");
}
}

View File

@@ -0,0 +1,76 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.repository.api.ExportFailedException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static sonia.scm.ContextEntry.ContextBuilder.noContext;
import static sonia.scm.store.ExportCopier.putFileContentIntoStream;
abstract class ExportableDirectoryBasedFileStore implements ExportableStore {
private final Path directory;
ExportableDirectoryBasedFileStore(Path directory) {
this.directory = directory;
}
@Override
public StoreEntryMetaData getMetaData() {
return new StoreEntryMetaData(getStoreType(), directory.getFileName().toString());
}
abstract StoreType getStoreType();
abstract boolean shouldIncludeFile(Path file);
@Override
public void export(Exporter exporter) throws IOException {
exportDirectoryEntries(exporter, directory);
}
private void exportDirectoryEntries(Exporter exporter, Path directory) {
try (Stream<Path> fileList = Files.list(directory)) {
fileList.forEach(fileOrDir -> exportIfRelevant(exporter, fileOrDir));
} catch (IOException e) {
throw new ExportFailedException(
noContext(),
"Could not read directory " + directory,
e
);
}
}
private void exportIfRelevant(Exporter exporter, Path fileOrDir) {
if (!Files.isDirectory(fileOrDir) && shouldIncludeFile(fileOrDir)) {
putFileContentIntoStream(exporter, fileOrDir);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import com.google.common.annotations.VisibleForTesting;
import sonia.scm.ContextEntry;
import sonia.scm.repository.api.ImportFailedException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
class FileBasedStoreEntryImporter implements StoreEntryImporter {
private final Path directory;
FileBasedStoreEntryImporter(Path directory) {
this.directory = directory;
}
@VisibleForTesting
Path getDirectory() {
return this.directory;
}
@Override
public void importEntry(String name, InputStream stream) {
Path filePath = directory.resolve(name);
try {
Files.copy(stream, filePath);
} catch (IOException e) {
throw new ImportFailedException(
ContextEntry.ContextBuilder.noContext(),
String.format("Could not import file %s for store %s", name, directory.toString()),
e
);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.ContextEntry;
import sonia.scm.repository.api.ImportFailedException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
class FileBasedStoreEntryImporterFactory implements StoreEntryImporterFactory {
private final Path directory;
FileBasedStoreEntryImporterFactory(Path directory) {
this.directory = directory;
}
@Override
public StoreEntryImporter importStore(StoreEntryMetaData metaData) {
StoreType storeType = metaData.getType();
String storeName = metaData.getName();
Path storeDirectory = directory.resolve(Store.STORE_DIRECTORY);
try {
storeDirectory = storeDirectory.resolve(resolveFilePath(storeType.getValue(), storeName));
Files.createDirectories(storeDirectory);
if (!Files.exists(storeDirectory)) {
throw new ImportFailedException(
ContextEntry.ContextBuilder.noContext(),
String.format("Could not create store for type %s and name %s", storeType, storeName)
);
}
return new FileBasedStoreEntryImporter(storeDirectory);
} catch (IOException e) {
throw new ImportFailedException(
ContextEntry.ContextBuilder.noContext(),
String.format("Could not create store directory %s for type %s and name %s", storeDirectory, storeType, storeName)
);
}
}
private Path resolveFilePath(String type, String name) {
if (name == null || name.isEmpty()) {
return Paths.get(type);
}
return Paths.get(type, name);
}
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.update.RepositoryUpdateIterator;
import javax.inject.Inject;
import java.nio.file.Path;
import java.util.function.Consumer;
public class FileRepositoryUpdateIterator implements RepositoryUpdateIterator {
private final RepositoryLocationResolver locationResolver;
@Inject
public FileRepositoryUpdateIterator(RepositoryLocationResolver locationResolver) {
this.locationResolver = locationResolver;
}
@Override
public void forEachRepository(Consumer<String> repositoryIdConsumer) {
locationResolver
.forClass(Path.class)
.forAllLocations((repositoryId, path) -> repositoryIdConsumer.accept(repositoryId));
}
}

View File

@@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.api.ExportFailedException;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static sonia.scm.ContextEntry.ContextBuilder.noContext;
import static sonia.scm.store.ExportableBlobFileStore.BLOB_FACTORY;
import static sonia.scm.store.ExportableConfigFileStore.CONFIG_FACTORY;
import static sonia.scm.store.ExportableDataFileStore.DATA_FACTORY;
public class FileStoreExporter implements StoreExporter {
private final RepositoryLocationResolver locationResolver;
private static final Collection<Function<StoreType, Optional<Function<Path, ExportableStore>>>> STORE_FACTORIES =
asList(DATA_FACTORY, BLOB_FACTORY, CONFIG_FACTORY);
@Inject
public FileStoreExporter(RepositoryLocationResolver locationResolver) {
this.locationResolver = locationResolver;
}
@Override
public List<ExportableStore> listExportableStores(Repository repository) {
List<ExportableStore> exportableStores = new ArrayList<>();
Path storeDirectory = resolveStoreDirectory(repository);
if (!Files.exists(storeDirectory)) {
return emptyList();
}
try (Stream<Path> storeTypeDirectories = Files.list(storeDirectory)) {
storeTypeDirectories.forEach(storeTypeDirectory ->
exportStoreTypeDirectories(exportableStores, storeTypeDirectory)
);
} catch (IOException e) {
throw new ExportFailedException(
noContext(),
"Could not list content of directory " + storeDirectory,
e
);
}
return exportableStores;
}
private Path resolveStoreDirectory(Repository repository) {
return locationResolver
.forClass(Path.class)
.getLocation(repository.getId())
.resolve(Store.STORE_DIRECTORY);
}
private void exportStoreTypeDirectories(List<ExportableStore> exportableStores, Path storeTypeDirectory) {
try (Stream<Path> storeDirectories = Files.list(storeTypeDirectory)) {
storeDirectories.forEach(storeDirectory ->
getStoreFor(storeDirectory).ifPresent(exportableStores::add)
);
} catch (IOException e) {
throw new ExportFailedException(
noContext(),
"Could not list content of directory " + storeTypeDirectory,
e
);
}
}
private Optional<ExportableStore> getStoreFor(Path storePath) {
return STORE_FACTORIES
.stream()
.map(factory -> factory.apply(getEnumForValue(storePath.getParent())))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.map(f -> f.apply(storePath));
}
private StoreType getEnumForValue(Path storeTypeDirectory) {
for (StoreType type : StoreType.values()) {
if (type.getValue().equals(storeTypeDirectory.getFileName().toString())) {
return type;
}
}
return null;
}
}

View File

@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.SCMContextProvider;
import sonia.scm.migration.UpdateException;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.update.StoreUpdateStepUtilFactory;
import sonia.scm.util.IOUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
class FileStoreUpdateStepUtil implements StoreUpdateStepUtilFactory.StoreUpdateStepUtil {
private final RepositoryLocationResolver locationResolver;
private final SCMContextProvider contextProvider;
private final StoreParameters parameters;
private final StoreType type;
FileStoreUpdateStepUtil(RepositoryLocationResolver locationResolver, SCMContextProvider contextProvider, StoreParameters parameters, StoreType type) {
this.locationResolver = locationResolver;
this.contextProvider = contextProvider;
this.parameters = parameters;
this.type = type;
}
@Override
public void renameStore(String newName) {
Path oldStorePath = resolveBasePath().resolve(parameters.getName());
if (Files.exists(oldStorePath)) {
Path newStorePath = resolveBasePath().resolve(newName);
try {
Files.move(oldStorePath, newStorePath);
} catch (IOException e) {
throw new UpdateException(String.format("Could not move store path %s to %s", oldStorePath, newStorePath), e);
}
}
}
@Override
public void deleteStore() {
Path oldStorePath = resolveBasePath().resolve(parameters.getName());
IOUtil.deleteSilently(oldStorePath.toFile());
}
private Path resolveBasePath() {
Path basePath;
if (parameters.getRepositoryId() != null) {
basePath = locationResolver.forClass(Path.class).getLocation(parameters.getRepositoryId());
} else {
basePath = contextProvider.getBaseDirectory().toPath();
}
Path storeBasePath;
if (parameters.getRepositoryId() == null) {
storeBasePath = basePath.resolve(Store.forStoreType(type).getGlobalStoreDirectory());
} else {
storeBasePath = basePath.resolve(Store.forStoreType(type).getRepositoryStoreDirectory());
}
return storeBasePath;
}
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.SCMContextProvider;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.update.StoreUpdateStepUtilFactory;
import javax.inject.Inject;
public class FileStoreUpdateStepUtilFactory implements StoreUpdateStepUtilFactory {
private final RepositoryLocationResolver locationResolver;
private final SCMContextProvider contextProvider;
@Inject
public FileStoreUpdateStepUtilFactory(RepositoryLocationResolver locationResolver, SCMContextProvider contextProvider) {
this.locationResolver = locationResolver;
this.contextProvider = contextProvider;
}
@Override
public StoreUpdateStepUtil build(StoreType type, StoreParameters parameters) {
return new FileStoreUpdateStepUtil(locationResolver, contextProvider, parameters, type);
}
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import javax.inject.Inject;
import java.nio.file.Path;
public class RepositoryStoreImporter implements StoreImporter {
private final RepositoryLocationResolver locationResolver;
@Inject
public RepositoryStoreImporter(RepositoryLocationResolver locationResolver) {
this.locationResolver = locationResolver;
}
@Override
public StoreEntryImporterFactory doImport(Repository repository) {
Path storeLocation = locationResolver
.forClass(Path.class)
.getLocation(repository.getId());
return new FileBasedStoreEntryImporterFactory(storeLocation);
}
}

View File

@@ -21,18 +21,32 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import java.io.File;
public enum Store {
enum Store {
CONFIG("config"),
DATA("data"),
BLOB("blob");
private static final String GLOBAL_STORE_BASE_DIRECTORY = "var";
private static final String STORE_DIRECTORY = "store";
static final String STORE_DIRECTORY = "store";
static Store forStoreType(StoreType storeType) {
switch (storeType) {
case BLOB:
return BLOB;
case DATA:
return DATA;
case CONFIG:
case CONFIG_ENTRY:
return CONFIG;
default:
throw new IllegalArgumentException("unknown store type: " + storeType);
}
}
private String directory;

View File

@@ -0,0 +1,144 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ExportableFileStoreTest {
@Mock
Exporter exporter;
@Test
void shouldNotPutContentIfNoFilesExists(@TempDir Path temp) throws IOException {
Path dataStoreDir = temp.resolve("some-store");
Files.createDirectories(dataStoreDir);
ExportableStore exportableFileStore = new ExportableDataFileStore(dataStoreDir);
exportableFileStore.export(exporter);
verify(exporter, never()).put(anyString(), anyLong());
}
@Test
void shouldPutContentIntoExporterForDataStore(@TempDir Path temp) throws IOException {
createFile(temp, "data", "trace", "first.xml");
createFile(temp, "data", "trace", "second.xml");
ByteArrayOutputStream os = new ByteArrayOutputStream();
ExportableStore exportableFileStore = new ExportableDataFileStore(temp.resolve("data").resolve("trace"));
when(exporter.put(anyString(), anyLong())).thenReturn(os);
exportableFileStore.export(exporter);
verify(exporter).put(eq("first.xml"), anyLong());
verify(exporter).put(eq("second.xml"), anyLong());
assertThat(os.toString()).isNotBlank();
}
@Test
void shouldPutContentIntoExporterForConfigStore(@TempDir Path temp) throws IOException {
createFile(temp, "config", "", "first.xml");
ByteArrayOutputStream os = new ByteArrayOutputStream();
ExportableStore exportableConfigFileStore = new ExportableConfigFileStore(temp.resolve("config").resolve("first.xml"));
when(exporter.put(anyString(), anyLong())).thenReturn(os);
exportableConfigFileStore.export(exporter);
verify(exporter).put(eq("first.xml"), anyLong());
assertThat(os.toString()).isNotBlank();
}
@Test
void shouldFilterNoneConfigFiles(@TempDir Path temp) throws IOException {
createFile(temp, "config", "", "first.bck");
ExportableStore exportableConfigFileStore = new ExportableConfigFileStore(temp.resolve("config").resolve("first.bck"));
exportableConfigFileStore.export(exporter);
verify(exporter, never()).put(anyString(), anyLong());
}
@Test
void shouldPutContentIntoExporterForBlobStore(@TempDir Path temp) throws IOException {
createFile(temp, "blob", "assets", "first.blob");
ByteArrayOutputStream os = new ByteArrayOutputStream();
Exporter exporter = mock(Exporter.class);
ExportableStore exportableBlobFileStore = new ExportableBlobFileStore(temp.resolve("blob").resolve("assets"));
when(exporter.put(anyString(), anyLong())).thenReturn(os);
exportableBlobFileStore.export(exporter);
verify(exporter).put(eq("first.blob"), anyLong());
assertThat(os.toString()).isNotBlank();
}
@Test
void shouldSkipFilteredBlobFiles(@TempDir Path temp) throws IOException {
createFile(temp, "blob", "security", "second.xml");
ByteArrayOutputStream os = new ByteArrayOutputStream();
Exporter exporter = mock(Exporter.class);
ExportableStore exportableBlobFileStore = new ExportableBlobFileStore(temp.resolve("blob").resolve("security"));
exportableBlobFileStore.export(exporter);
verify(exporter, never()).put(anyString(), anyLong());
assertThat(os.toString()).isBlank();
}
private File createFile(Path temp, String type, String name, String fileName) throws IOException {
Path path = name != null ? temp.resolve(type).resolve(name) : temp.resolve(type);
new File(path.toUri()).mkdirs();
File file = new File(path.toFile(), fileName);
if (!file.exists()) {
file.createNewFile();
}
FileWriter source = new FileWriter(file);
source.write("something");
source.close();
return file;
}
}

View File

@@ -0,0 +1,50 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class FileBasedStoreEntryImporterFactoryTest {
@Test
void shouldCreateStoreEntryImporterForDataStore(@TempDir Path temp) {
FileBasedStoreEntryImporterFactory factory = new FileBasedStoreEntryImporterFactory(temp);
FileBasedStoreEntryImporter dataImporter = (FileBasedStoreEntryImporter) factory.importStore(new StoreEntryMetaData(StoreType.DATA, "hitchhiker"));
assertThat(dataImporter.getDirectory()).isEqualTo(temp.resolve("store").resolve("data").resolve("hitchhiker"));
}
@Test
void shouldCreateStoreEntryImporterForConfigStore(@TempDir Path temp) {
FileBasedStoreEntryImporterFactory factory = new FileBasedStoreEntryImporterFactory(temp);
FileBasedStoreEntryImporter configImporter = (FileBasedStoreEntryImporter) factory.importStore(new StoreEntryMetaData(StoreType.CONFIG, ""));
assertThat(configImporter.getDirectory()).isEqualTo(temp.resolve("store").resolve("config"));
}
}

View File

@@ -0,0 +1,50 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.assertj.core.api.Assertions.assertThat;
class FileBasedStoreEntryImporterTest {
@Test
void shouldCreateFileFromInputStream(@TempDir Path temp) {
FileBasedStoreEntryImporter importer = new FileBasedStoreEntryImporter(temp);
String fileName = "testStore.xml";
importer.importEntry(fileName, new ByteArrayInputStream("testdata".getBytes()));
assertThat(Files.exists(temp.resolve(fileName))).isTrue();
assertThat(temp.resolve(fileName)).hasContent("testdata");
}
}

View File

@@ -0,0 +1,90 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.RepositoryTestData;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class FileStoreExporterTest {
private static final Repository REPOSITORY = RepositoryTestData.create42Puzzle();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RepositoryLocationResolver resolver;
@InjectMocks
private FileStoreExporter fileStoreExporter;
@Test
void shouldReturnEmptyList(@TempDir Path temp) {
when(resolver.forClass(Path.class).getLocation(REPOSITORY.getId())).thenReturn(temp);
List<ExportableStore> exportableStores = fileStoreExporter.listExportableStores(REPOSITORY);
assertThat(exportableStores).isEmpty();
}
@Test
void shouldReturnListOfExportableStores(@TempDir Path temp) throws IOException {
Path storePath = temp.resolve("store");
createFile(storePath, StoreType.CONFIG.getValue(), null, "first.xml");
createFile(storePath, StoreType.DATA.getValue(), "ci", "second.xml");
createFile(storePath, StoreType.DATA.getValue(), "jenkins", "third.xml");
when(resolver.forClass(Path.class).getLocation(REPOSITORY.getId())).thenReturn(temp);
List<ExportableStore> exportableStores = fileStoreExporter.listExportableStores(REPOSITORY);
assertThat(exportableStores).hasSize(3);
assertThat(exportableStores.stream().filter(e -> e.getMetaData().getType().equals(StoreType.CONFIG))).hasSize(1);
assertThat(exportableStores.stream().filter(e -> e.getMetaData().getType().equals(StoreType.DATA))).hasSize(2);
}
private void createFile(Path storePath, String type, String name, String fileName) throws IOException {
Path path = name != null ? storePath.resolve(type).resolve(name) : storePath.resolve(type);
Files.createDirectories(path);
Path file = path.resolve(fileName);
if (!Files.exists(file)) {
Files.createFile(file);
}
Files.write(file, Collections.singletonList("something"));
}
}

View File

@@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.update.StoreUpdateStepUtilFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.lenient;
@ExtendWith(MockitoExtension.class)
class FileStoreUpdateStepUtilFactoryTest {
@Mock
private RepositoryLocationResolver locationResolver;
@Mock
private RepositoryLocationResolver.RepositoryLocationResolverInstance locationResolverInstance;
@Mock
private SCMContextProvider contextProvider;
@InjectMocks
private FileStoreUpdateStepUtilFactory factory;
private Path globalPath;
private Path repositoryPath;
@BeforeEach
void initPaths(@TempDir Path temp) throws IOException {
globalPath = temp.resolve("global");
Files.createDirectories(globalPath);
lenient().doReturn(globalPath.toFile()).when(contextProvider).getBaseDirectory();
repositoryPath = temp.resolve("repo");
Files.createDirectories(repositoryPath);
lenient().doReturn(true).when(locationResolver).supportsLocationType(Path.class);
lenient().doReturn(locationResolverInstance).when(locationResolver).forClass(Path.class);
lenient().doReturn(repositoryPath).when(locationResolverInstance).getLocation("repo-id");
}
@Test
void shouldMoveGlobalDataDirectory() throws IOException {
Path dataPath = globalPath.resolve("var").resolve("data");
Files.createDirectories(dataPath.resolve("something"));
Files.createFile(dataPath.resolve("something").resolve("some.file"));
StoreUpdateStepUtilFactory.StoreUpdateStepUtil util =
factory
.forType(StoreType.DATA)
.forName("something")
.build();
util.renameStore("new-name");
assertThat(dataPath.resolve("new-name").resolve("some.file")).exists();
assertThat(dataPath.resolve("something")).doesNotExist();
}
@Test
void shouldMoveRepositoryDataDirectory() throws IOException {
Path dataPath = repositoryPath.resolve("store").resolve("data");
Files.createDirectories(dataPath.resolve("something"));
Files.createFile(dataPath.resolve("something").resolve("some.file"));
StoreUpdateStepUtilFactory.StoreUpdateStepUtil util =
factory
.forType(StoreType.DATA)
.forName("something")
.forRepository("repo-id")
.build();
util.renameStore("new-name");
assertThat(dataPath.resolve("new-name").resolve("some.file")).exists();
assertThat(dataPath.resolve("something")).doesNotExist();
}
@Test
void shouldHandleMissingMoveGlobalDataDirectory() throws IOException {
StoreUpdateStepUtilFactory.StoreUpdateStepUtil util =
factory
.forType(StoreType.DATA)
.forName("something")
.build();
util.renameStore("new-name");
assertThat(globalPath.resolve("new-name")).doesNotExist();
}
}

View File

@@ -0,0 +1,53 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.store;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.RepositoryTestData;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class)
class RepositoryStoreImporterTest {
private static final Repository REPOSITORY = RepositoryTestData.createHeartOfGold();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RepositoryLocationResolver locationResolver;
@InjectMocks
private RepositoryStoreImporter repositoryStoreImporter;
@Test
void shouldImportStore() {
StoreEntryImporterFactory storeEntryImporterFactory = repositoryStoreImporter.doImport(REPOSITORY);
assertThat(storeEntryImporterFactory).isInstanceOf(StoreEntryImporterFactory.class);
}
}

View File

@@ -0,0 +1 @@
mock-maker-inline