summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Miller <marco.miller@ericsson.com>2020-10-06 08:28:15 -0400
committerMarco Miller <marco.miller@ericsson.com>2020-10-06 12:10:25 -0400
commit1f4b2b49bb6401835ce8ab74be6933d7520043b5 (patch)
tree08c67f56b14d3a5aa09bd94f4acf62cc3eedbc47
parentf091a0da13b45e9a6ced6df011c55a1d290cdb2d (diff)
parent32afec8c15da03b2671e6760aa69a83451563fd6 (diff)
Merge branch 'stable-3.0' into stable-3.1
* stable-3.0: Bump Bazel version to 3.5.1 Upgrade jackson-core to 2.11.3 Register graceful shutdown for version command Register graceful shutdown for show queue command Register graceful shutdown for show connections command Register graceful shutdown for show caches command Register graceful shutdown for set reviewers command Register graceful shutdown for set project command Register graceful shutdown for set parent command Register graceful shutdown for set members command Register graceful shutdown for set logging level command Register graceful shutdown for set head command Register graceful shutdown for set account command Register graceful shutdown for review command Register graceful shutdown for rename group command Register graceful shutdown for reload config command Register graceful shutdown for query command Register graceful shutdown for list plugins command Register graceful shutdown for plugin admin commands Register graceful shutdown for list user refs command Register graceful shutdown for list projects command Register graceful shutdown for list members command Register graceful shutdown for list logging level command Register graceful shutdown for list groups command Register graceful shutdown for kill command Register graceful shutdown for index start command Register graceful shutdown for index changes in project command Register graceful shutdown for index changes command Register graceful shutdown for index activate command Register graceful shutdown for gc command Register graceful shutdown for flush caches command Register graceful shutdown for create project command Register graceful shutdown for create group command Register graceful shutdown for create branch command Register graceful shutdown for create account command Register graceful shutdown for close connection command Register graceful shutdown for prolog test commands Register graceful shutdown for ban commit command Register graceful shutdown for apropos command Limit graceful shutdown to SSH sessions serving git requests Update git submodules Change-Id: I519ba25b10209f8856ecf289915c65e5eb8ee09a
-rw-r--r--.bazelversion2
-rw-r--r--java/com/google/gerrit/acceptance/AbstractDaemonTest.java21
-rw-r--r--java/com/google/gerrit/acceptance/GerritServer.java30
-rw-r--r--java/com/google/gerrit/acceptance/SshSession.java16
-rw-r--r--java/com/google/gerrit/acceptance/StandaloneSiteTest.java2
-rw-r--r--java/com/google/gerrit/acceptance/ssh/GracefulCommand.java31
-rw-r--r--java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java31
-rw-r--r--java/com/google/gerrit/acceptance/ssh/TestCommand.java49
-rw-r--r--java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java25
-rw-r--r--java/com/google/gerrit/pgm/Daemon.java8
-rw-r--r--java/com/google/gerrit/sshd/AbstractGitCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/BaseCommand.java4
-rw-r--r--java/com/google/gerrit/sshd/SshDaemon.java27
-rw-r--r--java/com/google/gerrit/sshd/SshSession.java10
-rw-r--r--java/com/google/gerrit/sshd/commands/AproposCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/BanCommitCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CloseConnection.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateAccountCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateBranchCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateGroupCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/FlushCaches.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexActivateCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexChangesCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexStartCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/KillCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListGroupsCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListMembersCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListProjectsCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/LsUserRefs.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/PluginLsCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/Query.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ReloadConfig.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/RenameGroupCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ReviewCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetAccountCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetHeadCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetMembersCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetParentCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetReviewersCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowCaches.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowConnections.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowQueue.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/VersionCommand.java1
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java100
-rw-r--r--tools/nongoogle.bzl4
53 files changed, 381 insertions, 17 deletions
diff --git a/.bazelversion b/.bazelversion
index 1545d96657..d5c0c99142 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.5.0
+3.5.1
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 425bb88e80..8b7f9a0551 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -377,6 +377,15 @@ public abstract class AbstractDaemonTest {
initSsh();
}
+ protected void restart() throws Exception {
+ server = GerritServer.restart(server, createModule(), createSshModule());
+ server.getTestInjector().injectMembers(this);
+ if (resetter != null) {
+ server.getTestInjector().injectMembers(resetter);
+ }
+ initSsh();
+ }
+
protected void evictAndReindexAccount(Account.Id accountId) {
accountCache.evict(accountId);
accountIndexer.index(accountId);
@@ -419,13 +428,16 @@ public abstract class AbstractDaemonTest {
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
Module module = createModule();
+ Module sshModule = createSshModule();
if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
if (commonServer == null) {
- commonServer = GerritServer.initAndStart(temporaryFolder, classDesc, baseConfig, module);
+ commonServer =
+ GerritServer.initAndStart(temporaryFolder, classDesc, baseConfig, module, sshModule);
}
server = commonServer;
} else {
- server = GerritServer.initAndStart(temporaryFolder, methodDesc, baseConfig, module);
+ server =
+ GerritServer.initAndStart(temporaryFolder, methodDesc, baseConfig, module, sshModule);
}
server.getTestInjector().injectMembers(this);
@@ -518,6 +530,11 @@ public abstract class AbstractDaemonTest {
return null;
}
+ /** Override to bind an additional Guice module for SSH injector */
+ public Module createSshModule() {
+ return null;
+ }
+
protected void initSsh() throws Exception {
if (testRequiresSsh
&& SshMode.useSsh()
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 02690ae6b9..be37dd746d 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -312,6 +312,7 @@ public class GerritServer implements AutoCloseable {
* @param desc server description.
* @param baseConfig default config values; merged with config from {@code desc}.
* @param testSysModule additional Guice module to use.
+ * @param testSshModule additional Guice module to use.
* @return started server.
* @throws Exception
*/
@@ -319,14 +320,15 @@ public class GerritServer implements AutoCloseable {
TemporaryFolder temporaryFolder,
Description desc,
Config baseConfig,
- @Nullable Module testSysModule)
+ @Nullable Module testSysModule,
+ @Nullable Module testSshModule)
throws Exception {
Path site = temporaryFolder.newFolder().toPath();
try {
if (!desc.memory()) {
init(desc, baseConfig, site);
}
- return start(desc, baseConfig, site, testSysModule, null);
+ return start(desc, baseConfig, site, testSysModule, testSshModule, null);
} catch (Exception e) {
throw e;
}
@@ -342,6 +344,7 @@ public class GerritServer implements AutoCloseable {
* initialize this directory. Can be retrieved from the returned instance via {@link
* #getSitePath()}.
* @param testSysModule optional additional module to add to the system injector.
+ * @param testSshModule optional additional module to add to the ssh injector.
* @param inMemoryRepoManager {@link InMemoryRepositoryManager} that should be used if the site is
* started in memory
* @param additionalArgs additional command-line arguments for the daemon program; only allowed if
@@ -354,6 +357,7 @@ public class GerritServer implements AutoCloseable {
Config baseConfig,
Path site,
@Nullable Module testSysModule,
+ @Nullable Module testSshModule,
@Nullable InMemoryRepositoryManager inMemoryRepoManager,
String... additionalArgs)
throws Exception {
@@ -376,6 +380,9 @@ public class GerritServer implements AutoCloseable {
if (testSysModule != null) {
daemon.addAdditionalSysModuleForTesting(testSysModule);
}
+ if (testSshModule != null) {
+ daemon.addAdditionalSshModuleForTesting(testSshModule);
+ }
daemon.setEnableSshd(desc.useSsh());
if (desc.memory()) {
@@ -596,7 +603,24 @@ public class GerritServer implements AutoCloseable {
server.close();
server.daemon.stop();
- return start(server.desc, cfg, site, null, inMemoryRepoManager);
+ return start(server.desc, cfg, site, null, null, inMemoryRepoManager);
+ }
+
+ public static GerritServer restart(
+ GerritServer server, @Nullable Module testSysModule, @Nullable Module testSshModule)
+ throws Exception {
+ checkState(server.desc.sandboxed(), "restarting as slave requires @Sandboxed");
+ Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
+ Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class));
+
+ InMemoryRepositoryManager inMemoryRepoManager = null;
+ if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) {
+ inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class);
+ }
+
+ server.close();
+ server.daemon.stop();
+ return start(server.desc, cfg, site, testSysModule, testSshModule, inMemoryRepoManager);
}
private static boolean hasBinding(Injector injector, Class<?> clazz) {
diff --git a/java/com/google/gerrit/acceptance/SshSession.java b/java/com/google/gerrit/acceptance/SshSession.java
index fa0bc90c7e..fd60d16ed3 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -66,6 +66,22 @@ public class SshSession {
}
}
+ @SuppressWarnings("resource")
+ public int execAndReturnStatus(String command) throws Exception {
+ ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
+ try {
+ channel.setCommand(command);
+ InputStream err = channel.getErrStream();
+ channel.connect();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+ return channel.getExitStatus();
+ } finally {
+ channel.disconnect();
+ }
+ }
+
public InputStream exec2(String command, InputStream opt) throws Exception {
ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
channel.setCommand(command);
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index c38f5fae1d..43fe4ebfa0 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -187,7 +187,7 @@ public abstract class StandaloneSiteTest {
private GerritServer startImpl(@Nullable Module testSysModule, String... additionalArgs)
throws Exception {
return GerritServer.start(
- serverDesc, baseConfig, sitePaths.site_path, testSysModule, null, additionalArgs);
+ serverDesc, baseConfig, sitePaths.site_path, testSysModule, null, null, additionalArgs);
}
protected static void runGerrit(String... args) throws Exception {
diff --git a/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java b/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java
new file mode 100644
index 0000000000..ddaf341ee7
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 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.google.gerrit.acceptance.ssh;
+
+import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+
+import com.google.gerrit.sshd.CommandMetaData;
+
+@CommandMetaData(
+ name = "graceful",
+ description = "Test command for graceful shutdown",
+ runsAt = MASTER_OR_SLAVE)
+public class GracefulCommand extends TestCommand {
+
+ @Override
+ boolean isGraceful() {
+ return true;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java b/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java
new file mode 100644
index 0000000000..ed635c8fdb
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 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.google.gerrit.acceptance.ssh;
+
+import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+
+import com.google.gerrit.sshd.CommandMetaData;
+
+@CommandMetaData(
+ name = "non-graceful",
+ description = "Test command for immediate shutdown",
+ runsAt = MASTER_OR_SLAVE)
+public class NonGracefulCommand extends TestCommand {
+
+ @Override
+ boolean isGraceful() {
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/TestCommand.java b/java/com/google/gerrit/acceptance/ssh/TestCommand.java
new file mode 100644
index 0000000000..783957805a
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/TestCommand.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2020 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.google.gerrit.acceptance.ssh;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.sshd.SshCommand;
+import java.util.concurrent.CyclicBarrier;
+import org.kohsuke.args4j.Option;
+
+public abstract class TestCommand extends SshCommand {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ public static final CyclicBarrier syncPoint = new CyclicBarrier(2);
+
+ @Option(
+ name = "--duration",
+ aliases = {"-d"},
+ required = true,
+ usage = "Duration of the command execution in seconds")
+ private int duration;
+
+ @Override
+ protected void run() throws UnloggedFailure, Failure, Exception {
+ logger.atFine().log("Starting command.");
+ if (isGraceful()) {
+ enableGracefulStop();
+ }
+ try {
+ syncPoint.await();
+ Thread.sleep(duration * 1000);
+ logger.atFine().log("Stopping command.");
+ } catch (Exception e) {
+ throw die("Command ended prematurely.", e);
+ }
+ }
+
+ abstract boolean isGraceful();
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java b/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java
new file mode 100644
index 0000000000..626092bdbd
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 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.google.gerrit.acceptance.ssh;
+
+import com.google.gerrit.sshd.CommandModule;
+
+public class TestSshCommandModule extends CommandModule {
+ @Override
+ protected void configure() {
+ command("graceful").to(GracefulCommand.class);
+ command("non-graceful").to(NonGracefulCommand.class);
+ }
+}
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index e9beeb5d98..238cf290ac 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -192,6 +192,7 @@ public class Daemon extends SiteProgram {
private AbstractModule luceneModule;
private Module emailModule;
private List<Module> testSysModules = new ArrayList<>();
+ private List<Module> testSshModules = new ArrayList<>();
private Module auditEventModule;
private Runnable serverStarted;
@@ -323,6 +324,11 @@ public class Daemon extends SiteProgram {
}
@VisibleForTesting
+ public void addAdditionalSshModuleForTesting(@Nullable Module... modules) {
+ testSshModules.addAll(Arrays.asList(modules));
+ }
+
+ @VisibleForTesting
public void start() throws IOException {
if (dbInjector == null) {
dbInjector = createDbInjector(true /* enableMetrics */);
@@ -515,6 +521,8 @@ public class Daemon extends SiteProgram {
replica,
sysInjector.getInstance(DownloadConfig.class),
sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
+
+ modules.addAll(testSshModules);
if (!replica) {
modules.add(new IndexCommandsModule(sysInjector));
}
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index 8bf6cd5cb7..9efcff2d4c 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -52,6 +52,7 @@ public abstract class AbstractGitCommand extends BaseCommand {
@Override
public void start(ChannelSession channel, Environment env) {
+ enableGracefulStop();
String gitProtocol = env.getEnv().get(GIT_PROTOCOL);
if (gitProtocol != null) {
extraParameters = gitProtocol.split(":");
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index ab1f062d68..a027dd197d 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -403,6 +403,10 @@ public abstract class BaseCommand implements Command {
}
}
+ protected void enableGracefulStop() {
+ context.getSession().setGracefulStop(true);
+ }
+
protected String getTaskDescription() {
String[] ta = getTrimmedArguments();
if (ta != null) {
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index e2d262d58a..a1c057ffc0 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -89,6 +89,7 @@ import org.apache.sshd.common.mac.Mac;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.random.SingletonRandomFactory;
import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.common.session.helpers.DefaultUnknownChannelReferenceHandler;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -368,14 +369,24 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
Collection<IoSession> ioSessions = daemonAcceptor.getManagedSessions().values();
CountDownLatch allSessionsClosed = new CountDownLatch(ioSessions.size());
for (IoSession io : ioSessions) {
- logger.atFine().log("Waiting for session %s to stop.", io.getId());
- io.addCloseFutureListener(
- new SshFutureListener<CloseFuture>() {
- @Override
- public void operationComplete(CloseFuture future) {
- allSessionsClosed.countDown();
- }
- });
+ AbstractSession serverSession = AbstractSession.getSession(io, true);
+ SshSession sshSession =
+ serverSession != null ? serverSession.getAttribute(SshSession.KEY) : null;
+ if (sshSession != null && sshSession.requiresGracefulStop()) {
+ logger.atFine().log("Waiting for session %s to stop.", io.getId());
+ io.addCloseFutureListener(
+ new SshFutureListener<CloseFuture>() {
+ @Override
+ public void operationComplete(CloseFuture future) {
+ logger.atFine().log("Session %s was stopped.", io.getId());
+ allSessionsClosed.countDown();
+ }
+ });
+ } else {
+ logger.atFine().log("Stopping session %s immediately.", io.getId());
+ io.close(true);
+ allSessionsClosed.countDown();
+ }
}
try {
if (!allSessionsClosed.await(gracefulStopTimeout, TimeUnit.SECONDS)) {
diff --git a/java/com/google/gerrit/sshd/SshSession.java b/java/com/google/gerrit/sshd/SshSession.java
index d6ecc737e1..b39eaede24 100644
--- a/java/com/google/gerrit/sshd/SshSession.java
+++ b/java/com/google/gerrit/sshd/SshSession.java
@@ -35,6 +35,8 @@ public class SshSession {
private volatile String authError;
private volatile String peerAgent;
+ private volatile boolean gracefulStop = false;
+
SshSession(int sessionId, SocketAddress peer) {
this.sessionId = sessionId;
this.remoteAddress = peer;
@@ -58,6 +60,14 @@ public class SshSession {
return sessionId;
}
+ public boolean requiresGracefulStop() {
+ return gracefulStop;
+ }
+
+ public void setGracefulStop(boolean gracefulStop) {
+ this.gracefulStop = gracefulStop;
+ }
+
/** Identity of the authenticated user account on the socket. */
public CurrentUser getUser() {
return identity;
diff --git a/java/com/google/gerrit/sshd/commands/AproposCommand.java b/java/com/google/gerrit/sshd/commands/AproposCommand.java
index d3db70d1db..e7a88a1836 100644
--- a/java/com/google/gerrit/sshd/commands/AproposCommand.java
+++ b/java/com/google/gerrit/sshd/commands/AproposCommand.java
@@ -39,6 +39,7 @@ final class AproposCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
try {
List<QueryDocumentationExecutor.DocResult> res = searcher.doQuery(q);
for (DocResult docResult : res) {
diff --git a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index ee6f63590c..134fb03828 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -63,6 +63,7 @@ public class BanCommitCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
BanCommitInput input =
BanCommitInput.fromCommits(Lists.transform(commitsToBan, ObjectId::getName));
diff --git a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
index d70c153ae0..ad8e20d7db 100644
--- a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
@@ -59,6 +59,7 @@ abstract class BaseTestPrologCommand extends SshCommand {
@Override
protected final void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
RevisionResource revision =
revisions.parse(
diff --git a/java/com/google/gerrit/sshd/commands/CloseConnection.java b/java/com/google/gerrit/sshd/commands/CloseConnection.java
index 093f647997..e0b87f88cd 100644
--- a/java/com/google/gerrit/sshd/commands/CloseConnection.java
+++ b/java/com/google/gerrit/sshd/commands/CloseConnection.java
@@ -57,6 +57,7 @@ final class CloseConnection extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
SshUtil.forEachSshSession(
sshDaemon,
(k, sshSession, abstractSession, ioSession) -> {
diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 004a0ba1f4..4da55e22f5 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -72,6 +72,7 @@ final class CreateAccountCommand extends SshCommand {
@Override
protected void run()
throws IOException, ConfigInvalidException, UnloggedFailure, PermissionBackendException {
+ enableGracefulStop();
AccountInput input = new AccountInput();
input.username = username;
input.email = email;
diff --git a/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java b/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
index aad96a171d..a837ecda79 100644
--- a/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
@@ -44,6 +44,7 @@ public final class CreateBranchCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
BranchInput in = new BranchInput();
in.revision = revision;
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 9f420ed6ca..b243748adc 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -102,6 +102,7 @@ final class CreateGroupCommand extends SshCommand {
@Override
protected void run()
throws Failure, IOException, ConfigInvalidException, PermissionBackendException {
+ enableGracefulStop();
try {
GroupResource rsrc = createGroup();
diff --git a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index fca7427cab..f2ab4e8520 100644
--- a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -166,6 +166,7 @@ final class CreateProjectCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
if (!suggestParent) {
if (projectName == null) {
diff --git a/java/com/google/gerrit/sshd/commands/FlushCaches.java b/java/com/google/gerrit/sshd/commands/FlushCaches.java
index 2afc009a20..fe2a8972f8 100644
--- a/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -55,6 +55,7 @@ final class FlushCaches extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
if (list) {
if (all || !caches.isEmpty()) {
diff --git a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index 2073087c4f..28a7804498 100644
--- a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -62,6 +62,7 @@ public class GarbageCollectionCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
verifyCommandLine();
runGC();
}
diff --git a/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java b/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
index 0804d08c3e..30dc5c485b 100644
--- a/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
@@ -34,6 +34,7 @@ public class IndexActivateCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
if (versionManager.isKnownIndex(name)) {
if (versionManager.activateLatestIndex(name)) {
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index 209c44207c..041c85c856 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -53,6 +53,7 @@ final class IndexChangesCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
boolean ok = true;
for (ChangeResource rsrc : changes.values()) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
index 56b00a573d..168dc19755 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
@@ -43,6 +43,7 @@ final class IndexChangesInProjectCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure, Failure, Exception {
+ enableGracefulStop();
if (projects.isEmpty()) {
throw die("needs at least one project as command arguments");
}
diff --git a/java/com/google/gerrit/sshd/commands/IndexStartCommand.java b/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
index f3d349c809..5433b174d3 100644
--- a/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
@@ -38,6 +38,7 @@ public class IndexStartCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
if (versionManager.isKnownIndex(name)) {
if (versionManager.startReindexer(name, force)) {
diff --git a/java/com/google/gerrit/sshd/commands/KillCommand.java b/java/com/google/gerrit/sshd/commands/KillCommand.java
index df74f86366..a633a8a932 100644
--- a/java/com/google/gerrit/sshd/commands/KillCommand.java
+++ b/java/com/google/gerrit/sshd/commands/KillCommand.java
@@ -47,6 +47,7 @@ final class KillCommand extends SshCommand {
@Override
protected void run() {
+ enableGracefulStop();
ConfigResource cfgRsrc = new ConfigResource();
for (String id : taskIds) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index bdf5412ec6..7bf42eb06a 100644
--- a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -52,6 +52,7 @@ public class ListGroupsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
if (listGroups.getUser() != null && !listGroups.getProjects().isEmpty()) {
throw die("--user and --project options are not compatible.");
}
diff --git a/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java b/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
index c8b8fa111d..1a7be32338 100644
--- a/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
@@ -40,6 +40,7 @@ public class ListLoggingLevelCommand extends SshCommand {
@SuppressWarnings("unchecked")
@Override
protected void run() {
+ enableGracefulStop();
Map<String, String> logs = new TreeMap<>();
for (Enumeration<Logger> logger = LogManager.getCurrentLoggers(); logger.hasMoreElements(); ) {
Logger log = logger.nextElement();
diff --git a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
index dc1bc6ebdf..3269c2b0c4 100644
--- a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
@@ -45,6 +45,7 @@ public class ListMembersCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
impl.display(stdout);
}
diff --git a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
index 9f2ffa9772..e711d57a97 100644
--- a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
@@ -32,6 +32,7 @@ public class ListProjectsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
if (!impl.getFormat().isJson()) {
List<String> showBranch = impl.getShowBranch();
if (impl.isShowTree() && (showBranch != null) && !showBranch.isEmpty()) {
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index 1a60679c74..b8fc55a09d 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -74,6 +74,7 @@ public class LsUserRefs extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
Account.Id userAccountId;
try {
userAccountId = accountResolver.resolve(userName).asUnique().account().id();
diff --git a/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java b/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
index 7e32615180..086081c6d3 100644
--- a/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
@@ -28,6 +28,7 @@ public abstract class PluginAdminSshCommand extends SshCommand {
@Override
protected final void run() throws UnloggedFailure {
+ enableGracefulStop();
if (!loader.isRemoteAdminEnabled()) {
throw die("remote plugin administration is disabled");
}
diff --git a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index 3a952f024f..504b239d68 100644
--- a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -41,6 +41,7 @@ public class PluginLsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE).value();
if (format.isJson()) {
diff --git a/java/com/google/gerrit/sshd/commands/Query.java b/java/com/google/gerrit/sshd/commands/Query.java
index 78485d3442..772eabe085 100644
--- a/java/com/google/gerrit/sshd/commands/Query.java
+++ b/java/com/google/gerrit/sshd/commands/Query.java
@@ -106,6 +106,7 @@ public class Query extends SshCommand implements DynamicOptions.BeanReceiver {
@Override
protected void run() throws Exception {
+ enableGracefulStop();
processor.query(join(query, " "));
}
diff --git a/java/com/google/gerrit/sshd/commands/ReloadConfig.java b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
index cbe3c57031..eeb48bb864 100644
--- a/java/com/google/gerrit/sshd/commands/ReloadConfig.java
+++ b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
@@ -38,6 +38,7 @@ public class ReloadConfig extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
Multimap<UpdateResult, ConfigUpdateEntry> updates = gerritServerConfigReloader.reloadConfig();
if (updates.isEmpty()) {
stdout.println("No config entries updated!");
diff --git a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index 166ad68c67..976e7bd789 100644
--- a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -46,6 +46,7 @@ public class RenameGroupCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
GroupResource rsrc = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName));
NameInput input = new NameInput();
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 3a1bf6cd18..61e529f121 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -167,6 +167,7 @@ public class ReviewCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
if (abandonChange) {
if (restoreChange) {
throw die("abandon and restore actions are mutually exclusive");
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index df1e3ede73..43a1670891 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -154,6 +154,7 @@ final class SetAccountCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
user = genericUserFactory.create(id);
validate();
diff --git a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
index fd7ef75e55..b6d283eb02 100644
--- a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -43,6 +43,7 @@ public class SetHeadCommand extends SshCommand {
@Override
protected void run() throws Exception {
+ enableGracefulStop();
HeadInput input = new HeadInput();
input.ref = newHead;
try {
diff --git a/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java b/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
index cfdd73562c..3faf5989a4 100644
--- a/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
@@ -61,6 +61,7 @@ public class SetLoggingLevelCommand extends SshCommand {
@SuppressWarnings("unchecked")
@Override
protected void run() throws MalformedURLException {
+ enableGracefulStop();
if (level == LevelOption.RESET) {
reset();
} else {
diff --git a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
index 2511df4246..db8e42a3c1 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -102,6 +102,7 @@ public class SetMembersCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure, Failure, Exception {
+ enableGracefulStop();
try {
for (AccountGroup.UUID groupUuid : groups) {
GroupResource resource =
diff --git a/java/com/google/gerrit/sshd/commands/SetParentCommand.java b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
index 47a61daded..bfdbff9032 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -88,6 +88,7 @@ final class SetParentCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
if (oldParent == null && children.isEmpty()) {
throw die(
"child projects have to be specified as "
diff --git a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
index 8c9fc9fa5b..9866c4e4e8 100644
--- a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
@@ -132,6 +132,7 @@ final class SetProjectCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
ConfigInput configInput = new ConfigInput();
configInput.requireChangeId = requireChangeID;
configInput.submitType = submitType;
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 3d1723bb93..0e6fa65d27 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -96,6 +96,7 @@ public class SetReviewersCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
boolean ok = true;
for (ChangeResource rsrc : changes.values()) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index fdb57acfc9..63a0dda771 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -112,6 +112,7 @@ final class ShowCaches extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
nw = columns - 50;
Date now = new Date();
stdout.format(
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index decf5d5893..d27136469b 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -86,6 +86,7 @@ final class ShowConnections extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
final IoAcceptor acceptor = daemon.getIoAcceptor();
if (acceptor == null) {
throw new Failure(1, "fatal: sshd no longer running");
diff --git a/java/com/google/gerrit/sshd/commands/ShowQueue.java b/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 2ec9e2d3b3..779f2df591 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -85,6 +85,7 @@ final class ShowQueue extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
maxCommandWidth = wide ? Integer.MAX_VALUE : columns - 8 - 12 - 12 - 4 - 4;
stdout.print(
String.format(
diff --git a/java/com/google/gerrit/sshd/commands/VersionCommand.java b/java/com/google/gerrit/sshd/commands/VersionCommand.java
index 8fac979be2..f8771fb21f 100644
--- a/java/com/google/gerrit/sshd/commands/VersionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/VersionCommand.java
@@ -25,6 +25,7 @@ final class VersionCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
String v = Version.getVersion();
if (v == null) {
throw new Failure(1, "fatal: version unavailable");
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java
new file mode 100644
index 0000000000..827c192765
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2020 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.google.gerrit.acceptance.ssh;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.restapi.config.ListTasks;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import java.time.LocalDateTime;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@NoHttpd
+@UseSsh
+@Sandboxed
+@RunWith(ConfigSuite.class)
+@SuppressWarnings("unused")
+public class SshDaemonIT extends AbstractDaemonTest {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Inject private ListTasks listTasks;
+ @Inject private SitePaths gerritSitePath;
+
+ @ConfigSuite.Parameter protected Config config;
+
+ @ConfigSuite.Config
+ public static Config gracefulConfig() {
+ Config config = new Config();
+ config.setString("sshd", null, "gracefulStopTimeout", "10s");
+ return config;
+ }
+
+ @Override
+ public Module createSshModule() {
+ return new TestSshCommandModule();
+ }
+
+ public Future<Integer> startCommand(String command) throws Exception {
+ Callable<Integer> gracefulSession =
+ () -> {
+ int returnCode = -1;
+ logger.atFine().log("Before Command");
+ returnCode = userSshSession.execAndReturnStatus(command);
+ logger.atFine().log("After Command");
+ return returnCode;
+ };
+
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ Future<Integer> future = executor.submit(gracefulSession);
+
+ LocalDateTime timeout = LocalDateTime.now().plusSeconds(10);
+
+ TestCommand.syncPoint.await();
+
+ return future;
+ }
+
+ @Test
+ public void NonGracefulCommandIsStoppedImmediately() throws Exception {
+ Future<Integer> future = startCommand("non-graceful -d 5");
+ restart();
+ Assert.assertTrue(future.get() == -1);
+ }
+
+ @Test
+ public void GracefulCommandIsStoppedGracefully() throws Exception {
+ Future<Integer> future = startCommand("graceful -d 5");
+ restart();
+ if (cfg.getTimeUnit("sshd", null, "gracefulStopTimeout", 0, TimeUnit.SECONDS) == 0) {
+ Assert.assertTrue(future.get() == -1);
+ } else {
+ Assert.assertTrue(future.get() == 0);
+ }
+ }
+}
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 6f8d7c1720..eceead475e 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -108,8 +108,8 @@ def declare_nongoogle_deps():
maven_jar(
name = "jackson-core",
- artifact = "com.fasterxml.jackson.core:jackson-core:2.11.2",
- sha1 = "bc022ab0f0c83c07f9c52c5ab9a6a4932b15cc35",
+ artifact = "com.fasterxml.jackson.core:jackson-core:2.11.3",
+ sha1 = "c2351800432bdbdd8284c3f5a7f0782a352aa84a",
)
# Google internal dependencies: these are developed at Google, so there is