diff options
author | Luca Milanesio <luca.milanesio@gmail.com> | 2020-10-06 21:38:02 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-10-06 21:38:02 +0000 |
commit | 69c0c4757cae06b9a31610928a14fbbbf7187ef1 (patch) | |
tree | ace3b878fc06955fadaea66c2bc84e8ecc41732f | |
parent | 911611acacdfe84e0d658096fc4717041e792614 (diff) | |
parent | 1efffb1212728dde7174ebac1cb199653e5a2df1 (diff) |
Merge "Merge branch 'stable-3.2' into master"
56 files changed, 401 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 78a621cf87..c35c9c2f56 100644 --- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java +++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java @@ -388,6 +388,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 reindexAccount(Account.Id accountId) { accountIndexer.index(accountId); } @@ -430,15 +439,18 @@ public abstract class AbstractDaemonTest { 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 @@ 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 5942c0f0d3..0025396ed7 100644 --- a/java/com/google/gerrit/acceptance/GerritServer.java +++ b/java/com/google/gerrit/acceptance/GerritServer.java @@ -323,6 +323,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 */ @@ -331,14 +332,15 @@ public class GerritServer implements AutoCloseable { 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 @@ 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 @@ -367,6 +370,7 @@ public class GerritServer implements AutoCloseable { Path site, @Nullable Module testSysModule, @Nullable Module testAuditModule, + @Nullable Module testSshModule, @Nullable InMemoryRepositoryManager inMemoryRepoManager, String... additionalArgs) throws Exception { @@ -390,6 +394,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()) { @@ -614,7 +621,24 @@ public class GerritServer implements AutoCloseable { 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 6ecf85fdce..6698657298 100644 --- a/java/com/google/gerrit/acceptance/SshSession.java +++ b/java/com/google/gerrit/acceptance/SshSession.java @@ -65,6 +65,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(); + } + } + 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 43fe4ebfa0..dcb49a5943 100644 --- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java +++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java @@ -187,7 +187,14 @@ public abstract class StandaloneSiteTest { 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 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/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java index e4d594b927..013829032c 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 @@ public class ProjectOperationsImpl implements ProjectOperations { 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 2649dea36d..00759a0004 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 class TestProjectCreation { 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 class TestProjectCreation { 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 ba037d9789..2eb19aa9c7 100644 --- a/java/com/google/gerrit/pgm/Daemon.java +++ b/java/com/google/gerrit/pgm/Daemon.java @@ -206,6 +206,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; @@ -337,6 +338,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 */); @@ -532,6 +538,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)); modules.add(new SequenceCommandsModule()); 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 c43bf91076..c14ebd8d2d 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.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; @@ -369,14 +370,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 17f80c03e5..5fd22978e1 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 fb62b48a12..1fb0e13a3a 100644 --- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java +++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java @@ -52,6 +52,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 80aee0107e..6eb045bc79 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 78a7381721..b58cc451e9 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 406949e556..d23f7fa218 100644 --- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java +++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java @@ -90,6 +90,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 5bc5537267..95627e1582 100644 --- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java +++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java @@ -95,6 +95,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 1d756de5e6..ba84179085 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/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java index 62dfc638ff..b88810267a 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.AbstractDaemonTest; 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 @@ public class ProjectOperationsImplTest extends AbstractDaemonTest { } @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 ff8116b8c2..a8f5af052b 100644 --- a/tools/nongoogle.bzl +++ b/tools/nongoogle.bzl @@ -102,8 +102,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 |