Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0a4e1f0
Reduce test flakiness on WSL
dagguh Jan 22, 2021
2d91665
JPERF-273: Very very very early WIP
dagguh Mar 29, 2019
1946c51
JPERF-273: Model the Jira node flow
dagguh Apr 1, 2019
41b1f87
JPERF-273: Test `InstallableJiraIT`
dagguh Apr 2, 2019
6d1ffef
JPERF-273: Refactor the Jira lifecycle
dagguh Apr 5, 2019
ce3b38a
JPERF-273: Stop jira manually, because we can
dagguh Apr 5, 2019
f032696
Rename "track" to "flow"
dagguh Apr 15, 2019
06bee9a
Resolve DB TODO
dagguh Apr 15, 2019
336c278
Add DC hook
dagguh Apr 15, 2019
eec3e10
Let hooks be hooked in at any later stage
dagguh Apr 15, 2019
2e3a2a9
JPERF-273: Offer DB-related Jira hooks
dagguh Jul 2, 2019
d129d08
JPERF-273: Test `JiraNodeFlow` concurrency
dagguh Jul 4, 2019
58ba182
JPERF-273: Fix `JiraNodeFlowTest.shouldHookDuringListing`
dagguh Jul 4, 2019
1984d8e
JPERF-273: Add `ParallelInstallation`
dagguh Jul 4, 2019
7192b70
JPERF-273: Distinguish running hooks from hooking them in
dagguh Jul 5, 2019
fac32ba
Bump a bunch of timeouts, because my Internet is awful
dagguh Sep 6, 2019
ebf7325
JPERF-273: Poll hooks manually to avoid tail iteration
dagguh Sep 6, 2019
d311c65
JPERF-219: Block hooking to the past
wyrzyk Oct 16, 2019
c87d198
Fixup: 6ad30c059bcdcc8b05c4ec2f9c40cc0b3cdfb779
dagguh Oct 15, 2019
f036201
Extract `S3HostedJdk`
dagguh Oct 14, 2019
13a0c5c
JPERF-273: Define default `JiraNodeFlow`
dagguh Oct 16, 2019
1156aae
JPERF-273: Remove fluent hook API
dagguh Oct 16, 2019
23a7138
JPERF-273: Rename "flows" to "hooks"
dagguh Oct 16, 2019
c18a5b0
Expose `Reports.listReports`
dagguh Oct 16, 2019
26bdabb
JPERF-273 Postgres 9.6.15
Oct 18, 2019
2742b75
Refactor hooks: composition over inheritance
dagguh Jan 22, 2021
a336862
Repackage hooks
dagguh Jan 29, 2021
8dc645b
Reuse StaticBackoff
dagguh Jan 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[*.{kt,kts}]
indent_size = 4
[*.{kt, kts}]
indent_size = 4
insert_final_newline = true
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ Dropping a requirement of a major version of a dependency is a new contract.
## [Unreleased]
[Unreleased]: https://github.com/atlassian/infrastructure/compare/release-4.17.5...master

### Fixed
- Increase network-level retries for Jira/browser downloads. Decrease flakiness of such downloads on Ubuntu on WSL2.

