Merge branch 'stable-2.15' into stable-2.16
* stable-2.15:
Revert "Remove replication event from pending when runway is allowed"
ReplicationFileBasedConfig: Fix setting default sshConnectionTimeout
SshHelper: Add class javadoc
Make more classes and fields public to ease extensibility
Allow to configure timeout for SSH connection and commands
StartCommand: Fix synchronization on non-final field
Destination: Suppress FutureReturnValueIgnored warning
Change-Id: I0cbbf0db0bb8d6b44e7f86ec7ad3e0f2067f3a6d
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 5257c36..1e74d9a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
@@ -132,4 +132,14 @@
public synchronized void startup(WorkQueue workQueue) {
currentConfig.startup(workQueue);
}
+
+ @Override
+ public synchronized int getSshConnectionTimeout() {
+ return currentConfig.getSshConnectionTimeout();
+ }
+
+ @Override
+ public synchronized int getSshCommandTimeout() {
+ return currentConfig.getSshCommandTimeout();
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
index 02304f3..969f8e7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -69,6 +69,7 @@
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.apache.commons.io.FilenameUtils;
@@ -392,7 +393,9 @@
e = opFactory.create(project, uri);
addRef(e, ref);
e.addState(ref, state);
- pool.schedule(e, now ? 0 : config.getDelay(), TimeUnit.SECONDS);
+ @SuppressWarnings("unused")
+ ScheduledFuture<?> ignored =
+ pool.schedule(e, now ? 0 : config.getDelay(), TimeUnit.SECONDS);
pending.put(uri, e);
} else if (!e.getRefs().contains(ref)) {
addRef(e, ref);
@@ -493,7 +496,9 @@
pending.put(uri, pushOp);
switch (reason) {
case COLLISION:
- pool.schedule(pushOp, config.getRescheduleDelay(), TimeUnit.SECONDS);
+ @SuppressWarnings("unused")
+ ScheduledFuture<?> ignored =
+ pool.schedule(pushOp, config.getRescheduleDelay(), TimeUnit.SECONDS);
break;
case TRANSPORT_ERROR:
case REPOSITORY_MISSING:
@@ -505,7 +510,9 @@
postReplicationFailedEvent(pushOp, status);
if (pushOp.setToRetry()) {
postReplicationScheduledEvent(pushOp);
- pool.schedule(pushOp, config.getRetryDelay(), TimeUnit.MINUTES);
+ @SuppressWarnings("unused")
+ ScheduledFuture<?> ignored2 =
+ pool.schedule(pushOp, config.getRetryDelay(), TimeUnit.MINUTES);
} else {
pushOp.canceledByReplication();
pending.remove(uri);
@@ -524,11 +531,11 @@
if (op.wasCanceled()) {
return RunwayStatus.canceled();
}
+ pending.remove(op.getURI());
PushOne inFlightOp = inFlight.get(op.getURI());
if (inFlightOp != null) {
return RunwayStatus.denied(inFlightOp.getId());
}
- pending.remove(op.getURI());
inFlight.put(op.getURI(), op);
}
return RunwayStatus.allowed();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
index 856ffb1..b2d0de2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/DestinationConfiguration.java
@@ -19,7 +19,7 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.transport.RemoteConfig;
-class DestinationConfiguration {
+public class DestinationConfiguration {
static final int DEFAULT_REPLICATION_DELAY = 15;
static final int DEFAULT_RESCHEDULE_DELAY = 3;
@@ -40,7 +40,7 @@
private final RemoteConfig remoteConfig;
private final int maxRetries;
- DestinationConfiguration(RemoteConfig remoteConfig, Config cfg) {
+ protected DestinationConfiguration(RemoteConfig remoteConfig, Config cfg) {
this.remoteConfig = remoteConfig;
String name = remoteConfig.getName();
urls = ImmutableList.copyOf(cfg.getStringList("remote", name, "url"));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
index 364f1b4..fccdb7b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
@@ -21,13 +21,13 @@
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
public class RefReplicatedEvent extends RefEvent {
- static final String TYPE = "ref-replicated";
+ public static final String TYPE = "ref-replicated";
- final String project;
- final String ref;
- final String targetNode;
- final String status;
- final Status refStatus;
+ public final String project;
+ public final String ref;
+ public final String targetNode;
+ public final String status;
+ public final Status refStatus;
public RefReplicatedEvent(
String project,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
index 90595b3..4789a96 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
@@ -18,7 +18,7 @@
import com.google.gerrit.server.events.RefEvent;
public class RefReplicationDoneEvent extends RefEvent {
- static final String TYPE = "ref-replication-done";
+ public static final String TYPE = "ref-replication-done";
final String project;
final String ref;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationConfig.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationConfig.java
index 9693e2d..c9531e3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationConfig.java
@@ -38,4 +38,8 @@
int shutdown();
void startup(WorkQueue workQueue);
+
+ int getSshConnectionTimeout();
+
+ int getSshCommandTimeout();
}
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 46d2f66..d1be563 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
@@ -13,6 +13,8 @@
// limitations under the License.
package com.googlesource.gerrit.plugins.replication;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
@@ -20,6 +22,7 @@
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.annotations.PluginData;
+import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.WorkQueue;
import com.google.inject.Inject;
@@ -42,12 +45,15 @@
@Singleton
public class ReplicationFileBasedConfig implements ReplicationConfig {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final int DEFAULT_SSH_CONNECTION_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
private List<Destination> destinations;
private final SitePaths site;
private Path cfgPath;
private boolean replicateAllOnPluginStart;
private boolean defaultForceUpdate;
+ private int sshCommandTimeout;
+ private int sshConnectionTimeout = DEFAULT_SSH_CONNECTION_TIMEOUT_MS;
private final FileBasedConfig config;
private final Path pluginDataDir;
@@ -111,6 +117,18 @@
defaultForceUpdate = config.getBoolean("gerrit", "defaultForceUpdate", false);
+ sshCommandTimeout =
+ (int) ConfigUtil.getTimeUnit(config, "gerrit", null, "sshCommandTimeout", 0, SECONDS);
+ sshConnectionTimeout =
+ (int)
+ ConfigUtil.getTimeUnit(
+ config,
+ "gerrit",
+ null,
+ "sshConnectionTimeout",
+ DEFAULT_SSH_CONNECTION_TIMEOUT_MS,
+ MILLISECONDS);
+
ImmutableList.Builder<Destination> dest = ImmutableList.builder();
for (RemoteConfig c : allRemotes(config)) {
if (c.getURIs().isEmpty()) {
@@ -219,4 +237,14 @@
cfg.start(workQueue);
}
}
+
+ @Override
+ public int getSshConnectionTimeout() {
+ return sshConnectionTimeout;
+ }
+
+ @Override
+ public int getSshCommandTimeout() {
+ return sshCommandTimeout;
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationScheduledEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationScheduledEvent.java
index 301219f..aa965fe 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationScheduledEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationScheduledEvent.java
@@ -18,11 +18,11 @@
import com.google.gerrit.server.events.RefEvent;
public class ReplicationScheduledEvent extends RefEvent {
- static final String TYPE = "ref-replication-scheduled";
+ public static final String TYPE = "ref-replication-scheduled";
- final String project;
- final String ref;
- final String targetNode;
+ public final String project;
+ public final String ref;
+ public final String targetNode;
public ReplicationScheduledEvent(String project, String ref, String targetNode) {
super(TYPE);
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 cfa95dd..f2d55de 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationStateLogger.java
@@ -26,7 +26,7 @@
* state to the stderr console.
*/
@Singleton
-class ReplicationStateLogger implements ReplicationStateListener {
+public class ReplicationStateLogger implements ReplicationStateListener {
@Override
public void warn(String msg, ReplicationState... states) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/SshHelper.java b/src/main/java/com/googlesource/gerrit/plugins/replication/SshHelper.java
index 68e9652..d73c101 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/SshHelper.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/SshHelper.java
@@ -25,19 +25,23 @@
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.io.StreamCopyThread;
-class SshHelper {
- private static final int SSH_REMOTE_TIMEOUT = 120 * 1000; // 2 minutes = 120 * 1000ms
-
+/** Utility class that provides SSH access to remote URIs. */
+public class SshHelper {
private final Provider<SshSessionFactory> sshSessionFactoryProvider;
+ private final int commandTimeout;
+ private final int connectionTimeout;
@Inject
- SshHelper(Provider<SshSessionFactory> sshSessionFactoryProvider) {
+ protected SshHelper(
+ ReplicationConfig replicationConfig, Provider<SshSessionFactory> sshSessionFactoryProvider) {
this.sshSessionFactoryProvider = sshSessionFactoryProvider;
+ this.commandTimeout = replicationConfig.getSshCommandTimeout();
+ this.connectionTimeout = replicationConfig.getSshConnectionTimeout();
}
- int executeRemoteSsh(URIish uri, String cmd, OutputStream errStream) throws IOException {
+ public int executeRemoteSsh(URIish uri, String cmd, OutputStream errStream) throws IOException {
RemoteSession ssh = connect(uri);
- Process proc = ssh.exec(cmd, 0);
+ Process proc = ssh.exec(cmd, commandTimeout);
proc.getOutputStream().close();
StreamCopyThread out = new StreamCopyThread(proc.getInputStream(), errStream);
StreamCopyThread err = new StreamCopyThread(proc.getErrorStream(), errStream);
@@ -54,7 +58,7 @@
return proc.exitValue();
}
- OutputStream newErrorBufferStream() {
+ public OutputStream newErrorBufferStream() {
return new OutputStream() {
private final StringBuilder out = new StringBuilder();
private final StringBuilder line = new StringBuilder();
@@ -83,7 +87,7 @@
};
}
- RemoteSession connect(URIish uri) throws TransportException {
- return sshSessionFactoryProvider.get().getSession(uri, null, FS.DETECTED, SSH_REMOTE_TIMEOUT);
+ protected RemoteSession connect(URIish uri) throws TransportException {
+ return sshSessionFactoryProvider.get().getSession(uri, null, FS.DETECTED, connectionTimeout);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java
index 77bc285..20bdf65 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/StartCommand.java
@@ -53,6 +53,8 @@
@Inject private ReplicationState.Factory replicationStateFactory;
+ private final Object lock = new Object();
+
@Override
protected void run() throws Failure {
if (all && projectPatterns.size() > 0) {
@@ -100,7 +102,7 @@
public void writeStdOutSync(String message) {
if (wait) {
- synchronized (stdout) {
+ synchronized (lock) {
stdout.println(message);
stdout.flush();
}
@@ -109,7 +111,7 @@
public void writeStdErrSync(String message) {
if (wait) {
- synchronized (stderr) {
+ synchronized (lock) {
stderr.println(message);
stderr.flush();
}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 2359599..417b2ad 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -82,6 +82,14 @@
: If true, the default push refspec will be set to use forced
update to the remote when no refspec is given. By default, false.
+gerrit.sshCommandTimeout
+: Timeout for SSH command execution. If 0, there is no timeout and
+ the client waits indefinitely. By default, 0.
+
+gerrit.sshConnectionTimeout
+: Timeout for SSH connections. If 0, there is no timeout and
+ the client waits indefinitely. By default, 2 minutes.
+
replication.lockErrorMaxRetries
: Number of times to retry a replication operation if a lock
error is detected.