From 49573c2b30efaf376a043b71a2a52de66b9e0ece Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Thu, 10 Dec 2020 17:13:51 +0100 Subject: [PATCH 1/7] JPERF-715: Test detaching a process --- .../tools/ssh/api/SshConnectionTest.kt | 2 +- .../performance/tools/ssh/api/SshTest.kt | 24 ++++++++++++++++++ src/test/resources/log4j2.xml | 25 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt create mode 100644 src/test/resources/log4j2.xml diff --git a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshConnectionTest.kt b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshConnectionTest.kt index f64cfad..499611c 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshConnectionTest.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshConnectionTest.kt @@ -14,4 +14,4 @@ class SshConnectionTest { Assert.assertEquals(sshResult.output, "test\n") } } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt new file mode 100644 index 0000000..c101c17 --- /dev/null +++ b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt @@ -0,0 +1,24 @@ +package com.atlassian.performance.tools.ssh.api + +import org.junit.Test + +class SshTest { + + @Test + fun shouldDetachProcess() { + SshContainer().useSsh { sshHost -> + installPing(sshHost) + + val ping = sshHost.newConnection().use { ssh -> + ssh.startProcess("ping localhost") + } + sshHost.newConnection().use { ssh -> + ssh.stopProcess(ping) + } + } + } + + private fun installPing(sshHost: Ssh) { + sshHost.newConnection().use { it.execute("apt-get update -qq && apt-get install iputils-ping -y") } + } +} diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml new file mode 100644 index 0000000..840af97 --- /dev/null +++ b/src/test/resources/log4j2.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + From 2a99d22b62769d22c45926e55d8ff4cf39c61d09 Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 14:47:57 +0100 Subject: [PATCH 2/7] JPERF-715: Extract `WaitingCommand` for reuse --- .../performance/tools/ssh/SshjConnection.kt | 63 +-------------- .../performance/tools/ssh/WaitingCommand.kt | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/com/atlassian/performance/tools/ssh/WaitingCommand.kt diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt index f11962a..1b07cd0 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt @@ -11,11 +11,8 @@ import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger import java.io.File -import java.io.InputStream import java.nio.file.Path import java.time.Duration -import java.time.Instant -import java.util.concurrent.TimeUnit /** * An [SshConnection] based on the [SSHJ library](https://github.com/hierynomus/sshj). @@ -63,12 +60,7 @@ internal class SshjConnection internal constructor( ): SshResult { logger.debug("${sshHost.userName}$ $cmd") return session.exec(cmd).use { command -> - command.waitForCompletion(cmd, timeout) - SshResult( - exitStatus = command.exitStatus, - output = command.inputStream.readAndLog(stdout), - errorOutput = command.errorStream.readAndLog(stderr) - ) + WaitingCommand(command, timeout, stdout, stderr).waitForResult() } } @@ -91,62 +83,9 @@ internal class SshjConnection internal constructor( scpFileTransfer.upload(localSource.absolutePath, remoteDestination) } - private fun Session.Command.waitForCompletion( - cmd: String, - timeout: Duration - ) { - val expectedEnd = Instant.now().plus(timeout) - val extendedTime = timeout.multipliedBy(5).dividedBy(4) - try { - this.join(extendedTime.toMillis(), TimeUnit.MILLISECONDS) - } catch (e: Exception) { - val output = readOutput(cmd) - throw Exception("SSH command failed to finish in extended time ($extendedTime): $output", e) - } - val actualEnd = Instant.now() - if (actualEnd.isAfter(expectedEnd)) { - val overtime = Duration.between(expectedEnd, actualEnd) - throw Exception("SSH command exceeded timeout $timeout by $overtime: '$cmd'") - } - } - - private fun Session.Command.readOutput( - cmd: String - ): SshExecutedCommand { - return try { - this.close() - SshExecutedCommand( - cmd = cmd, - stdout = this.inputStream.reader().use { it.readText() }, - stderr = this.errorStream.reader().use { it.readText() } - ) - } catch (e: Exception) { - logger.error("Failed do close ssh channel. Can't get command output", e) - SshExecutedCommand( - cmd = cmd, - stdout = "", - stderr = "" - ) - } - } - - private fun InputStream.readAndLog(level: Level): String { - val output = this.reader().use { it.readText() } - if (output.isNotBlank()) { - logger.log(level, output) - } - return output - } - override fun getHost(): SshHost = sshHost override fun close() { ssh.close() } - - private data class SshExecutedCommand( - val cmd: String, - val stdout: String, - val stderr: String - ) } diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/WaitingCommand.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/WaitingCommand.kt new file mode 100644 index 0000000..38df0f2 --- /dev/null +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/WaitingCommand.kt @@ -0,0 +1,79 @@ +package com.atlassian.performance.tools.ssh + +import com.atlassian.performance.tools.ssh.api.SshConnection +import net.schmizz.sshj.connection.channel.direct.Session +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger +import java.io.InputStream +import java.time.Duration +import java.time.Instant +import java.util.concurrent.TimeUnit + +internal class WaitingCommand( + private val command: Session.Command, + private val timeout: Duration, + private val stdout: Level, + private val stderr: Level +) { + + fun waitForResult(): SshConnection.SshResult { + command.waitForCompletion(timeout) + return SshConnection.SshResult( + exitStatus = command.exitStatus, + output = command.inputStream.readAndLog(stdout), + errorOutput = command.errorStream.readAndLog(stderr) + ) + } + + private fun Session.Command.waitForCompletion( + timeout: Duration + ) { + val expectedEnd = Instant.now().plus(timeout) + val extendedTime = timeout.multipliedBy(5).dividedBy(4) + try { + this.join(extendedTime.toMillis(), TimeUnit.MILLISECONDS) + } catch (e: Exception) { + val output = readOutput() + throw Exception("SSH command failed to finish in extended time ($extendedTime): $output", e) + } + val actualEnd = Instant.now() + if (actualEnd.isAfter(expectedEnd)) { + val overtime = Duration.between(expectedEnd, actualEnd) + throw Exception("SSH command exceeded timeout $timeout by $overtime") + } + } + + private fun Session.Command.readOutput(): SshjExecutedCommand { + return try { + this.close() + SshjExecutedCommand( + stdout = this.inputStream.reader().use { it.readText() }, + stderr = this.errorStream.reader().use { it.readText() } + ) + } catch (e: Exception) { + LOG.error("Failed do close ssh channel. Can't get command output", e) + SshjExecutedCommand( + stdout = "", + stderr = "" + ) + } + } + + private fun InputStream.readAndLog(level: Level): String { + val output = this.reader().use { it.readText() } + if (output.isNotBlank()) { + LOG.log(level, output) + } + return output + } + + private data class SshjExecutedCommand( + val stdout: String, + val stderr: String + ) + + private companion object { + private val LOG: Logger = LogManager.getLogger(this::class.java) + } +} From 3cdc0d202ff3f91daa5e856a3a1b0c6d8630b49b Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 14:53:47 +0100 Subject: [PATCH 3/7] JPERF-716: Add `runInBackground` --- CHANGELOG.md | 5 +++ .../tools/ssh/SshjBackgroundProcess.kt | 41 +++++++++++++++++++ .../tools/ssh/api/BackgroundProcess.kt | 20 +++++++++ .../performance/tools/ssh/api/Ssh.kt | 15 ++++++- .../performance/tools/ssh/api/SshTest.kt | 31 ++++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt create mode 100644 src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 889f3bd..bb969d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ The API consists of all public Kotlin types from `com.atlassian.performance.tool ## [Unreleased] [Unreleased]: https://github.com/atlassian/ssh/compare/release-2.3.1...master +### Added +- Add `Ssh.runInBackground`, which yields `SshResult`s unlike the old `SshConnection.startProcess`. Resolve [JPERF-716]. + +[JPERF-716]: https://ecosystem.atlassian.net/browse/JPERF-716 + ## [2.3.1] - 2020-04-06 [2.3.1]: https://github.com/atlassian/ssh/compare/release-2.3.0...release-2.3.1 diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt new file mode 100644 index 0000000..ecb3c58 --- /dev/null +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt @@ -0,0 +1,41 @@ +package com.atlassian.performance.tools.ssh + +import com.atlassian.performance.tools.ssh.api.BackgroundProcess +import com.atlassian.performance.tools.ssh.api.SshConnection +import net.schmizz.sshj.connection.channel.direct.Session +import org.apache.logging.log4j.Level +import java.time.Duration +import java.util.concurrent.atomic.AtomicBoolean + +internal class SshjBackgroundProcess( + private val session: Session, + private val command: Session.Command +) : BackgroundProcess { + + private var closed = AtomicBoolean(false) + + override fun stop(timeout: Duration): SshConnection.SshResult { + sendSigint() + val result = WaitingCommand(command, timeout, Level.DEBUG, Level.DEBUG).waitForResult() + close() + return result + } + + /** + * [Session.Command.signal] doesn't work, so send the CTRL-C character rather than SSH-level SIGINT signal. + * [OpenSSH server was not supporting this standard](https://bugzilla.mindrot.org/show_bug.cgi?id=1424). + * It's supported since 7.9p1 (late 2018), but our test Ubuntu still runs on 7.6p1. + */ + private fun sendSigint() { + val ctrlC = 3 + command.outputStream.write(ctrlC); + command.outputStream.flush(); + } + + override fun close() { + if (!closed.getAndSet(true)) { + command.use {} + session.use {} + } + } +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt new file mode 100644 index 0000000..4a18487 --- /dev/null +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt @@ -0,0 +1,20 @@ +package com.atlassian.performance.tools.ssh.api + +import java.time.Duration + +/** + * Runs in the background. Is independent of `SshConnection`s being closed. + * Can be used for commands, which will not stop on their own, e.g. `tail -f`, `ping`, `top`, etc. + * @since 2.4.0 + */ +interface BackgroundProcess : AutoCloseable { + + /** + * Interrupts the process, then waits up to [timeout] for its completion. + * Throws if getting the [SshConnection.SshResult] fails. + * Closes the open resources. + * + * @return the result of the stopped process, could have a non-zero exit code + */ + fun stop(timeout: Duration): SshConnection.SshResult +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/Ssh.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/Ssh.kt index 305ba88..899434e 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/Ssh.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/Ssh.kt @@ -3,6 +3,7 @@ package com.atlassian.performance.tools.ssh.api import com.atlassian.performance.tools.jvmtasks.api.ExponentialBackoff import com.atlassian.performance.tools.jvmtasks.api.IdempotentAction import com.atlassian.performance.tools.ssh.PerformanceDefaultConfig +import com.atlassian.performance.tools.ssh.SshjBackgroundProcess import com.atlassian.performance.tools.ssh.SshjConnection import com.atlassian.performance.tools.ssh.port.LocalPort import com.atlassian.performance.tools.ssh.port.RemotePort @@ -44,6 +45,18 @@ data class Ssh( ) } + /** + * Runs [cmd] in the background, without waiting for its completion. The returned process can be stopped later. + * + * @since 2.4.0 + */ + fun runInBackground(cmd: String): BackgroundProcess { + val session = prepareClient().startSession() + session.allocateDefaultPTY() + val command = session.exec(cmd) + return SshjBackgroundProcess(session, command) + } + /** * Creates an encrypted connection between a local machine and a remote machine through which you can relay traffic. * @@ -102,4 +115,4 @@ data class Ssh( ) ) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt index c101c17..d838ad2 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt @@ -1,6 +1,9 @@ package com.atlassian.performance.tools.ssh.api +import org.junit.Assert import org.junit.Test +import java.time.Duration +import kotlin.system.measureTimeMillis class SshTest { @@ -18,6 +21,34 @@ class SshTest { } } + @Test + fun shouldNotWaitForBackground() { + SshContainer().useSsh { sshHost -> + val runMillis = measureTimeMillis { + sshHost.runInBackground("sleep 8") + } + + Assert.assertTrue(runMillis < 1000) + } + } + + @Test + fun shouldGetBackgroundResults() { + SshContainer().useSsh { sshHost -> + installPing(sshHost) + + val ping = sshHost.runInBackground("ping localhost") + Thread.sleep(2000) + // meanwhile we can create and kill connections + sshHost.newConnection().use { it.safeExecute("ls") } + Thread.sleep(2000) + val pingResult = ping.stop(Duration.ofMillis(20)) + + Assert.assertTrue(pingResult.isSuccessful()) + Assert.assertTrue(pingResult.output.contains("localhost ping statistics")) + } + } + private fun installPing(sshHost: Ssh) { sshHost.newConnection().use { it.execute("apt-get update -qq && apt-get install iputils-ping -y") } } From 1ab56f8be1c84ac4ed5a61de180e16893bdabb8d Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 15:15:06 +0100 Subject: [PATCH 4/7] JPERF-716: Tolerate background processes finishing early --- CHANGELOG.md | 1 + .../tools/ssh/SshjBackgroundProcess.kt | 15 ++++++++++++++- .../tools/ssh/api/BackgroundProcess.kt | 1 + .../performance/tools/ssh/api/SshTest.kt | 12 ++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb969d0..245a3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The API consists of all public Kotlin types from `com.atlassian.performance.tool ### Added - Add `Ssh.runInBackground`, which yields `SshResult`s unlike the old `SshConnection.startProcess`. Resolve [JPERF-716]. +- Tolerate lack of interrupt if `BackgroundProcess` is already finished. [JPERF-716]: https://ecosystem.atlassian.net/browse/JPERF-716 diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt index ecb3c58..c2af7eb 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjBackgroundProcess.kt @@ -4,6 +4,7 @@ import com.atlassian.performance.tools.ssh.api.BackgroundProcess import com.atlassian.performance.tools.ssh.api.SshConnection import net.schmizz.sshj.connection.channel.direct.Session import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager import java.time.Duration import java.util.concurrent.atomic.AtomicBoolean @@ -15,12 +16,20 @@ internal class SshjBackgroundProcess( private var closed = AtomicBoolean(false) override fun stop(timeout: Duration): SshConnection.SshResult { - sendSigint() + tryToInterrupt() val result = WaitingCommand(command, timeout, Level.DEBUG, Level.DEBUG).waitForResult() close() return result } + private fun tryToInterrupt() { + try { + sendSigint() + } catch (e: Exception) { + LOG.debug("cannot interrupt, if the command doesn't run anymore, then the write connection is closed", e) + } + } + /** * [Session.Command.signal] doesn't work, so send the CTRL-C character rather than SSH-level SIGINT signal. * [OpenSSH server was not supporting this standard](https://bugzilla.mindrot.org/show_bug.cgi?id=1424). @@ -38,4 +47,8 @@ internal class SshjBackgroundProcess( session.use {} } } + + private companion object { + private val LOG = LogManager.getLogger(this::class.java) + } } diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt index 4a18487..ef51d54 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/BackgroundProcess.kt @@ -11,6 +11,7 @@ interface BackgroundProcess : AutoCloseable { /** * Interrupts the process, then waits up to [timeout] for its completion. + * Skips the interrupt if the process is already finished. * Throws if getting the [SshConnection.SshResult] fails. * Closes the open resources. * diff --git a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt index d838ad2..d6ae211 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt @@ -49,6 +49,18 @@ class SshTest { } } + @Test + fun shouldTolerateEarlyFinish() { + SshContainer().useSsh { sshHost -> + installPing(sshHost) + + val fail = sshHost.runInBackground("nonexistent-command") + val failResult = fail.stop(Duration.ofMillis(20)) + + Assert.assertEquals(127, failResult.exitStatus) + } + } + private fun installPing(sshHost: Ssh) { sshHost.newConnection().use { it.execute("apt-get update -qq && apt-get install iputils-ping -y") } } From 4469ee614bfb5238f3dd88875434a1e4827be7ff Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 15:00:20 +0100 Subject: [PATCH 5/7] JPERF-716: Deprecate `DetachedProcess` for `BackgroundProcess` Pros: * can get exit code, stdout, stderr * can `stop` even if the process died earlier * doesn't assume or require `screen` to be installed * conveys the need to release an external resource via `AutoCloseable` * doesn't use unreliable Kotlin `internal` to hide members Cons: * maintains a connection since start to stop * could accumulate unnecessarily big stdout - but it can be fixed later --- CHANGELOG.md | 3 +++ .../com/atlassian/performance/tools/ssh/SshjConnection.kt | 2 ++ .../atlassian/performance/tools/ssh/api/DetachedProcess.kt | 4 +++- .../com/atlassian/performance/tools/ssh/api/SshConnection.kt | 2 ++ .../kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 245a3a0..9aef3b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ The API consists of all public Kotlin types from `com.atlassian.performance.tool - Add `Ssh.runInBackground`, which yields `SshResult`s unlike the old `SshConnection.startProcess`. Resolve [JPERF-716]. - Tolerate lack of interrupt if `BackgroundProcess` is already finished. +### Deprecated +- Deprecate `SshConnection.startProcess`, `stopProcess` and `DetachedProcess` in favor of new `BackgroundProcess` APIs. + [JPERF-716]: https://ecosystem.atlassian.net/browse/JPERF-716 ## [2.3.1] - 2020-04-06 diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt index 1b07cd0..0770969 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/SshjConnection.kt @@ -64,10 +64,12 @@ internal class SshjConnection internal constructor( } } + @Suppress("DEPRECATION", "OverridingDeprecatedMember") // used in public API, can only remove in a MAJOR release override fun startProcess(cmd: String): DetachedProcess { return ssh.startSession().use { DetachedProcess.start(cmd, it) } } + @Suppress("DEPRECATION", "OverridingDeprecatedMember") // used in public API, can only remove in a MAJOR release override fun stopProcess(process: DetachedProcess) { ssh.startSession().use { process.stop(it) } } diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/DetachedProcess.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/DetachedProcess.kt index e1d445d..6832959 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/DetachedProcess.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/DetachedProcess.kt @@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit * * @see [SshConnection.stopProcess] */ +@Deprecated(message = "Use BackgroundProcess instead") class DetachedProcess private constructor( private val cmd: String, private val uuid: UUID @@ -26,6 +27,7 @@ class DetachedProcess private constructor( logger.debug("Starting process $uuid $cmd") session.exec("screen -dm bash -c '${savePID(uuid)} && $cmd'") .use { command -> command.join(15, TimeUnit.SECONDS) } + @Suppress("DEPRECATION") // used transitively by public API return DetachedProcess(cmd, uuid) } @@ -37,4 +39,4 @@ class DetachedProcess private constructor( session.exec("kill -3 `cat $dir/$uuid`") .use { command -> command.join(15, TimeUnit.SECONDS) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/SshConnection.kt b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/SshConnection.kt index 4bd2774..ee80f79 100644 --- a/src/main/kotlin/com/atlassian/performance/tools/ssh/api/SshConnection.kt +++ b/src/main/kotlin/com/atlassian/performance/tools/ssh/api/SshConnection.kt @@ -120,6 +120,7 @@ interface SshConnection : Closeable { /** * Starts a [DetachedProcess]. You can use [stopProcess] to stop it later. */ + @Deprecated(message = "Use Ssh.runInBackground instead") fun startProcess( cmd: String ): DetachedProcess @@ -127,6 +128,7 @@ interface SshConnection : Closeable { /** * Stops a [DetachedProcess]. */ + @Deprecated(message = "Use BackgroundProcess.stop instead") fun stopProcess( process: DetachedProcess ) diff --git a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt index d6ae211..c035ed9 100644 --- a/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt +++ b/src/test/kotlin/com/atlassian/performance/tools/ssh/api/SshTest.kt @@ -13,9 +13,11 @@ class SshTest { installPing(sshHost) val ping = sshHost.newConnection().use { ssh -> + @Suppress("DEPRECATION") // tests public API ssh.startProcess("ping localhost") } sshHost.newConnection().use { ssh -> + @Suppress("DEPRECATION") // tests public API ssh.stopProcess(ping) } } From cdc5f53a4d731f5e9b93faebc8802c285bef5919 Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 15:40:07 +0100 Subject: [PATCH 6/7] Add GitHub Actions --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..33e9771 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + inputs: + release: + description: 'Release? yes/no' + default: 'no' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Cache Gradle + uses: actions/cache@v2 + with: + path: ~/.gradle + key: ${{ runner.os }}-${{ hashFiles('gradle') }} + - name: Build + run: ./gradlew build + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-reports + path: build/reports/tests + - name: Release + if: github.event.inputs.release == 'yes' + env: + atlassian_private_username: ${{ secrets.ARTIFACTORY_USERNAME }} + atlassian_private_password: ${{ secrets.ARTIFACTORY_API_KEY }} + run: | + ./gradlew release -Prelease.customUsername=${{ secrets.REPOSITORY_ACCESS_TOKEN }} + ./gradlew publish From fc9107f825610e76b10bfd1bd9ede60fb43ba125 Mon Sep 17 00:00:00 2001 From: Maciej Kwidzinski Date: Fri, 11 Dec 2020 15:40:57 +0100 Subject: [PATCH 7/7] Trigger GHA on non-master --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33e9771..8edb5e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,7 @@ name: CI on: push: - branches: [ master ] pull_request: - branches: [ master ] workflow_dispatch: inputs: release: