blob: 53d33edfd6ae273eb830e81ea96a4d3a8df39cb6 [file] [log] [blame]
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.ericsson.gerrit.plugins.highavailability;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.CLUSTER_NAME_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_CLUSTER_NAME;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_SHARED_DIRECTORY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGROUPS_SUBSECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.MAIN_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.PEER_INFO_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.SHARED_DIRECTORY_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.STRATEGY_KEY;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.FileUtil;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
class SetupLocalHAReplica {
private final SitePaths master;
private final FileBasedConfig masterConfig;
private Path sharedDir;
private SitePaths replica;
@Inject
SetupLocalHAReplica(SitePaths master, InitFlags flags) {
this.master = master;
this.masterConfig = flags.cfg;
this.sharedDir = master.site_path.resolve(DEFAULT_SHARED_DIRECTORY);
}
void run(SitePaths replica, FileBasedConfig pluginConfig) throws Exception {
this.replica = replica;
FileUtil.mkdirsOrDie(replica.site_path, "cannot create " + replica.site_path);
configureMainSection(pluginConfig);
configurePeerInfo(pluginConfig);
for (Path dir : listDirsForCopy()) {
copyFiles(dir);
}
mkdir(replica.logs_dir);
mkdir(replica.tmp_dir);
symlink(Paths.get(masterConfig.getString("gerrit", null, "basePath")));
symlink(sharedDir);
FileBasedConfig replicaConfig =
new FileBasedConfig(replica.gerrit_config.toFile(), FS.DETECTED);
replicaConfig.load();
if ("h2".equals(masterConfig.getString("database", null, "type"))) {
masterConfig.setBoolean("database", "h2", "autoServer", true);
replicaConfig.setBoolean("database", "h2", "autoServer", true);
symlinkH2ReviewDbDir();
}
}
private List<Path> listDirsForCopy() throws IOException {
ImmutableList.Builder<Path> toSkipBuilder = ImmutableList.builder();
toSkipBuilder.add(
master.resolve(masterConfig.getString("gerrit", null, "basePath")),
master.db_dir,
master.logs_dir,
replica.site_path,
master.site_path.resolve(sharedDir),
master.tmp_dir);
if ("h2".equals(masterConfig.getString("database", null, "type"))) {
toSkipBuilder.add(
master.resolve(masterConfig.getString("database", null, "database")).getParent());
}
final ImmutableList<Path> toSkip = toSkipBuilder.build();
final ArrayList<Path> dirsForCopy = new ArrayList<>();
Files.walkFileTree(
master.site_path,
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
if (Files.isSameFile(dir, master.site_path)) {
return FileVisitResult.CONTINUE;
}
Path p = master.site_path.relativize(dir);
if (shouldSkip(p)) {
return FileVisitResult.SKIP_SUBTREE;
}
dirsForCopy.add(p);
return FileVisitResult.CONTINUE;
}
private boolean shouldSkip(Path p) throws IOException {
Path resolved = master.site_path.resolve(p);
for (Path skip : toSkip) {
if (Files.exists(skip) && Files.isSameFile(resolved, skip)) {
return true;
}
}
return false;
}
});
return dirsForCopy;
}
private void copyFiles(Path dir) throws IOException {
final Path source = master.site_path.resolve(dir);
final Path target = replica.site_path.resolve(dir);
Files.createDirectories(target);
Files.walkFileTree(
source,
EnumSet.noneOf(FileVisitOption.class),
1,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Path f = source.relativize(file);
if (Files.isRegularFile(file)) {
Files.copy(file, target.resolve(f));
}
return FileVisitResult.CONTINUE;
}
});
}
private static void mkdir(Path dir) throws IOException {
Files.createDirectories(dir);
}
private void symlink(Path path) throws IOException {
if (!path.isAbsolute()) {
Files.createSymbolicLink(
replica.site_path.resolve(path),
master.site_path.resolve(path).toAbsolutePath().normalize());
}
}
private void symlinkH2ReviewDbDir() throws IOException {
symlink(Paths.get(masterConfig.getString("database", null, "database")).getParent());
}
private void configureMainSection(FileBasedConfig pluginConfig) throws IOException {
pluginConfig.setString(
MAIN_SECTION,
null,
SHARED_DIRECTORY_KEY,
master.site_path.relativize(sharedDir).toString());
pluginConfig.save();
}
private void configurePeerInfo(FileBasedConfig pluginConfig) throws IOException {
pluginConfig.setString(PEER_INFO_SECTION, null, STRATEGY_KEY, "jgroups");
pluginConfig.setString(
PEER_INFO_SECTION, JGROUPS_SUBSECTION, CLUSTER_NAME_KEY, DEFAULT_CLUSTER_NAME);
pluginConfig.save();
}
}