Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook into Jira provisoning #102

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ Dropping a requirement of a major version of a dependency is a new contract.
### Added
- Point to remote files on SSH hosts via `RemotePath`.

Progress on [JPERF-273]:
Fix [JPERF-273]:
- Allow multiple ways of installing Jira via `JiraInstallation` or starting it via `JiraStart`.
- Represent the information required to use an already installed Jira via `InstalledJira` or `JiraStart` if started.
- Represent a brand-new Jira instance via `EmptyJiraHome`.
- Hook into Jira installation via `PreInstallHooks` and `PostInstallHooks`.
- Hook into Jira start via `PreStartHooks` and `PostStartHooks`.
- Let hooks insert new hooks.
- Locate and download any logs, charts, profiles and other reports via `Report` (rather than hardcoding the paths).

### Fixed
- Increase network-level retries for Jira/browser downloads. Decrease flakiness of such downloads on Ubuntu on WSL2.
Expand Down
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.path // TODO what's the difference between localSharedHome and jiraHome? should both be hookable?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • remove the DataCenterHook until we get a DC test

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")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

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

class DisabledAutoBackup : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
ssh.execute("echo jira.autoexport=false > ${jira.home.path}/jira-config.properties")
}
}
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.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.install.JiraInstallation
import com.atlassian.performance.tools.infrastructure.api.jira.install.TcpServer

class HookedJiraInstallation(
private val installation: JiraInstallation,
private val hooks: PreInstallHooks
) : JiraInstallation {

override fun install(
server: TcpServer
): InstalledJira {
server.ssh.newConnection().use { ssh ->
hooks.call(ssh, server)
}
val installed = installation.install(server)
server.ssh.newConnection().use { ssh ->
hooks.postInstall.call(ssh, installed)
}
return installed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

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

class JiraHomeProperty : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
val properties = "${jira.installation.path}/atlassian-jira/WEB-INF/classes/jira-application.properties"
ssh.execute("echo jira.home=`realpath ${jira.home.path}` > $properties")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.report.StaticReport
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.nio.file.Path
import java.nio.file.Paths

class JiraLogs : PostInstallHook {

override fun call(ssh: SshConnection, jira: InstalledJira, hooks: PostInstallHooks) {
listOf(
"${jira.home.path}/log/atlassian-jira.log",
"${jira.installation.path}/logs/catalina.out"
)
.onEach { ensureFile(Paths.get(it), ssh) }
.map { StaticReport(it) }
.forEach { hooks.reports.add(it) }
}

private fun ensureFile(
path: Path,
ssh: SshConnection
) {
ssh.execute("mkdir -p ${path.parent!!}")
ssh.execute("touch $path")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.api.jira.JiraGcLog
import com.atlassian.performance.tools.infrastructure.api.jira.JiraNodeConfig
import com.atlassian.performance.tools.infrastructure.api.jira.SetenvSh
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.report.FileListing
import com.atlassian.performance.tools.ssh.api.SshConnection

class JvmConfig(
private val config: JiraNodeConfig
) : PostInstallHook {

override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
val gcLog = JiraGcLog(jira.installation.path)
SetenvSh(jira.installation.path).setup(
connection = ssh,
config = config,
gcLog = gcLog,
jiraIp = jira.server.ip
)
val report = FileListing(gcLog.path("*"))
hooks.reports.add(report)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.Iostat
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
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.infrastructure.api.os.OsMetric
import com.atlassian.performance.tools.infrastructure.api.os.Ubuntu
import com.atlassian.performance.tools.infrastructure.api.os.Vmstat
import com.atlassian.performance.tools.infrastructure.jira.report.RemoteMonitoringProcessReport
import com.atlassian.performance.tools.ssh.api.SshConnection

class LateUbuntuSysstat : PostInstallHook {
override fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
) {
val ubuntu = Ubuntu()
ubuntu.install(ssh, listOf("sysstat"))
listOf(Vmstat(), Iostat())
.map { PostStartOsMetric(it) }
.forEach { hooks.postStart.insert(it) }
}
}

private class PostStartOsMetric(
private val metric: OsMetric
) : PostStartHook {
override fun call(ssh: SshConnection, jira: StartedJira, hooks: PostStartHooks) {
val process = metric.start(ssh)
hooks.reports.add(RemoteMonitoringProcessReport(process))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

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

/**
* Intercepts a call after Jira is installed.
*/
interface PostInstallHook {

/**
* @param [ssh] connects to the [jira]
* @param [jira] points to the installed Jira
* @param [hooks] inserts future hooks and reports
*/
fun call(
ssh: SshConnection,
jira: InstalledJira,
hooks: PostInstallHooks
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.atlassian.performance.tools.infrastructure.api.jira.install.hook

import com.atlassian.performance.tools.infrastructure.api.jira.JiraNodeConfig
import com.atlassian.performance.tools.infrastructure.api.jira.install.InstalledJira
import com.atlassian.performance.tools.infrastructure.api.jira.start.hook.PreStartHooks
import com.atlassian.performance.tools.infrastructure.jira.install.hook.ProfilerHook
import com.atlassian.performance.tools.infrastructure.jira.install.hook.SplunkForwarderHook
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue

class PostInstallHooks private constructor(
val preStart: PreStartHooks
) {

private val hooks: Queue<PostInstallHook> = ConcurrentLinkedQueue()
val postStart = preStart.postStart
val reports = postStart.reports

fun insert(
hook: PostInstallHook
) {
hooks.add(hook)
}

internal fun call(
ssh: SshConnection,
jira: InstalledJira
) {
while (true) {
hooks
.poll()
?.call(ssh, jira, this)
?: break
}
}

companion object Factory {
fun default(): PostInstallHooks = PostInstallHooks(PreStartHooks.default()).apply {
val config = JiraNodeConfig.Builder().build()
listOf(
JiraHomeProperty(),
DisabledAutoBackup(),
JvmConfig(config),
ProfilerHook(config.profiler),
SplunkForwarderHook(config.splunkForwarder),
JiraLogs(),
LateUbuntuSysstat()
).forEach { insert(it) }
}

fun empty(): PostInstallHooks = PostInstallHooks(PreStartHooks.empty())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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.ssh.api.SshConnection

/**
* Intercepts a call before Jira is installed.
*/
interface PreInstallHook {

/**
* @param [ssh] connects to the [server]
* @param [server] will install Jira
* @param [hooks] inserts future hooks and reports
*/
fun call(
ssh: SshConnection,
server: TcpServer,
hooks: PreInstallHooks
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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.ssh.api.SshConnection
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue

class PreInstallHooks private constructor(
val postInstall: PostInstallHooks
) {

private val hooks: Queue<PreInstallHook> = ConcurrentLinkedQueue()
val preStart = postInstall.preStart
val postStart = preStart.postStart
val reports = postStart.reports

fun insert(
hook: PreInstallHook
) {
hooks.add(hook)
}

internal fun call(
ssh: SshConnection,
server: TcpServer
) {
while (true) {
hooks
.poll()
?.call(ssh, server, this)
?: break
}
}

companion object Factory {
fun default(): PreInstallHooks = PreInstallHooks(PostInstallHooks.default())

fun empty(): PreInstallHooks = PreInstallHooks(PostInstallHooks.empty())
}
}
Loading