## [4.17.5] - 2020-12-15
[4.17.5]: https://github.com/atlassian/infrastructure/compare/release-4.17.4...release-4.17.5

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class HttpResource(
) {
Ubuntu().install(ssh, listOf("lftp"), Duration.ofMinutes(2))
ssh.execute(
"""lftp -c 'set net:timeout 15; set net:max-retries 10; pget -n 32 -c "$uri" -o $destination'""",
"""lftp -c 'set net:timeout 15; set net:max-retries 50; pget -n 32 -c "$uri" -o $destination'""",
timeout
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import java.time.temporal.ChronoUnit
internal class Iostat : OsMetric {
companion object {
private val DELAY: Duration = Duration.ofSeconds(2)
private val LOG_PATH: String = "~/jpt-iostat.log"
private val LOG_PATH: String = "./jpt-iostat.log"

private val TIME = "date -u \"+%d-%m-%Y %H:%M:%S UTC\""
private val ADD_TIME =
Expand All @@ -32,4 +32,4 @@ internal class Iostat : OsMetric {
val process = connection.startProcess("iostat -d $seconds -x | $ADD_TIME > $LOG_PATH")
return MonitoringProcess(process, LOG_PATH)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.atlassian.performance.tools.infrastructure.api.database

import com.atlassian.performance.tools.infrastructure.api.Sed
import com.atlassian.performance.tools.infrastructure.api.jira.install.hook.PostInstallHooks
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.install.hook.PostInstallHook
import com.atlassian.performance.tools.ssh.api.SshConnection

class DatabaseIpConfig(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test to cover this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but right now it's externalized.

We can include this coverage in here via SshUbuntu

private val databaseIp: String
) : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
Sed().replace(
connection = ssh,
expression = "(<url>.*(@(//)?|//))" + "([^:/]+)" + "(.*</url>)",
output = """\1$databaseIp\5""",
file = "${jira.home}/dbconfig.xml"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ class MySqlDatabase(
Thread.sleep(Duration.ofSeconds(10).toMillis())
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.atlassian.performance.tools.infrastructure.api.database

import com.atlassian.performance.tools.infrastructure.api.jira.install.hook.PostInstallHooks
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.install.hook.PostInstallHook
import com.atlassian.performance.tools.jvmtasks.api.Backoff
import com.atlassian.performance.tools.jvmtasks.api.IdempotentAction
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.time.Duration

class MysqlConnector : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
val connector = "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.40.tar.gz"
IdempotentAction(
description = "Download MySQL connector",
action = { ssh.execute("wget -q $connector") }
).retry(
maxAttempts = 3,
backoff = StaticBackoff(Duration.ofSeconds(5))
)
ssh.execute("tar -xzf mysql-connector-java-5.1.40.tar.gz")
ssh.execute("cp mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar ${jira.installation}/lib")
}
}

private class StaticBackoff(
private val backOff: Duration
) : Backoff {
override fun backOff(attempt: Int): Duration = backOff
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.atlassian.performance.tools.infrastructure.api.database

import com.atlassian.performance.tools.infrastructure.DockerImage
import com.atlassian.performance.tools.infrastructure.api.dataset.DatasetPackage
import com.atlassian.performance.tools.ssh.api.SshConnection
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.net.URI
import java.time.Duration

class PostgresDatabase(
private val source: DatasetPackage,
private val maxConnections: Int
) : Database {
private val logger: Logger = LogManager.getLogger(this::class.java)

private val image: DockerImage = DockerImage(
name = "postgres:9.6.15",
pullTimeout = Duration.ofMinutes(5)
)

constructor(
source: DatasetPackage
) : this(
source = source,
maxConnections = 200
)

override fun setup(ssh: SshConnection): String {
val data = source.download(ssh)
val containerName = image.run(
ssh = ssh,
// TODO Dataset for Postgres
// parameters = "-p 5432:5432 -v `realpath $data`:/",
parameters = "-p 5432:5432",
arguments = "-c 'listen_addresses='*'' -c 'max_connections=$maxConnections'"
)
Thread.sleep(Duration.ofSeconds(15).toMillis())
logger.debug("Postgres - creating jira user and database")
ssh.execute("sudo docker exec -u postgres $containerName psql --command \"CREATE USER jira WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN PASSWORD 'jira';\"")
ssh.execute("sudo docker exec -u postgres $containerName createdb -E UNICODE -l C -T template0 -O jira jira")
return data
}

override fun start(jira: URI, ssh: SshConnection) {
// TODO Check logs for the following entry
// LOG: database system is ready to accept connections
Thread.sleep(Duration.ofSeconds(15).toMillis())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.atlassian.performance.tools.infrastructure.api.jira

import com.atlassian.performance.tools.ssh.api.SshConnection

class EmptyJiraHome : JiraHomeSource {
override fun download(ssh: SshConnection): String {
val jiraHome = "jira-home"
ssh.execute("mkdir $jiraHome")
return jiraHome
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package com.atlassian.performance.tools.infrastructure.api.jira

import com.atlassian.performance.tools.infrastructure.api.jvm.DisabledJvmDebug
import com.atlassian.performance.tools.infrastructure.api.jvm.JavaDevelopmentKit
import com.atlassian.performance.tools.infrastructure.api.jvm.VersionedJavaDevelopmentKit
import com.atlassian.performance.tools.infrastructure.api.jvm.JvmDebug
import com.atlassian.performance.tools.infrastructure.api.jvm.OracleJDK
import com.atlassian.performance.tools.infrastructure.api.jvm.*
import com.atlassian.performance.tools.infrastructure.api.jvm.jmx.DisabledRemoteJmx
import com.atlassian.performance.tools.infrastructure.api.jvm.jmx.RemoteJmx
import com.atlassian.performance.tools.infrastructure.api.profiler.Profiler
Expand Down Expand Up @@ -157,4 +153,4 @@ class JiraNodeConfig private constructor(
JiraNodeConfig::class.java.getResource("/collectd/conf/jira-default.conf").toURI()
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install

import com.atlassian.performance.tools.infrastructure.api.jvm.JavaDevelopmentKit

class InstalledJira(
/**
* E.g. it contains `./dbconfig.xml`
*/
val home: String,
/**
* E.g. it contains `./conf/server.xml`
*/
val installation: String,
val jdk: JavaDevelopmentKit,
val server: TcpServer
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install

import com.atlassian.performance.tools.ssh.api.SshConnection
import net.jcip.annotations.ThreadSafe

@ThreadSafe
interface JiraInstallation {

fun install(
ssh: SshConnection,
server: TcpServer
): InstalledJira
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install

import com.atlassian.performance.tools.concurrency.api.submitWithLogContext
import com.atlassian.performance.tools.infrastructure.api.distribution.ProductDistribution
import com.atlassian.performance.tools.infrastructure.api.jira.JiraHomeSource
import com.atlassian.performance.tools.infrastructure.api.jvm.JavaDevelopmentKit
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.util.concurrent.Executors

class ParallelInstallation(
private val jiraHomeSource: JiraHomeSource,
private val productDistribution: ProductDistribution,
private val jdk: JavaDevelopmentKit
) : JiraInstallation {

override fun install(
ssh: SshConnection,
server: TcpServer
): InstalledJira {
val pool = Executors.newCachedThreadPool { runnable ->
Thread(runnable, "jira-installation-${runnable.hashCode()}")
}
val product = pool.submitWithLogContext("product") {
productDistribution.install(ssh, ".")
}
val home = pool.submitWithLogContext("home") {
jiraHomeSource.download(ssh)
}
val java = pool.submitWithLogContext("java") {
jdk.also { it.install(ssh) }
}
val jira = InstalledJira(home.get(), product.get(), java.get(), server)
pool.shutdownNow()
return jira
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install

import com.atlassian.performance.tools.infrastructure.api.distribution.ProductDistribution
import com.atlassian.performance.tools.infrastructure.api.jira.JiraHomeSource
import com.atlassian.performance.tools.infrastructure.api.jvm.JavaDevelopmentKit
import com.atlassian.performance.tools.ssh.api.SshConnection

class SequentialInstallation(
private val jiraHomeSource: JiraHomeSource,
private val productDistribution: ProductDistribution,
private val jdk: JavaDevelopmentKit
) : JiraInstallation {

override fun install(
ssh: SshConnection,
server: TcpServer
): InstalledJira {
val installation = productDistribution.install(ssh, ".")
val home = jiraHomeSource.download(ssh)
jdk.install(ssh)
return InstalledJira(home, installation, jdk, server)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install

class TcpServer(
val ip: String,
val publicPort: Int,
val privatePort: Int,
val name: String
) {
constructor(
ip: String,
port: Int,
name: String
) : this(
ip,
port,
port,
name
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.api.jira.install.TcpServer
import com.atlassian.performance.tools.infrastructure.api.jira.report.Report
import com.atlassian.performance.tools.infrastructure.api.jira.start.StartedJira
import com.atlassian.performance.tools.infrastructure.api.jira.start.hook.PostStartHook
import com.atlassian.performance.tools.infrastructure.api.jira.start.hook.PostStartHooks
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.net.URI

class AsyncProfilerHook : PreInstallHook {

override fun call(
ssh: SshConnection,
server: TcpServer,
hooks: PreInstallHooks
) {
val directory = "async-profiler"
val downloads = URI("https://github.com/jvm-profiling-tools/async-profiler/releases/download/")
val distribution = downloads.resolve("v1.4/async-profiler-1.4-linux-x64.tar.gz")
ssh.execute("wget -q $distribution")
ssh.execute("mkdir $directory")
ssh.execute("tar -xzf async-profiler-1.4-linux-x64.tar.gz -C $directory")
ssh.execute("sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'")
ssh.execute("sudo sh -c 'echo 0 > /proc/sys/kernel/kptr_restrict'")
val profilerPath = "./$directory/profiler.sh"
val profiler = InstalledAsyncProfiler(profilerPath)
hooks.postStart.insert(profiler)
}
}

private class InstalledAsyncProfiler(
private val profilerPath: String
) : PostStartHook {

override fun call(
ssh: SshConnection,
jira: StartedJira,
hooks: PostStartHooks
) {
ssh.execute("$profilerPath -b 20000000 start ${jira.pid}")
val profiler = StartedAsyncProfiler(jira.pid, profilerPath)
hooks.reports.add(profiler)
}
}

private class StartedAsyncProfiler(
private val pid: Int,
private val profilerPath: String
) : Report {

override fun locate(ssh: SshConnection): List<String> {
val flameGraphFile = "flamegraph.svg"
ssh.execute("$profilerPath stop $pid -o svg > $flameGraphFile")
return listOf(flameGraphFile)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.api.jira.SharedHome
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.ssh.api.SshConnection

class DataCenterHook(
private val nodeId: String,
private val sharedHome: SharedHome
) : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
val localSharedHome = sharedHome.localSharedHome
sharedHome.mount(ssh)
val jiraHome = jira.home // TODO what's the difference between localSharedHome and jiraHome? should both be hookable?
ssh.execute("echo ehcache.object.port = 40011 >> $jiraHome/cluster.properties")
ssh.execute("echo jira.node.id = $nodeId >> $jiraHome/cluster.properties")
ssh.execute("echo jira.shared.home = `realpath $localSharedHome` >> $jiraHome/cluster.properties")
}
}
Loading