Merge branch 'stable-2.11'
* stable-2.11:
Emit replication status events after initial full sync
Fix creation of missing repositories
Change-Id: I6a3dedcca8af777c2080c3bac9a4d0f80f0bb6f5
diff --git a/BUCK b/BUCK
index 2e8a623..8a84334 100644
--- a/BUCK
+++ b/BUCK
@@ -18,6 +18,7 @@
java_test(
name = 'replication_tests',
srcs = glob(['src/test/java/**/*.java']),
+ labels = ['replication'],
deps = [
':replication__plugin',
'//gerrit-common:server',
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
index 62cad2c..6b1687c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.googlesource.gerrit.plugins.replication;
+import com.google.gerrit.common.FileUtil;
import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.SitePaths;
@@ -57,10 +58,14 @@
this.gitRepositoryManager = grm;
this.groupBackend = gb;
this.currentConfig = loadConfig();
- this.currentConfigTs = currentConfig.getCfgPath().lastModified();
+ this.currentConfigTs = getLastModified(currentConfig);
this.workQueue = workQueue;
}
+ private static long getLastModified(ReplicationFileBasedConfig cfg) {
+ return FileUtil.lastModified(cfg.getCfgPath());
+ }
+
private ReplicationFileBasedConfig loadConfig()
throws ConfigInvalidException, IOException {
return new ReplicationFileBasedConfig(injector, site,
@@ -79,25 +84,27 @@
}
private void reloadIfNeeded() {
- if (isAutoReload()
- && currentConfig.getCfgPath().lastModified() > currentConfigTs) {
- try {
- ReplicationFileBasedConfig newConfig = loadConfig();
- newConfig.startup(workQueue);
- int discarded = currentConfig.shutdown();
+ try {
+ if (isAutoReload()) {
+ long lastModified = getLastModified(currentConfig);
+ if (lastModified > currentConfigTs) {
+ ReplicationFileBasedConfig newConfig = loadConfig();
+ newConfig.startup(workQueue);
+ int discarded = currentConfig.shutdown();
- this.currentConfig = newConfig;
- this.currentConfigTs = currentConfig.getCfgPath().lastModified();
- log.info("Configuration reloaded: "
+ this.currentConfig = newConfig;
+ this.currentConfigTs = lastModified;
+ log.info("Configuration reloaded: "
+ currentConfig.getDestinations(FilterType.ALL).size() + " destinations, "
+ discarded + " replication events discarded");
- } catch (Exception e) {
- log.error(
- "Cannot reload replication configuration: keeping existing settings",
- e);
- return;
+ }
}
+ } catch (Exception e) {
+ log.error(
+ "Cannot reload replication configuration: keeping existing settings",
+ e);
+ return;
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadSecureCredentialsFactoryDecorator.java b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadSecureCredentialsFactoryDecorator.java
index 4017822..3a0cc3f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadSecureCredentialsFactoryDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadSecureCredentialsFactoryDecorator.java
@@ -11,18 +11,20 @@
// 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.googlesource.gerrit.plugins.replication;
+import static com.google.gerrit.common.FileUtil.lastModified;
+
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicReference;
public class AutoReloadSecureCredentialsFactoryDecorator implements
@@ -47,32 +49,29 @@
}
private long getSecureConfigLastEditTs() {
- FileBasedConfig cfg = new FileBasedConfig(site.secure_config, FS.DETECTED);
- if (cfg.getFile().exists()) {
- return cfg.getFile().lastModified();
- } else {
+ if (!Files.exists(site.secure_config)) {
return 0L;
}
+ return lastModified(site.secure_config);
}
@Override
public SecureCredentialsProvider create(String remoteName) {
- if (needsReload()) {
- try {
+ try {
+ if (needsReload()) {
secureCredentialsFactory.compareAndSet(secureCredentialsFactory.get(),
new SecureCredentialsFactory(site));
secureCredentialsFactoryLoadTs = getSecureConfigLastEditTs();
log.info("secure.config reloaded as it was updated on the file system");
- } catch (Exception e) {
- log.error("Unexpected error while trying to reload "
- + "secure.config: keeping existing credentials", e);
}
+ } catch (Exception e) {
+ log.error("Unexpected error while trying to reload "
+ + "secure.config: keeping existing credentials", e);
}
return secureCredentialsFactory.get().create(remoteName);
}
-
private boolean needsReload() {
return config.getConfig().getBoolean("gerrit", "autoReload", false) &&
getSecureConfigLastEditTs() != secureCredentialsFactoryLoadTs;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
index 0614959..32ffcbd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
@@ -14,6 +14,8 @@
package com.googlesource.gerrit.plugins.replication;
+import static com.googlesource.gerrit.plugins.replication.ReplicationQueue.repLog;
+
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;
import com.google.common.collect.LinkedListMultimap;
@@ -59,7 +61,6 @@
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
-import org.slf4j.Logger;
import org.slf4j.MDC;
import java.io.IOException;
@@ -79,8 +80,7 @@
* take that lock to ensure they are working with a current view of the object.
*/
class PushOne implements ProjectRunnable {
- private static final Logger repLog = ReplicationQueue.repLog;
- private static final ReplicationStateLogger stateLog =
+ private static final ReplicationStateListener stateLog =
new ReplicationStateLogger(repLog);
static final String ALL_REFS = "..all..";
static final String ID_MDC_KEY = "pushOneId";
@@ -611,7 +611,7 @@
stateMap.clear();
}
- public class LockFailureException extends TransportException {
+ public static class LockFailureException extends TransportException {
private static final long serialVersionUID = 1L;
public LockFailureException(URIish uri, String message) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
index f56185d..c360f75 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
@@ -35,9 +35,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -46,7 +46,7 @@
public class ReplicationFileBasedConfig implements ReplicationConfig {
static final Logger log = LoggerFactory.getLogger(ReplicationFileBasedConfig.class);
private List<Destination> destinations;
- private File cfgPath;
+ private Path cfgPath;
private boolean replicateAllOnPluginStart;
private boolean defaultForceUpdate;
private Injector injector;
@@ -61,13 +61,13 @@
final RemoteSiteUser.Factory ruf, final PluginUser pu,
final GitRepositoryManager grm,
final GroupBackend gb) throws ConfigInvalidException, IOException {
- this.cfgPath = new File(site.etc_dir, "replication.config");
+ this.cfgPath = site.etc_dir.resolve("replication.config");
this.injector = injector;
this.replicationUserFactory = ruf;
this.pluginUser = pu;
this.gitRepositoryManager = grm;
this.groupBackend = gb;
- this.config = new FileBasedConfig(cfgPath, FS.DETECTED);
+ this.config = new FileBasedConfig(cfgPath.toFile(), FS.DETECTED);
this.destinations = allDestinations();
}
@@ -215,7 +215,7 @@
return destinations.isEmpty();
}
- File getCfgPath() {
+ Path getCfgPath() {
return cfgPath;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
index 95693d9..ea5be66 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -63,7 +63,7 @@
static final String REPLICATION_LOG_NAME = "replication_log";
static final Logger repLog = LoggerFactory.getLogger(REPLICATION_LOG_NAME);
private static final int SSH_REMOTE_TIMEOUT = 120 * 1000;
- private static final ReplicationStateLogger stateLog =
+ private static final ReplicationStateListener stateLog =
new ReplicationStateLogger(repLog);
static String replaceName(String in, String name, boolean keyIsOptional) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateListener.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateListener.java
new file mode 100644
index 0000000..e5ac9d5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateListener.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.replication;
+
+/**
+ * Interface for notifying replication status updates.
+ */
+public interface ReplicationStateListener {
+
+ /**
+ * Notify a non-fatal replication error.
+ *
+ * Replication states received a non-fatal error with an associated
+ * warning message.
+ *
+ * @param msg message description of the error
+ * @param states replication states impacted
+ */
+ public abstract void warn(String msg, ReplicationState... states);
+
+ /**
+ * Notify a fatal replication error.
+ *
+ * Replication states have received a fatal error and replication has
+ * failed.
+ *
+ * @param msg message description of the error
+ * @param states replication states impacted
+ */
+ public abstract void error(String msg, ReplicationState... states);
+
+ /**
+ * Notify a fatal replication error with the associated exception.
+ *
+ * Replication states have received a fatal exception and replication has failed.
+ *
+ * @param msg message description of the error
+ * @param t exception that caused the replication to fail
+ * @param states replication states impacted
+ */
+ public abstract void error(String msg, Throwable t,
+ ReplicationState... states);
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java
index cb1d4ce..a1bc5a4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java
@@ -24,7 +24,7 @@
* and logs additional information about the replication state to the
* stderr console.
*/
-public class ReplicationStateLogger {
+public class ReplicationStateLogger implements ReplicationStateListener {
private final Logger logger;
@@ -32,21 +32,19 @@
this.logger = logger;
}
+ @Override
public void warn(String msg, ReplicationState... states) {
stateWriteErr("Warning: " + msg, states);
logger.warn(msg);
}
- public void warn(String msg, Throwable t, ReplicationState... states) {
- stateWriteErr("Warning: " + msg, states);
- logger.warn(msg, t);
- }
-
+ @Override
public void error(String msg, ReplicationState... states) {
stateWriteErr("Error: " + msg, states);
logger.error(msg);
}
+ @Override
public void error(String msg, Throwable t, ReplicationState... states) {
stateWriteErr("Error: " + msg, states);
logger.error(msg, t);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/SecureCredentialsFactory.java b/src/main/java/com/googlesource/gerrit/plugins/replication/SecureCredentialsFactory.java
index 32d3905..a10f62f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/SecureCredentialsFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/SecureCredentialsFactory.java
@@ -36,7 +36,8 @@
private static Config load(SitePaths site)
throws ConfigInvalidException, IOException {
- FileBasedConfig cfg = new FileBasedConfig(site.secure_config, FS.DETECTED);
+ FileBasedConfig cfg =
+ new FileBasedConfig(site.secure_config.toFile(), FS.DETECTED);
if (cfg.getFile().exists() && cfg.getFile().length() > 0) {
try {
cfg.load();