Merge "Merge branch 'stable-3.2' into master"
diff --git a/.bazelversion b/.bazelversion
index 1545d96..d5c0c99 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 78a621c..c35c9c2 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -388,6 +388,15 @@
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 reindexAccount(Account.Id accountId) {
accountIndexer.index(accountId);
}
@@ -430,15 +439,18 @@
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
Module module = createModule();
Module auditModule = createAuditModule();
+ Module sshModule = createSshModule();
if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
if (commonServer == null) {
commonServer =
- GerritServer.initAndStart(temporaryFolder, classDesc, baseConfig, module, auditModule);
+ GerritServer.initAndStart(
+ temporaryFolder, classDesc, baseConfig, module, auditModule, sshModule);
}
server = commonServer;
} else {
server =
- GerritServer.initAndStart(temporaryFolder, methodDesc, baseConfig, module, auditModule);
+ GerritServer.initAndStart(
+ temporaryFolder, methodDesc, baseConfig, module, auditModule, sshModule);
}
server.getTestInjector().injectMembers(this);
@@ -536,6 +548,11 @@
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 5942c0f..0025396 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -323,6 +323,7 @@
* @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
*/
@@ -331,14 +332,15 @@
Description desc,
Config baseConfig,
@Nullable Module testSysModule,
- @Nullable Module testAuditModule)
+ @Nullable Module testAuditModule,
+ @Nullable Module testSshModule)
throws Exception {
Path site = temporaryFolder.newFolder().toPath();
try {
if (!desc.memory()) {
init(desc, baseConfig, site);
}
- return start(desc, baseConfig, site, testSysModule, testAuditModule, null);
+ return start(desc, baseConfig, site, testSysModule, testAuditModule, testSshModule, null);
} catch (Exception e) {
throw e;
}
@@ -354,6 +356,7 @@
* 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
@@ -367,6 +370,7 @@
Path site,
@Nullable Module testSysModule,
@Nullable Module testAuditModule,
+ @Nullable Module testSshModule,
@Nullable InMemoryRepositoryManager inMemoryRepoManager,
String... additionalArgs)
throws Exception {
@@ -390,6 +394,9 @@
if (testSysModule != null) {
daemon.addAdditionalSysModuleForTesting(testSysModule);
}
+ if (testSshModule != null) {
+ daemon.addAdditionalSshModuleForTesting(testSshModule);
+ }
daemon.setEnableSshd(desc.useSsh());
if (desc.memory()) {
@@ -614,7 +621,24 @@
server.close();
server.daemon.stop();
- return start(server.desc, cfg, site, null, null, inMemoryRepoManager);
+ return start(server.desc, cfg, site, null, 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, null, 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 6ecf85f..6698657 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -65,6 +65,22 @@
}
}
+ @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();
+ }
+ }
+
private boolean hasError() {
return error != null;
}
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index 43fe4eb..dcb49a5 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -187,7 +187,14 @@
private GerritServer startImpl(@Nullable Module testSysModule, String... additionalArgs)
throws Exception {
return GerritServer.start(
- serverDesc, baseConfig, sitePaths.site_path, testSysModule, null, null, additionalArgs);
+ serverDesc,
+ baseConfig,
+ sitePaths.site_path,
+ testSysModule,
+ null,
+ 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 0000000..ddaf341
--- /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 0000000..ed635c8
--- /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 0000000..7839578
--- /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 0000000..626092b
--- /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/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index e4d594b..0138290 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -90,6 +90,7 @@
CreateProjectArgs args = new CreateProjectArgs();
args.setProjectName(name);
+ args.permissionsOnly = projectCreation.permissionOnly().orElse(false);
args.branch =
projectCreation.branches().stream().map(RefNames::fullName).collect(toImmutableList());
args.createEmptyCommit = projectCreation.createEmptyCommit().orElse(true);
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
index 2649dea..00759a0 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
@@ -35,6 +35,8 @@
public abstract Optional<Boolean> createEmptyCommit();
+ public abstract Optional<Boolean> permissionOnly();
+
public abstract Optional<SubmitType> submitType();
abstract ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator();
@@ -67,6 +69,8 @@
public abstract TestProjectCreation.Builder createEmptyCommit(boolean value);
+ public abstract TestProjectCreation.Builder permissionOnly(boolean value);
+
/** Skips the empty commit on creation. This means that project's branches will not exist. */
public TestProjectCreation.Builder noEmptyCommit() {
return createEmptyCommit(false);
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index ba037d9..2eb19aa 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -206,6 +206,7 @@
private AbstractModule luceneModule;
private Module emailModule;
private List<Module> testSysModules = new ArrayList<>();
+ private List<Module> testSshModules = new ArrayList<>();
private Module auditEventModule;
private Runnable serverStarted;
@@ -337,6 +338,11 @@
}
@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 */);
@@ -532,6 +538,8 @@
replica,
sysInjector.getInstance(DownloadConfig.class),
sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
+
+ modules.addAll(testSshModules);
if (!replica) {
modules.add(new IndexCommandsModule(sysInjector));
modules.add(new SequenceCommandsModule());
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index 8bf6cd5..9efcff2 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -52,6 +52,7 @@
@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 ab1f062..a027dd1 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -403,6 +403,10 @@
}
}
+ 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 c43bf91..c14ebd8 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -90,6 +90,7 @@
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;
@@ -369,14 +370,24 @@
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 d6ecc73..b39eaed 100644
--- a/java/com/google/gerrit/sshd/SshSession.java
+++ b/java/com/google/gerrit/sshd/SshSession.java
@@ -35,6 +35,8 @@
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 @@
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 d3db70d..e7a88a1 100644
--- a/java/com/google/gerrit/sshd/commands/AproposCommand.java
+++ b/java/com/google/gerrit/sshd/commands/AproposCommand.java
@@ -39,6 +39,7 @@
@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 ee6f635..134fb03 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -63,6 +63,7 @@
@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 d70c153..ad8e20d 100644
--- a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
@@ -59,6 +59,7 @@
@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 093f647..e0b87f8 100644
--- a/java/com/google/gerrit/sshd/commands/CloseConnection.java
+++ b/java/com/google/gerrit/sshd/commands/CloseConnection.java
@@ -57,6 +57,7 @@
@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 004a0ba..4da55e2 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -72,6 +72,7 @@
@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 aad96a1..a837ecd 100644
--- a/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
@@ -44,6 +44,7 @@
@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 17f80c0..5fd2297 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -102,6 +102,7 @@
@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 fca7427..f2ab4e8 100644
--- a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -166,6 +166,7 @@
@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 2afc009..fe2a897 100644
--- a/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -55,6 +55,7 @@
@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 2073087..28a7804 100644
--- a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -62,6 +62,7 @@
@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 0804d08..30dc5c4 100644
--- a/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
@@ -34,6 +34,7 @@
@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 fb62b48..1fb0e13 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -52,6 +52,7 @@
@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 56b00a5..168dc19 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
@@ -43,6 +43,7 @@
@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 f3d349c..5433b17 100644
--- a/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
@@ -38,6 +38,7 @@
@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 df74f86..a633a8a 100644
--- a/java/com/google/gerrit/sshd/commands/KillCommand.java
+++ b/java/com/google/gerrit/sshd/commands/KillCommand.java
@@ -47,6 +47,7 @@
@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 bdf5412..7bf42eb 100644
--- a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -52,6 +52,7 @@
@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 c8b8fa1..1a7be32 100644
--- a/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
@@ -40,6 +40,7 @@
@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 dc1bc6e..3269c2b 100644
--- a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
@@ -45,6 +45,7 @@
@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 9f2ffa9..e711d57 100644
--- a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
@@ -32,6 +32,7 @@
@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 80aee01..6eb045b 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -74,6 +74,7 @@
@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 7e32615..086081c 100644
--- a/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
@@ -28,6 +28,7 @@
@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 3a952f0..504b239 100644
--- a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -41,6 +41,7 @@
@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 78485d3..772eabe 100644
--- a/java/com/google/gerrit/sshd/commands/Query.java
+++ b/java/com/google/gerrit/sshd/commands/Query.java
@@ -106,6 +106,7 @@
@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 cbe3c57..eeb48bb 100644
--- a/java/com/google/gerrit/sshd/commands/ReloadConfig.java
+++ b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
@@ -38,6 +38,7 @@
@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 166ad68..976e7bd 100644
--- a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -46,6 +46,7 @@
@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 78a7381..b58cc45 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -167,6 +167,7 @@
@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 df1e3ed..43a1670 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -154,6 +154,7 @@
@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 fd7ef75..b6d283e 100644
--- a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -43,6 +43,7 @@
@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 cfdd735..3faf598 100644
--- a/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
@@ -61,6 +61,7 @@
@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 2511df4..db8e42a 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -102,6 +102,7 @@
@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 406949e..d23f7fa 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -90,6 +90,7 @@
@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 8c9fc9f..9866c4e 100644
--- a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
@@ -132,6 +132,7 @@
@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 5bc5537..95627e1 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -95,6 +95,7 @@
@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 1d756de..ba84179 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -112,6 +112,7 @@
@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 decf5d5..d271364 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -86,6 +86,7 @@
@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 2ec9e2d..779f2df 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -85,6 +85,7 @@
@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 8fac979..f8771fb 100644
--- a/java/com/google/gerrit/sshd/commands/VersionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/VersionCommand.java
@@ -25,6 +25,7 @@
@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 0000000..827c192
--- /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/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
index 62dfc63..b888102 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -41,6 +41,7 @@
import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermission;
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.server.project.ProjectConfig;
@@ -114,6 +115,13 @@
}
@Test
+ public void permissionOnly() throws Exception {
+ Project.NameKey key = projectOperations.newProject().permissionOnly(true).create();
+ String head = gApi.projects().name(key.get()).head();
+ assertThat(head).isEqualTo(RefNames.REFS_CONFIG);
+ }
+
+ @Test
public void getProjectConfig() throws Exception {
Project.NameKey key = projectOperations.newProject().create();
assertThat(projectOperations.project(key).getProjectConfig().getProject().getDescription())
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index ff8116b..a8f5af0 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -102,8 +102,8 @@
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